]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSString.cpp
JavaScriptCore-1097.3.3.tar.gz
[apple/javascriptcore.git] / runtime / JSString.cpp
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"
27 #include "JSGlobalObjectFunctions.h"
28 #include "JSObject.h"
29 #include "Operations.h"
30 #include "StringObject.h"
31 #include "StringPrototype.h"
32
33 namespace JSC {
34
35 static const unsigned substringFromRopeCutoff = 4;
36
37 const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) };
38
39 void JSRopeString::RopeBuilder::expand()
40 {
41 ASSERT(m_index == JSRopeString::s_maxInternalRopeLength);
42 JSString* jsString = m_jsString;
43 m_jsString = jsStringBuilder(&m_globalData);
44 m_index = 0;
45 append(jsString);
46 }
47
48 void JSString::destroy(JSCell* cell)
49 {
50 JSString* thisObject = jsCast<JSString*>(cell);
51 thisObject->JSString::~JSString();
52 }
53
54 void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
55 {
56 JSString* thisObject = jsCast<JSString*>(cell);
57 Base::visitChildren(thisObject, visitor);
58
59 if (thisObject->isRope())
60 static_cast<JSRopeString*>(thisObject)->visitFibers(visitor);
61 }
62
63 void JSRopeString::visitFibers(SlotVisitor& visitor)
64 {
65 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
66 visitor.append(&m_fibers[i]);
67 }
68
69 void JSRopeString::resolveRope(ExecState* exec) const
70 {
71 ASSERT(isRope());
72
73 if (is8Bit()) {
74 LChar* buffer;
75 if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
76 Heap::heap(this)->reportExtraMemoryCost(newImpl->cost());
77 m_value = newImpl.release();
78 } else {
79 outOfMemory(exec);
80 return;
81 }
82
83 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
84 if (m_fibers[i]->isRope())
85 return resolveRopeSlowCase8(buffer);
86 }
87
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);
93 position += length;
94 m_fibers[i].clear();
95 }
96 ASSERT((buffer + m_length) == position);
97 ASSERT(!isRope());
98
99 return;
100 }
101
102 UChar* buffer;
103 if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
104 Heap::heap(this)->reportExtraMemoryCost(newImpl->cost());
105 m_value = newImpl.release();
106 } else {
107 outOfMemory(exec);
108 return;
109 }
110
111 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
112 if (m_fibers[i]->isRope())
113 return resolveRopeSlowCase(buffer);
114 }
115
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);
121 position += length;
122 m_fibers[i].clear();
123 }
124 ASSERT((buffer + m_length) == position);
125 ASSERT(!isRope());
126 }
127
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
137 // rope-of-ropes.)
138 void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const
139 {
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.
142
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.
146 m_fibers[i].clear();
147 }
148
149 while (!workQueue.isEmpty()) {
150 JSString* currentFiber = workQueue.last();
151 workQueue.removeLast();
152
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());
157 continue;
158 }
159
160 StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
161 unsigned length = string->length();
162 position -= length;
163 StringImpl::copyChars(position, string->characters8(), length);
164 }
165
166 ASSERT(buffer == position);
167 ASSERT(!isRope());
168 }
169
170 void JSRopeString::resolveRopeSlowCase(UChar* buffer) const
171 {
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.
174
175 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
176 workQueue.append(m_fibers[i].get());
177
178 while (!workQueue.isEmpty()) {
179 JSString* currentFiber = workQueue.last();
180 workQueue.removeLast();
181
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());
186 continue;
187 }
188
189 StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
190 unsigned length = string->length();
191 position -= length;
192 StringImpl::copyChars(position, string->characters(), length);
193 }
194
195 ASSERT(buffer == position);
196 ASSERT(!isRope());
197 }
198
199 void JSRopeString::outOfMemory(ExecState* exec) const
200 {
201 for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
202 m_fibers[i].clear();
203 ASSERT(isRope());
204 ASSERT(m_value == UString());
205 if (exec)
206 throwOutOfMemoryError(exec);
207 }
208
209 JSString* JSRopeString::getIndexSlowCase(ExecState* exec, unsigned i)
210 {
211 ASSERT(isRope());
212 resolveRope(exec);
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, "");
216 ASSERT(!isRope());
217 ASSERT(i < m_value.length());
218 return jsSingleCharacterSubstring(exec, m_value, i);
219 }
220
221 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
222 {
223 return const_cast<JSString*>(this);
224 }
225
226 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
227 {
228 result = this;
229 number = jsToNumber(value(exec));
230 return false;
231 }
232
233 bool JSString::toBoolean(ExecState*) const
234 {
235 return m_length;
236 }
237
238 double JSString::toNumber(ExecState* exec) const
239 {
240 return jsToNumber(value(exec));
241 }
242
243 inline StringObject* StringObject::create(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
244 {
245 StringObject* object = new (NotNull, allocateCell<StringObject>(*exec->heap())) StringObject(exec->globalData(), globalObject->stringObjectStructure());
246 object->finishCreation(exec->globalData(), string);
247 return object;
248 }
249
250 JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const
251 {
252 return StringObject::create(exec, globalObject, const_cast<JSString*>(this));
253 }
254
255 JSObject* JSString::toThisObject(JSCell* cell, ExecState* exec)
256 {
257 return StringObject::create(exec, exec->lexicalGlobalObject(), jsCast<JSString*>(cell));
258 }
259
260 bool JSString::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
261 {
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))
266 return true;
267 slot.setBase(thisObject);
268 JSObject* object;
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))
272 return true;
273 }
274 slot.setUndefined();
275 return true;
276 }
277
278 bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
279 {
280 if (propertyName == exec->propertyNames().length) {
281 descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly);
282 return true;
283 }
284
285 bool isStrictUInt32;
286 unsigned i = propertyName.toUInt32(isStrictUInt32);
287 if (isStrictUInt32 && i < m_length) {
288 descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly);
289 return true;
290 }
291
292 return false;
293 }
294
295 bool JSString::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
296 {
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))
301 return true;
302 return JSString::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot);
303 }
304
305 } // namespace JSC