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.
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.
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.
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.
26 #include "JSGlobalObject.h"
27 #include "JSGlobalObjectFunctions.h"
29 #include "JSCInlines.h"
30 #include "StringObject.h"
31 #include "StringPrototype.h"
32 #include "StrongInlines.h"
36 const ClassInfo
JSString::s_info
= { "string", 0, 0, CREATE_METHOD_TABLE(JSString
) };
38 void JSRopeString::RopeBuilder::expand()
40 ASSERT(m_index
== JSRopeString::s_maxInternalRopeLength
);
41 JSString
* jsString
= m_jsString
;
42 RELEASE_ASSERT(jsString
);
43 m_jsString
= jsStringBuilder(&m_vm
);
48 void JSString::destroy(JSCell
* cell
)
50 JSString
* thisObject
= static_cast<JSString
*>(cell
);
51 thisObject
->JSString::~JSString();
54 void JSString::dumpToStream(const JSCell
* cell
, PrintStream
& out
)
56 const JSString
* thisObject
= jsCast
<const JSString
*>(cell
);
57 out
.printf("<%p, %s, [%u], ", thisObject
, thisObject
->className(), thisObject
->length());
58 if (thisObject
->isRope())
61 WTF::StringImpl
* ourImpl
= thisObject
->m_value
.impl();
62 if (ourImpl
->is8Bit())
63 out
.printf("[8 %p]", ourImpl
->characters8());
65 out
.printf("[16 %p]", ourImpl
->characters16());
70 void JSString::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
72 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
73 Base::visitChildren(thisObject
, visitor
);
75 if (thisObject
->isRope())
76 static_cast<JSRopeString
*>(thisObject
)->visitFibers(visitor
);
78 StringImpl
* impl
= thisObject
->m_value
.impl();
80 visitor
.reportExtraMemoryVisited(thisObject
, impl
->costDuringGC());
84 void JSRopeString::visitFibers(SlotVisitor
& visitor
)
87 visitor
.append(&substringBase());
90 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& fiber(i
); ++i
)
91 visitor
.append(&fiber(i
));
94 static const unsigned maxLengthForOnStackResolve
= 2048;
96 void JSRopeString::resolveRopeInternal8(LChar
* buffer
) const
99 StringImpl::copyChars(
100 buffer
, substringBase()->m_value
.characters8() + substringOffset(), m_length
);
104 resolveRopeInternal8NoSubstring(buffer
);
107 void JSRopeString::resolveRopeInternal8NoSubstring(LChar
* buffer
) const
109 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& fiber(i
); ++i
) {
110 if (fiber(i
)->isRope()) {
111 resolveRopeSlowCase8(buffer
);
116 LChar
* position
= buffer
;
117 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& fiber(i
); ++i
) {
118 const StringImpl
& fiberString
= *fiber(i
)->m_value
.impl();
119 unsigned length
= fiberString
.length();
120 StringImpl::copyChars(position
, fiberString
.characters8(), length
);
123 ASSERT((buffer
+ m_length
) == position
);
126 void JSRopeString::resolveRopeInternal16(UChar
* buffer
) const
129 StringImpl::copyChars(
130 buffer
, substringBase()->m_value
.characters16() + substringOffset(), m_length
);
134 resolveRopeInternal16NoSubstring(buffer
);
137 void JSRopeString::resolveRopeInternal16NoSubstring(UChar
* buffer
) const
139 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& fiber(i
); ++i
) {
140 if (fiber(i
)->isRope()) {
141 resolveRopeSlowCase(buffer
);
146 UChar
* position
= buffer
;
147 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& fiber(i
); ++i
) {
148 const StringImpl
& fiberString
= *fiber(i
)->m_value
.impl();
149 unsigned length
= fiberString
.length();
150 if (fiberString
.is8Bit())
151 StringImpl::copyChars(position
, fiberString
.characters8(), length
);
153 StringImpl::copyChars(position
, fiberString
.characters16(), length
);
156 ASSERT((buffer
+ m_length
) == position
);
159 void JSRopeString::resolveRopeToAtomicString(ExecState
* exec
) const
161 if (m_length
> maxLengthForOnStackResolve
) {
163 m_value
= AtomicString(m_value
);
164 setIs8Bit(m_value
.impl()->is8Bit());
169 LChar buffer
[maxLengthForOnStackResolve
];
170 resolveRopeInternal8(buffer
);
171 m_value
= AtomicString(buffer
, m_length
);
172 setIs8Bit(m_value
.impl()->is8Bit());
174 UChar buffer
[maxLengthForOnStackResolve
];
175 resolveRopeInternal16(buffer
);
176 m_value
= AtomicString(buffer
, m_length
);
177 setIs8Bit(m_value
.impl()->is8Bit());
182 // If we resolved a string that didn't previously exist, notify the heap that we've grown.
183 if (m_value
.impl()->hasOneRef())
184 Heap::heap(this)->reportExtraMemoryAllocated(m_value
.impl()->cost());
187 void JSRopeString::clearFibers() const
189 for (size_t i
= 0; i
< s_maxInternalRopeLength
; ++i
)
193 RefPtr
<AtomicStringImpl
> JSRopeString::resolveRopeToExistingAtomicString(ExecState
* exec
) const
195 if (m_length
> maxLengthForOnStackResolve
) {
197 if (RefPtr
<AtomicStringImpl
> existingAtomicString
= AtomicStringImpl::lookUp(m_value
.impl())) {
198 m_value
= *existingAtomicString
;
199 setIs8Bit(m_value
.impl()->is8Bit());
201 return existingAtomicString
;
207 LChar buffer
[maxLengthForOnStackResolve
];
208 resolveRopeInternal8(buffer
);
209 if (RefPtr
<AtomicStringImpl
> existingAtomicString
= AtomicStringImpl::lookUp(buffer
, m_length
)) {
210 m_value
= *existingAtomicString
;
211 setIs8Bit(m_value
.impl()->is8Bit());
213 return existingAtomicString
;
216 UChar buffer
[maxLengthForOnStackResolve
];
217 resolveRopeInternal16(buffer
);
218 if (RefPtr
<AtomicStringImpl
> existingAtomicString
= AtomicStringImpl::lookUp(buffer
, m_length
)) {
219 m_value
= *existingAtomicString
;
220 setIs8Bit(m_value
.impl()->is8Bit());
222 return existingAtomicString
;
229 void JSRopeString::resolveRope(ExecState
* exec
) const
234 ASSERT(!substringBase()->isRope());
235 m_value
= substringBase()->m_value
.substring(substringOffset(), m_length
);
236 substringBase().clear();
242 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
243 Heap::heap(this)->reportExtraMemoryAllocated(newImpl
->cost());
244 m_value
= newImpl
.release();
249 resolveRopeInternal8NoSubstring(buffer
);
256 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
257 Heap::heap(this)->reportExtraMemoryAllocated(newImpl
->cost());
258 m_value
= newImpl
.release();
264 resolveRopeInternal16NoSubstring(buffer
);
269 // Overview: These functions convert a JSString from holding a string in rope form
270 // down to a simple String representation. It does so by building up the string
271 // backwards, since we want to avoid recursion, we expect that the tree structure
272 // representing the rope is likely imbalanced with more nodes down the left side
273 // (since appending to the string is likely more common) - and as such resolving
274 // in this fashion should minimize work queue size. (If we built the queue forwards
275 // we would likely have to place all of the constituent StringImpls into the
276 // Vector before performing any concatenation, but by working backwards we likely
277 // only fill the queue with the number of substrings at any given level in a
279 void JSRopeString::resolveRopeSlowCase8(LChar
* buffer
) const
281 LChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
282 Vector
<JSString
*, 32, UnsafeVectorOverflow
> workQueue
; // Putting strings into a Vector is only OK because there are no GC points in this method.
284 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& fiber(i
); ++i
)
285 workQueue
.append(fiber(i
).get());
287 while (!workQueue
.isEmpty()) {
288 JSString
* currentFiber
= workQueue
.last();
289 workQueue
.removeLast();
291 const LChar
* characters
;
293 if (currentFiber
->isRope()) {
294 JSRopeString
* currentFiberAsRope
= static_cast<JSRopeString
*>(currentFiber
);
295 if (!currentFiberAsRope
->isSubstring()) {
296 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& currentFiberAsRope
->fiber(i
); ++i
)
297 workQueue
.append(currentFiberAsRope
->fiber(i
).get());
300 ASSERT(!currentFiberAsRope
->substringBase()->isRope());
302 currentFiberAsRope
->substringBase()->m_value
.characters8() +
303 currentFiberAsRope
->substringOffset();
305 characters
= currentFiber
->m_value
.characters8();
307 unsigned length
= currentFiber
->length();
309 StringImpl::copyChars(position
, characters
, length
);
312 ASSERT(buffer
== position
);
315 void JSRopeString::resolveRopeSlowCase(UChar
* buffer
) const
317 UChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
318 Vector
<JSString
*, 32, UnsafeVectorOverflow
> workQueue
; // These strings are kept alive by the parent rope, so using a Vector is OK.
320 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& fiber(i
); ++i
)
321 workQueue
.append(fiber(i
).get());
323 while (!workQueue
.isEmpty()) {
324 JSString
* currentFiber
= workQueue
.last();
325 workQueue
.removeLast();
327 if (currentFiber
->isRope()) {
328 JSRopeString
* currentFiberAsRope
= static_cast<JSRopeString
*>(currentFiber
);
329 if (currentFiberAsRope
->isSubstring()) {
330 ASSERT(!currentFiberAsRope
->substringBase()->isRope());
331 StringImpl
* string
= static_cast<StringImpl
*>(
332 currentFiberAsRope
->substringBase()->m_value
.impl());
333 unsigned offset
= currentFiberAsRope
->substringOffset();
334 unsigned length
= currentFiberAsRope
->length();
336 if (string
->is8Bit())
337 StringImpl::copyChars(position
, string
->characters8() + offset
, length
);
339 StringImpl::copyChars(position
, string
->characters16() + offset
, length
);
342 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& currentFiberAsRope
->fiber(i
); ++i
)
343 workQueue
.append(currentFiberAsRope
->fiber(i
).get());
347 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
->m_value
.impl());
348 unsigned length
= string
->length();
350 if (string
->is8Bit())
351 StringImpl::copyChars(position
, string
->characters8(), length
);
353 StringImpl::copyChars(position
, string
->characters16(), length
);
356 ASSERT(buffer
== position
);
359 void JSRopeString::outOfMemory(ExecState
* exec
) const
363 ASSERT(m_value
.isNull());
365 throwOutOfMemoryError(exec
);
368 JSValue
JSString::toPrimitive(ExecState
*, PreferredPrimitiveType
) const
370 return const_cast<JSString
*>(this);
373 bool JSString::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
376 number
= jsToNumber(view(exec
));
380 double JSString::toNumber(ExecState
* exec
) const
382 return jsToNumber(view(exec
));
385 inline StringObject
* StringObject::create(VM
& vm
, JSGlobalObject
* globalObject
, JSString
* string
)
387 StringObject
* object
= new (NotNull
, allocateCell
<StringObject
>(vm
.heap
)) StringObject(vm
, globalObject
->stringObjectStructure());
388 object
->finishCreation(vm
, string
);
392 JSObject
* JSString::toObject(ExecState
* exec
, JSGlobalObject
* globalObject
) const
394 return StringObject::create(exec
->vm(), globalObject
, const_cast<JSString
*>(this));
397 JSValue
JSString::toThis(JSCell
* cell
, ExecState
* exec
, ECMAMode ecmaMode
)
399 if (ecmaMode
== StrictMode
)
401 return StringObject::create(exec
->vm(), exec
->lexicalGlobalObject(), jsCast
<JSString
*>(cell
));
404 bool JSString::getStringPropertyDescriptor(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
406 if (propertyName
== exec
->propertyNames().length
) {
407 descriptor
.setDescriptor(jsNumber(m_length
), DontEnum
| DontDelete
| ReadOnly
);
411 Optional
<uint32_t> index
= parseIndex(propertyName
);
412 if (index
&& index
.value() < m_length
) {
413 descriptor
.setDescriptor(getIndex(exec
, index
.value()), DontDelete
| ReadOnly
);
420 JSString
* jsStringWithCacheSlowCase(VM
& vm
, StringImpl
& stringImpl
)
422 if (JSString
* string
= vm
.stringCache
.get(&stringImpl
))
425 JSString
* string
= jsString(&vm
, String(stringImpl
));
426 vm
.lastCachedString
.set(vm
, string
);