]>
Commit | Line | Data |
---|---|---|
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" |
f9bf01c6 | 29 | #include "Operations.h" |
9dae56ea A |
30 | #include "StringObject.h" |
31 | #include "StringPrototype.h" | |
32 | ||
33 | namespace JSC { | |
b80e6193 | 34 | |
14957cd0 A |
35 | static const unsigned substringFromRopeCutoff = 4; |
36 | ||
6fe7ccc8 | 37 | const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) }; |
14957cd0 | 38 | |
6fe7ccc8 A |
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 | |
14957cd0 A |
70 | { |
71 | ASSERT(isRope()); | |
72 | ||
6fe7ccc8 A |
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 | ||
14957cd0 | 102 | UChar* buffer; |
6fe7ccc8 A |
103 | if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) { |
104 | Heap::heap(this)->reportExtraMemoryCost(newImpl->cost()); | |
105 | m_value = newImpl.release(); | |
106 | } else { | |
14957cd0 A |
107 | outOfMemory(exec); |
108 | return; | |
109 | } | |
110 | ||
6fe7ccc8 A |
111 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { |
112 | if (m_fibers[i]->isRope()) | |
113 | return resolveRopeSlowCase(buffer); | |
14957cd0 A |
114 | } |
115 | ||
116 | UChar* position = buffer; | |
6fe7ccc8 A |
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(); | |
14957cd0 A |
120 | StringImpl::copyChars(position, string->characters(), length); |
121 | position += length; | |
6fe7ccc8 | 122 | m_fibers[i].clear(); |
14957cd0 | 123 | } |
14957cd0 | 124 | ASSERT((buffer + m_length) == position); |
14957cd0 A |
125 | ASSERT(!isRope()); |
126 | } | |
9dae56ea | 127 | |
6fe7ccc8 | 128 | // Overview: These functions convert a JSString from holding a string in rope form |
f9bf01c6 A |
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 | |
14957cd0 | 134 | // we would likely have to place all of the constituent StringImpls into the |
f9bf01c6 A |
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 | |
14957cd0 | 137 | // rope-of-ropes.) |
6fe7ccc8 | 138 | void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const |
f9bf01c6 | 139 | { |
6fe7ccc8 A |
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(); | |
f9bf01c6 | 147 | } |
14957cd0 | 148 | |
6fe7ccc8 A |
149 | while (!workQueue.isEmpty()) { |
150 | JSString* currentFiber = workQueue.last(); | |
151 | workQueue.removeLast(); | |
b80e6193 | 152 | |
6fe7ccc8 A |
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()); | |
b80e6193 | 157 | continue; |
b80e6193 | 158 | } |
b80e6193 | 159 | |
6fe7ccc8 A |
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); | |
b80e6193 | 164 | } |
6fe7ccc8 A |
165 | |
166 | ASSERT(buffer == position); | |
167 | ASSERT(!isRope()); | |
b80e6193 | 168 | } |
f9bf01c6 | 169 | |
6fe7ccc8 | 170 | void JSRopeString::resolveRopeSlowCase(UChar* buffer) const |
4e4e5a6f | 171 | { |
6fe7ccc8 A |
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. | |
4e4e5a6f | 174 | |
6fe7ccc8 A |
175 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) |
176 | workQueue.append(m_fibers[i].get()); | |
4e4e5a6f | 177 | |
6fe7ccc8 A |
178 | while (!workQueue.isEmpty()) { |
179 | JSString* currentFiber = workQueue.last(); | |
180 | workQueue.removeLast(); | |
4e4e5a6f | 181 | |
6fe7ccc8 A |
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()); | |
4e4e5a6f A |
186 | continue; |
187 | } | |
188 | ||
6fe7ccc8 A |
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); | |
4e4e5a6f A |
193 | } |
194 | ||
6fe7ccc8 A |
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); | |
4e4e5a6f A |
207 | } |
208 | ||
6fe7ccc8 | 209 | JSString* JSRopeString::getIndexSlowCase(ExecState* exec, unsigned i) |
4e4e5a6f A |
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()); | |
14957cd0 | 217 | ASSERT(i < m_value.length()); |
4e4e5a6f A |
218 | return jsSingleCharacterSubstring(exec, m_value, i); |
219 | } | |
220 | ||
ba379fdc | 221 | JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const |
9dae56ea A |
222 | { |
223 | return const_cast<JSString*>(this); | |
224 | } | |
225 | ||
6fe7ccc8 | 226 | bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const |
9dae56ea | 227 | { |
f9bf01c6 | 228 | result = this; |
14957cd0 | 229 | number = jsToNumber(value(exec)); |
9dae56ea A |
230 | return false; |
231 | } | |
232 | ||
233 | bool JSString::toBoolean(ExecState*) const | |
234 | { | |
4e4e5a6f | 235 | return m_length; |
9dae56ea A |
236 | } |
237 | ||
f9bf01c6 | 238 | double JSString::toNumber(ExecState* exec) const |
9dae56ea | 239 | { |
14957cd0 | 240 | return jsToNumber(value(exec)); |
9dae56ea A |
241 | } |
242 | ||
14957cd0 | 243 | inline StringObject* StringObject::create(ExecState* exec, JSGlobalObject* globalObject, JSString* string) |
9dae56ea | 244 | { |
6fe7ccc8 A |
245 | StringObject* object = new (NotNull, allocateCell<StringObject>(*exec->heap())) StringObject(exec->globalData(), globalObject->stringObjectStructure()); |
246 | object->finishCreation(exec->globalData(), string); | |
247 | return object; | |
9dae56ea A |
248 | } |
249 | ||
14957cd0 | 250 | JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const |
9dae56ea | 251 | { |
14957cd0 | 252 | return StringObject::create(exec, globalObject, const_cast<JSString*>(this)); |
9dae56ea A |
253 | } |
254 | ||
6fe7ccc8 | 255 | JSObject* JSString::toThisObject(JSCell* cell, ExecState* exec) |
9dae56ea | 256 | { |
6fe7ccc8 | 257 | return StringObject::create(exec, exec->lexicalGlobalObject(), jsCast<JSString*>(cell)); |
9dae56ea A |
258 | } |
259 | ||
6fe7ccc8 | 260 | bool JSString::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
9dae56ea | 261 | { |
6fe7ccc8 | 262 | JSString* thisObject = jsCast<JSString*>(cell); |
9dae56ea A |
263 | // The semantics here are really getPropertySlot, not getOwnPropertySlot. |
264 | // This function should only be called by JSValue::get. | |
6fe7ccc8 | 265 | if (thisObject->getStringPropertySlot(exec, propertyName, slot)) |
ba379fdc | 266 | return true; |
6fe7ccc8 | 267 | slot.setBase(thisObject); |
9dae56ea | 268 | JSObject* object; |
ba379fdc | 269 | for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) { |
9dae56ea | 270 | object = asObject(prototype); |
6fe7ccc8 | 271 | if (object->methodTable()->getOwnPropertySlot(object, exec, propertyName, slot)) |
9dae56ea A |
272 | return true; |
273 | } | |
274 | slot.setUndefined(); | |
275 | return true; | |
276 | } | |
277 | ||
f9bf01c6 | 278 | bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) |
9dae56ea | 279 | { |
f9bf01c6 | 280 | if (propertyName == exec->propertyNames().length) { |
14957cd0 | 281 | descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly); |
9dae56ea | 282 | return true; |
9dae56ea | 283 | } |
9dae56ea | 284 | |
f9bf01c6 | 285 | bool isStrictUInt32; |
14957cd0 | 286 | unsigned i = propertyName.toUInt32(isStrictUInt32); |
4e4e5a6f A |
287 | if (isStrictUInt32 && i < m_length) { |
288 | descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly); | |
f9bf01c6 | 289 | return true; |
9dae56ea | 290 | } |
f9bf01c6 A |
291 | |
292 | return false; | |
9dae56ea A |
293 | } |
294 | ||
6fe7ccc8 | 295 | bool JSString::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) |
f9bf01c6 | 296 | { |
6fe7ccc8 | 297 | JSString* thisObject = jsCast<JSString*>(cell); |
f9bf01c6 A |
298 | // The semantics here are really getPropertySlot, not getOwnPropertySlot. |
299 | // This function should only be called by JSValue::get. | |
6fe7ccc8 | 300 | if (thisObject->getStringPropertySlot(exec, propertyName, slot)) |
f9bf01c6 | 301 | return true; |
6fe7ccc8 | 302 | return JSString::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); |
9dae56ea A |
303 | } |
304 | ||
305 | } // namespace JSC |