]>
git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSString.cpp
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 "Operations.h"
30 #include "StringObject.h"
31 #include "StringPrototype.h"
35 static const unsigned substringFromRopeCutoff
= 4;
37 const ClassInfo
JSString::s_info
= { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString
) };
39 void JSRopeString::RopeBuilder::expand()
41 ASSERT(m_index
== JSRopeString::s_maxInternalRopeLength
);
42 JSString
* jsString
= m_jsString
;
43 m_jsString
= jsStringBuilder(&m_globalData
);
48 void JSString::destroy(JSCell
* cell
)
50 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
51 thisObject
->JSString::~JSString();
54 void JSString::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
56 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
57 Base::visitChildren(thisObject
, visitor
);
59 if (thisObject
->isRope())
60 static_cast<JSRopeString
*>(thisObject
)->visitFibers(visitor
);
63 void JSRopeString::visitFibers(SlotVisitor
& visitor
)
65 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
66 visitor
.append(&m_fibers
[i
]);
69 void JSRopeString::resolveRope(ExecState
* exec
) const
75 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
76 Heap::heap(this)->reportExtraMemoryCost(newImpl
->cost());
77 m_value
= newImpl
.release();
83 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
84 if (m_fibers
[i
]->isRope())
85 return resolveRopeSlowCase8(buffer
);
88 LChar
* position
= buffer
;
89 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
90 StringImpl
* string
= m_fibers
[i
]->m_value
.impl();
91 unsigned length
= string
->length();
92 StringImpl::copyChars(position
, string
->characters8(), length
);
96 ASSERT((buffer
+ m_length
) == position
);
103 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
104 Heap::heap(this)->reportExtraMemoryCost(newImpl
->cost());
105 m_value
= newImpl
.release();
111 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
112 if (m_fibers
[i
]->isRope())
113 return resolveRopeSlowCase(buffer
);
116 UChar
* position
= buffer
;
117 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
118 StringImpl
* string
= m_fibers
[i
]->m_value
.impl();
119 unsigned length
= string
->length();
120 StringImpl::copyChars(position
, string
->characters(), length
);
124 ASSERT((buffer
+ m_length
) == position
);
128 // Overview: These functions convert a JSString from holding a string in rope form
129 // down to a simple UString representation. It does so by building up the string
130 // backwards, since we want to avoid recursion, we expect that the tree structure
131 // representing the rope is likely imbalanced with more nodes down the left side
132 // (since appending to the string is likely more common) - and as such resolving
133 // in this fashion should minimize work queue size. (If we built the queue forwards
134 // we would likely have to place all of the constituent StringImpls into the
135 // Vector before performing any concatenation, but by working backwards we likely
136 // only fill the queue with the number of substrings at any given level in a
138 void JSRopeString::resolveRopeSlowCase8(LChar
* buffer
) const
140 LChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
141 Vector
<JSString
*, 32> workQueue
; // Putting strings into a Vector is only OK because there are no GC points in this method.
143 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
144 workQueue
.append(m_fibers
[i
].get());
145 // Clearing here works only because there are no GC points in this method.
149 while (!workQueue
.isEmpty()) {
150 JSString
* currentFiber
= workQueue
.last();
151 workQueue
.removeLast();
153 if (currentFiber
->isRope()) {
154 JSRopeString
* currentFiberAsRope
= static_cast<JSRopeString
*>(currentFiber
);
155 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& currentFiberAsRope
->m_fibers
[i
]; ++i
)
156 workQueue
.append(currentFiberAsRope
->m_fibers
[i
].get());
160 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
->m_value
.impl());
161 unsigned length
= string
->length();
163 StringImpl::copyChars(position
, string
->characters8(), length
);
166 ASSERT(buffer
== position
);
170 void JSRopeString::resolveRopeSlowCase(UChar
* buffer
) const
172 UChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
173 Vector
<JSString
*, 32> workQueue
; // These strings are kept alive by the parent rope, so using a Vector is OK.
175 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
176 workQueue
.append(m_fibers
[i
].get());
178 while (!workQueue
.isEmpty()) {
179 JSString
* currentFiber
= workQueue
.last();
180 workQueue
.removeLast();
182 if (currentFiber
->isRope()) {
183 JSRopeString
* currentFiberAsRope
= static_cast<JSRopeString
*>(currentFiber
);
184 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& currentFiberAsRope
->m_fibers
[i
]; ++i
)
185 workQueue
.append(currentFiberAsRope
->m_fibers
[i
].get());
189 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
->m_value
.impl());
190 unsigned length
= string
->length();
192 StringImpl::copyChars(position
, string
->characters(), length
);
195 ASSERT(buffer
== position
);
199 void JSRopeString::outOfMemory(ExecState
* exec
) const
201 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
204 ASSERT(m_value
== UString());
206 throwOutOfMemoryError(exec
);
209 JSString
* JSRopeString::getIndexSlowCase(ExecState
* exec
, unsigned i
)
213 // Return a safe no-value result, this should never be used, since the excetion will be thrown.
214 if (exec
->exception())
215 return jsString(exec
, "");
217 ASSERT(i
< m_value
.length());
218 return jsSingleCharacterSubstring(exec
, m_value
, i
);
221 JSValue
JSString::toPrimitive(ExecState
*, PreferredPrimitiveType
) const
223 return const_cast<JSString
*>(this);
226 bool JSString::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
229 number
= jsToNumber(value(exec
));
233 bool JSString::toBoolean(ExecState
*) const
238 double JSString::toNumber(ExecState
* exec
) const
240 return jsToNumber(value(exec
));
243 inline StringObject
* StringObject::create(ExecState
* exec
, JSGlobalObject
* globalObject
, JSString
* string
)
245 StringObject
* object
= new (NotNull
, allocateCell
<StringObject
>(*exec
->heap())) StringObject(exec
->globalData(), globalObject
->stringObjectStructure());
246 object
->finishCreation(exec
->globalData(), string
);
250 JSObject
* JSString::toObject(ExecState
* exec
, JSGlobalObject
* globalObject
) const
252 return StringObject::create(exec
, globalObject
, const_cast<JSString
*>(this));
255 JSObject
* JSString::toThisObject(JSCell
* cell
, ExecState
* exec
)
257 return StringObject::create(exec
, exec
->lexicalGlobalObject(), jsCast
<JSString
*>(cell
));
260 bool JSString::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
262 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
263 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
264 // This function should only be called by JSValue::get.
265 if (thisObject
->getStringPropertySlot(exec
, propertyName
, slot
))
267 slot
.setBase(thisObject
);
269 for (JSValue prototype
= exec
->lexicalGlobalObject()->stringPrototype(); !prototype
.isNull(); prototype
= object
->prototype()) {
270 object
= asObject(prototype
);
271 if (object
->methodTable()->getOwnPropertySlot(object
, exec
, propertyName
, slot
))
278 bool JSString::getStringPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
280 if (propertyName
== exec
->propertyNames().length
) {
281 descriptor
.setDescriptor(jsNumber(m_length
), DontEnum
| DontDelete
| ReadOnly
);
286 unsigned i
= propertyName
.toUInt32(isStrictUInt32
);
287 if (isStrictUInt32
&& i
< m_length
) {
288 descriptor
.setDescriptor(getIndex(exec
, i
), DontDelete
| ReadOnly
);
295 bool JSString::getOwnPropertySlotByIndex(JSCell
* cell
, ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
297 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
298 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
299 // This function should only be called by JSValue::get.
300 if (thisObject
->getStringPropertySlot(exec
, propertyName
, slot
))
302 return JSString::getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, propertyName
), slot
);