]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSString.cpp
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / runtime / JSString.cpp
CommitLineData
9dae56ea
A
1/*
2 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24#include "JSString.h"
25
26#include "JSGlobalObject.h"
14957cd0 27#include "JSGlobalObjectFunctions.h"
9dae56ea 28#include "JSObject.h"
81345200 29#include "JSCInlines.h"
9dae56ea
A
30#include "StringObject.h"
31#include "StringPrototype.h"
81345200 32#include "StrongInlines.h"
9dae56ea
A
33
34namespace JSC {
b80e6193 35
6fe7ccc8 36const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) };
14957cd0 37
6fe7ccc8
A
38void JSRopeString::RopeBuilder::expand()
39{
40 ASSERT(m_index == JSRopeString::s_maxInternalRopeLength);
41 JSString* jsString = m_jsString;
4be4e309 42 RELEASE_ASSERT(jsString);
93a37866 43 m_jsString = jsStringBuilder(&m_vm);
6fe7ccc8
A
44 m_index = 0;
45 append(jsString);
46}
47
48void JSString::destroy(JSCell* cell)
49{
93a37866 50 JSString* thisObject = static_cast<JSString*>(cell);
6fe7ccc8
A
51 thisObject->JSString::~JSString();
52}
53
81345200 54void JSString::dumpToStream(const JSCell* cell, PrintStream& out)
6fe7ccc8 55{
81345200
A
56 const JSString* thisObject = jsCast<const JSString*>(cell);
57 out.printf("<%p, %s, [%u], ", thisObject, thisObject->className(), thisObject->length());
58 if (thisObject->isRope())
59 out.printf("[rope]");
60 else {
93a37866
A
61 WTF::StringImpl* ourImpl = thisObject->m_value.impl();
62 if (ourImpl->is8Bit())
81345200 63 out.printf("[8 %p]", ourImpl->characters8());
93a37866 64 else
81345200
A
65 out.printf("[16 %p]", ourImpl->characters16());
66 }
67 out.printf(">");
68}
93a37866 69
81345200
A
70void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
71{
72 JSString* thisObject = jsCast<JSString*>(cell);
73 Base::visitChildren(thisObject, visitor);
74
6fe7ccc8
A
75 if (thisObject->isRope())
76 static_cast<JSRopeString*>(thisObject)->visitFibers(visitor);
81345200
A
77 else {
78 StringImpl* impl = thisObject->m_value.impl();
79 ASSERT(impl);
80 visitor.reportExtraMemoryUsage(thisObject, impl->costDuringGC());
81 }
6fe7ccc8
A
82}
83
84void JSRopeString::visitFibers(SlotVisitor& visitor)
85{
86 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
87 visitor.append(&m_fibers[i]);
88}
89
81345200
A
90static const unsigned maxLengthForOnStackResolve = 2048;
91
92void JSRopeString::resolveRopeInternal8(LChar* buffer) const
93{
94 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
95 if (m_fibers[i]->isRope()) {
96 resolveRopeSlowCase8(buffer);
97 return;
98 }
99 }
100
101 LChar* position = buffer;
102 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
103 const StringImpl& fiberString = *m_fibers[i]->m_value.impl();
104 unsigned length = fiberString.length();
105 StringImpl::copyChars(position, fiberString.characters8(), length);
106 position += length;
107 }
108 ASSERT((buffer + m_length) == position);
109}
110
111void JSRopeString::resolveRopeInternal16(UChar* buffer) const
112{
113 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
114 if (m_fibers[i]->isRope()) {
115 resolveRopeSlowCase(buffer);
116 return;
117 }
118 }
119
120 UChar* position = buffer;
121 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
122 const StringImpl& fiberString = *m_fibers[i]->m_value.impl();
123 unsigned length = fiberString.length();
124 if (fiberString.is8Bit())
125 StringImpl::copyChars(position, fiberString.characters8(), length);
126 else
127 StringImpl::copyChars(position, fiberString.characters16(), length);
128 position += length;
129 }
130 ASSERT((buffer + m_length) == position);
131}
132
133void JSRopeString::resolveRopeToAtomicString(ExecState* exec) const
134{
135 if (m_length > maxLengthForOnStackResolve) {
136 resolveRope(exec);
137 m_value = AtomicString(m_value);
138 return;
139 }
140
141 if (is8Bit()) {
142 LChar buffer[maxLengthForOnStackResolve];
143 resolveRopeInternal8(buffer);
144 m_value = AtomicString(buffer, m_length);
145 } else {
146 UChar buffer[maxLengthForOnStackResolve];
147 resolveRopeInternal16(buffer);
148 m_value = AtomicString(buffer, m_length);
149 }
150
151 clearFibers();
152
153 // If we resolved a string that didn't previously exist, notify the heap that we've grown.
154 if (m_value.impl()->hasOneRef())
155 Heap::heap(this)->reportExtraMemoryCost(m_value.impl()->cost());
156}
157
158void JSRopeString::clearFibers() const
159{
160 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
161 m_fibers[i].clear();
162}
163
164AtomicStringImpl* JSRopeString::resolveRopeToExistingAtomicString(ExecState* exec) const
165{
166 if (m_length > maxLengthForOnStackResolve) {
167 resolveRope(exec);
168 if (AtomicStringImpl* existingAtomicString = AtomicString::find(m_value.impl())) {
169 m_value = *existingAtomicString;
170 clearFibers();
171 return existingAtomicString;
172 }
173 return nullptr;
174 }
175
176 if (is8Bit()) {
177 LChar buffer[maxLengthForOnStackResolve];
178 resolveRopeInternal8(buffer);
179 if (AtomicStringImpl* existingAtomicString = AtomicString::find(buffer, m_length)) {
180 m_value = *existingAtomicString;
181 clearFibers();
182 return existingAtomicString;
183 }
184 } else {
185 UChar buffer[maxLengthForOnStackResolve];
186 resolveRopeInternal16(buffer);
187 if (AtomicStringImpl* existingAtomicString = AtomicString::find(buffer, m_length)) {
188 m_value = *existingAtomicString;
189 clearFibers();
190 return existingAtomicString;
191 }
192 }
193
194 return nullptr;
195}
196
6fe7ccc8 197void JSRopeString::resolveRope(ExecState* exec) const
14957cd0
A
198{
199 ASSERT(isRope());
200
6fe7ccc8
A
201 if (is8Bit()) {
202 LChar* buffer;
203 if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
204 Heap::heap(this)->reportExtraMemoryCost(newImpl->cost());
205 m_value = newImpl.release();
206 } else {
207 outOfMemory(exec);
208 return;
209 }
81345200
A
210 resolveRopeInternal8(buffer);
211 clearFibers();
6fe7ccc8 212 ASSERT(!isRope());
6fe7ccc8
A
213 return;
214 }
215
14957cd0 216 UChar* buffer;
6fe7ccc8
A
217 if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
218 Heap::heap(this)->reportExtraMemoryCost(newImpl->cost());
219 m_value = newImpl.release();
220 } else {
14957cd0
A
221 outOfMemory(exec);
222 return;
223 }
224
81345200
A
225 resolveRopeInternal16(buffer);
226 clearFibers();
14957cd0
A
227 ASSERT(!isRope());
228}
9dae56ea 229
6fe7ccc8 230// Overview: These functions convert a JSString from holding a string in rope form
93a37866 231// down to a simple String representation. It does so by building up the string
f9bf01c6
A
232// backwards, since we want to avoid recursion, we expect that the tree structure
233// representing the rope is likely imbalanced with more nodes down the left side
234// (since appending to the string is likely more common) - and as such resolving
235// in this fashion should minimize work queue size. (If we built the queue forwards
14957cd0 236// we would likely have to place all of the constituent StringImpls into the
f9bf01c6
A
237// Vector before performing any concatenation, but by working backwards we likely
238// only fill the queue with the number of substrings at any given level in a
14957cd0 239// rope-of-ropes.)
6fe7ccc8 240void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const
f9bf01c6 241{
6fe7ccc8 242 LChar* position = buffer + m_length; // We will be working backwards over the rope.
93a37866 243 Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method.
6fe7ccc8 244
81345200 245 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
6fe7ccc8 246 workQueue.append(m_fibers[i].get());
14957cd0 247
6fe7ccc8
A
248 while (!workQueue.isEmpty()) {
249 JSString* currentFiber = workQueue.last();
250 workQueue.removeLast();
b80e6193 251
6fe7ccc8
A
252 if (currentFiber->isRope()) {
253 JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
254 for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i)
255 workQueue.append(currentFiberAsRope->m_fibers[i].get());
b80e6193 256 continue;
b80e6193 257 }
b80e6193 258
6fe7ccc8
A
259 StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
260 unsigned length = string->length();
261 position -= length;
262 StringImpl::copyChars(position, string->characters8(), length);
b80e6193 263 }
6fe7ccc8
A
264
265 ASSERT(buffer == position);
b80e6193 266}
f9bf01c6 267
6fe7ccc8 268void JSRopeString::resolveRopeSlowCase(UChar* buffer) const
4e4e5a6f 269{
6fe7ccc8 270 UChar* position = buffer + m_length; // We will be working backwards over the rope.
93a37866 271 Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK.
4e4e5a6f 272
6fe7ccc8
A
273 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
274 workQueue.append(m_fibers[i].get());
4e4e5a6f 275
6fe7ccc8
A
276 while (!workQueue.isEmpty()) {
277 JSString* currentFiber = workQueue.last();
278 workQueue.removeLast();
4e4e5a6f 279
6fe7ccc8
A
280 if (currentFiber->isRope()) {
281 JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
282 for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i)
283 workQueue.append(currentFiberAsRope->m_fibers[i].get());
4e4e5a6f
A
284 continue;
285 }
286
6fe7ccc8
A
287 StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
288 unsigned length = string->length();
289 position -= length;
93a37866
A
290 if (string->is8Bit())
291 StringImpl::copyChars(position, string->characters8(), length);
292 else
293 StringImpl::copyChars(position, string->characters16(), length);
4e4e5a6f
A
294 }
295
6fe7ccc8 296 ASSERT(buffer == position);
6fe7ccc8
A
297}
298
299void JSRopeString::outOfMemory(ExecState* exec) const
300{
81345200 301 clearFibers();
6fe7ccc8 302 ASSERT(isRope());
93a37866 303 ASSERT(m_value.isNull());
6fe7ccc8
A
304 if (exec)
305 throwOutOfMemoryError(exec);
4e4e5a6f
A
306}
307
6fe7ccc8 308JSString* JSRopeString::getIndexSlowCase(ExecState* exec, unsigned i)
4e4e5a6f
A
309{
310 ASSERT(isRope());
311 resolveRope(exec);
312 // Return a safe no-value result, this should never be used, since the excetion will be thrown.
313 if (exec->exception())
93a37866 314 return jsEmptyString(exec);
4e4e5a6f 315 ASSERT(!isRope());
93a37866 316 RELEASE_ASSERT(i < m_value.length());
4e4e5a6f
A
317 return jsSingleCharacterSubstring(exec, m_value, i);
318}
319
ba379fdc 320JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
9dae56ea
A
321{
322 return const_cast<JSString*>(this);
323}
324
6fe7ccc8 325bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
9dae56ea 326{
f9bf01c6 327 result = this;
14957cd0 328 number = jsToNumber(value(exec));
9dae56ea
A
329 return false;
330}
331
93a37866 332bool JSString::toBoolean() const
9dae56ea 333{
4e4e5a6f 334 return m_length;
9dae56ea
A
335}
336
f9bf01c6 337double JSString::toNumber(ExecState* exec) const
9dae56ea 338{
14957cd0 339 return jsToNumber(value(exec));
9dae56ea
A
340}
341
81345200 342inline StringObject* StringObject::create(VM& vm, JSGlobalObject* globalObject, JSString* string)
9dae56ea 343{
81345200
A
344 StringObject* object = new (NotNull, allocateCell<StringObject>(vm.heap)) StringObject(vm, globalObject->stringObjectStructure());
345 object->finishCreation(vm, string);
6fe7ccc8 346 return object;
9dae56ea
A
347}
348
14957cd0 349JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const
9dae56ea 350{
81345200 351 return StringObject::create(exec->vm(), globalObject, const_cast<JSString*>(this));
9dae56ea
A
352}
353
81345200 354JSValue JSString::toThis(JSCell* cell, ExecState* exec, ECMAMode ecmaMode)
9dae56ea 355{
81345200
A
356 if (ecmaMode == StrictMode)
357 return cell;
358 return StringObject::create(exec->vm(), exec->lexicalGlobalObject(), jsCast<JSString*>(cell));
9dae56ea
A
359}
360
93a37866 361bool JSString::getStringPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
9dae56ea 362{
f9bf01c6 363 if (propertyName == exec->propertyNames().length) {
14957cd0 364 descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly);
9dae56ea 365 return true;
9dae56ea 366 }
9dae56ea 367
93a37866
A
368 unsigned i = propertyName.asIndex();
369 if (i < m_length) {
370 ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail!
4e4e5a6f 371 descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly);
f9bf01c6 372 return true;
9dae56ea 373 }
f9bf01c6
A
374
375 return false;
9dae56ea
A
376}
377
81345200 378JSString* jsStringWithCacheSlowCase(VM& vm, StringImpl& stringImpl)
f9bf01c6 379{
81345200
A
380 auto addResult = vm.stringCache.add(&stringImpl, nullptr);
381 if (addResult.isNewEntry)
382 addResult.iterator->value = jsString(&vm, String(stringImpl));
383 vm.lastCachedString.set(vm, addResult.iterator->value.get());
384 return addResult.iterator->value.get();
9dae56ea
A
385}
386
387} // namespace JSC