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, 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
.reportExtraMemoryUsage(thisObject
, impl
->costDuringGC());
84 void JSRopeString::visitFibers(SlotVisitor
& visitor
)
86 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
87 visitor
.append(&m_fibers
[i
]);
90 static const unsigned maxLengthForOnStackResolve
= 2048;
92 void JSRopeString::resolveRopeInternal8(LChar
* buffer
) const
94 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
95 if (m_fibers
[i
]->isRope()) {
96 resolveRopeSlowCase8(buffer
);
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
);
108 ASSERT((buffer
+ m_length
) == position
);
111 void JSRopeString::resolveRopeInternal16(UChar
* buffer
) const
113 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
114 if (m_fibers
[i
]->isRope()) {
115 resolveRopeSlowCase(buffer
);
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
);
127 StringImpl::copyChars(position
, fiberString
.characters16(), length
);
130 ASSERT((buffer
+ m_length
) == position
);
133 void JSRopeString::resolveRopeToAtomicString(ExecState
* exec
) const
135 if (m_length
> maxLengthForOnStackResolve
) {
137 m_value
= AtomicString(m_value
);
142 LChar buffer
[maxLengthForOnStackResolve
];
143 resolveRopeInternal8(buffer
);
144 m_value
= AtomicString(buffer
, m_length
);
146 UChar buffer
[maxLengthForOnStackResolve
];
147 resolveRopeInternal16(buffer
);
148 m_value
= AtomicString(buffer
, m_length
);
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());
158 void JSRopeString::clearFibers() const
160 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
164 AtomicStringImpl
* JSRopeString::resolveRopeToExistingAtomicString(ExecState
* exec
) const
166 if (m_length
> maxLengthForOnStackResolve
) {
168 if (AtomicStringImpl
* existingAtomicString
= AtomicString::find(m_value
.impl())) {
169 m_value
= *existingAtomicString
;
171 return existingAtomicString
;
177 LChar buffer
[maxLengthForOnStackResolve
];
178 resolveRopeInternal8(buffer
);
179 if (AtomicStringImpl
* existingAtomicString
= AtomicString::find(buffer
, m_length
)) {
180 m_value
= *existingAtomicString
;
182 return existingAtomicString
;
185 UChar buffer
[maxLengthForOnStackResolve
];
186 resolveRopeInternal16(buffer
);
187 if (AtomicStringImpl
* existingAtomicString
= AtomicString::find(buffer
, m_length
)) {
188 m_value
= *existingAtomicString
;
190 return existingAtomicString
;
197 void JSRopeString::resolveRope(ExecState
* exec
) const
203 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
204 Heap::heap(this)->reportExtraMemoryCost(newImpl
->cost());
205 m_value
= newImpl
.release();
210 resolveRopeInternal8(buffer
);
217 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
218 Heap::heap(this)->reportExtraMemoryCost(newImpl
->cost());
219 m_value
= newImpl
.release();
225 resolveRopeInternal16(buffer
);
230 // Overview: These functions convert a JSString from holding a string in rope form
231 // down to a simple String representation. It does so by building up the string
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
236 // we would likely have to place all of the constituent StringImpls into the
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
240 void JSRopeString::resolveRopeSlowCase8(LChar
* buffer
) const
242 LChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
243 Vector
<JSString
*, 32, UnsafeVectorOverflow
> workQueue
; // Putting strings into a Vector is only OK because there are no GC points in this method.
245 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
246 workQueue
.append(m_fibers
[i
].get());
248 while (!workQueue
.isEmpty()) {
249 JSString
* currentFiber
= workQueue
.last();
250 workQueue
.removeLast();
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());
259 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
->m_value
.impl());
260 unsigned length
= string
->length();
262 StringImpl::copyChars(position
, string
->characters8(), length
);
265 ASSERT(buffer
== position
);
268 void JSRopeString::resolveRopeSlowCase(UChar
* buffer
) const
270 UChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
271 Vector
<JSString
*, 32, UnsafeVectorOverflow
> workQueue
; // These strings are kept alive by the parent rope, so using a Vector is OK.
273 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
274 workQueue
.append(m_fibers
[i
].get());
276 while (!workQueue
.isEmpty()) {
277 JSString
* currentFiber
= workQueue
.last();
278 workQueue
.removeLast();
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());
287 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
->m_value
.impl());
288 unsigned length
= string
->length();
290 if (string
->is8Bit())
291 StringImpl::copyChars(position
, string
->characters8(), length
);
293 StringImpl::copyChars(position
, string
->characters16(), length
);
296 ASSERT(buffer
== position
);
299 void JSRopeString::outOfMemory(ExecState
* exec
) const
303 ASSERT(m_value
.isNull());
305 throwOutOfMemoryError(exec
);
308 JSString
* JSRopeString::getIndexSlowCase(ExecState
* exec
, unsigned i
)
312 // Return a safe no-value result, this should never be used, since the excetion will be thrown.
313 if (exec
->exception())
314 return jsEmptyString(exec
);
316 RELEASE_ASSERT(i
< m_value
.length());
317 return jsSingleCharacterSubstring(exec
, m_value
, i
);
320 JSValue
JSString::toPrimitive(ExecState
*, PreferredPrimitiveType
) const
322 return const_cast<JSString
*>(this);
325 bool JSString::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
328 number
= jsToNumber(value(exec
));
332 bool JSString::toBoolean() const
337 double JSString::toNumber(ExecState
* exec
) const
339 return jsToNumber(value(exec
));
342 inline StringObject
* StringObject::create(VM
& vm
, JSGlobalObject
* globalObject
, JSString
* string
)
344 StringObject
* object
= new (NotNull
, allocateCell
<StringObject
>(vm
.heap
)) StringObject(vm
, globalObject
->stringObjectStructure());
345 object
->finishCreation(vm
, string
);
349 JSObject
* JSString::toObject(ExecState
* exec
, JSGlobalObject
* globalObject
) const
351 return StringObject::create(exec
->vm(), globalObject
, const_cast<JSString
*>(this));
354 JSValue
JSString::toThis(JSCell
* cell
, ExecState
* exec
, ECMAMode ecmaMode
)
356 if (ecmaMode
== StrictMode
)
358 return StringObject::create(exec
->vm(), exec
->lexicalGlobalObject(), jsCast
<JSString
*>(cell
));
361 bool JSString::getStringPropertyDescriptor(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
363 if (propertyName
== exec
->propertyNames().length
) {
364 descriptor
.setDescriptor(jsNumber(m_length
), DontEnum
| DontDelete
| ReadOnly
);
368 unsigned i
= propertyName
.asIndex();
370 ASSERT(i
!= PropertyName::NotAnIndex
); // No need for an explicit check, the above test would always fail!
371 descriptor
.setDescriptor(getIndex(exec
, i
), DontDelete
| ReadOnly
);
378 JSString
* jsStringWithCacheSlowCase(VM
& vm
, StringImpl
& stringImpl
)
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();