]>
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; | |
93a37866 | 43 | m_jsString = jsStringBuilder(&m_vm); |
6fe7ccc8 A |
44 | m_index = 0; |
45 | append(jsString); | |
46 | } | |
47 | ||
48 | void JSString::destroy(JSCell* cell) | |
49 | { | |
93a37866 | 50 | JSString* thisObject = static_cast<JSString*>(cell); |
6fe7ccc8 A |
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 | ||
93a37866 A |
59 | MARK_LOG_MESSAGE1("[%u]: ", thisObject->length()); |
60 | ||
61 | #if ENABLE(OBJECT_MARK_LOGGING) | |
62 | if (!thisObject->isRope()) { | |
63 | WTF::StringImpl* ourImpl = thisObject->m_value.impl(); | |
64 | if (ourImpl->is8Bit()) | |
65 | MARK_LOG_MESSAGE1("[8 %p]", ourImpl->characters8()); | |
66 | else | |
67 | MARK_LOG_MESSAGE1("[16 %p]", ourImpl->characters16()); | |
68 | } else | |
69 | MARK_LOG_MESSAGE0("[rope]: "); | |
70 | #endif | |
71 | ||
6fe7ccc8 A |
72 | if (thisObject->isRope()) |
73 | static_cast<JSRopeString*>(thisObject)->visitFibers(visitor); | |
74 | } | |
75 | ||
76 | void JSRopeString::visitFibers(SlotVisitor& visitor) | |
77 | { | |
78 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) | |
79 | visitor.append(&m_fibers[i]); | |
80 | } | |
81 | ||
82 | void JSRopeString::resolveRope(ExecState* exec) const | |
14957cd0 A |
83 | { |
84 | ASSERT(isRope()); | |
85 | ||
6fe7ccc8 A |
86 | if (is8Bit()) { |
87 | LChar* buffer; | |
88 | if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) { | |
89 | Heap::heap(this)->reportExtraMemoryCost(newImpl->cost()); | |
90 | m_value = newImpl.release(); | |
91 | } else { | |
92 | outOfMemory(exec); | |
93 | return; | |
94 | } | |
95 | ||
96 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { | |
97 | if (m_fibers[i]->isRope()) | |
98 | return resolveRopeSlowCase8(buffer); | |
99 | } | |
100 | ||
101 | LChar* position = buffer; | |
102 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { | |
103 | StringImpl* string = m_fibers[i]->m_value.impl(); | |
104 | unsigned length = string->length(); | |
105 | StringImpl::copyChars(position, string->characters8(), length); | |
106 | position += length; | |
107 | m_fibers[i].clear(); | |
108 | } | |
109 | ASSERT((buffer + m_length) == position); | |
110 | ASSERT(!isRope()); | |
111 | ||
112 | return; | |
113 | } | |
114 | ||
14957cd0 | 115 | UChar* buffer; |
6fe7ccc8 A |
116 | if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) { |
117 | Heap::heap(this)->reportExtraMemoryCost(newImpl->cost()); | |
118 | m_value = newImpl.release(); | |
119 | } else { | |
14957cd0 A |
120 | outOfMemory(exec); |
121 | return; | |
122 | } | |
123 | ||
6fe7ccc8 A |
124 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { |
125 | if (m_fibers[i]->isRope()) | |
126 | return resolveRopeSlowCase(buffer); | |
14957cd0 A |
127 | } |
128 | ||
129 | UChar* position = buffer; | |
6fe7ccc8 A |
130 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { |
131 | StringImpl* string = m_fibers[i]->m_value.impl(); | |
132 | unsigned length = string->length(); | |
93a37866 A |
133 | if (string->is8Bit()) |
134 | StringImpl::copyChars(position, string->characters8(), length); | |
135 | else | |
136 | StringImpl::copyChars(position, string->characters16(), length); | |
14957cd0 | 137 | position += length; |
6fe7ccc8 | 138 | m_fibers[i].clear(); |
14957cd0 | 139 | } |
14957cd0 | 140 | ASSERT((buffer + m_length) == position); |
14957cd0 A |
141 | ASSERT(!isRope()); |
142 | } | |
9dae56ea | 143 | |
6fe7ccc8 | 144 | // Overview: These functions convert a JSString from holding a string in rope form |
93a37866 | 145 | // down to a simple String representation. It does so by building up the string |
f9bf01c6 A |
146 | // backwards, since we want to avoid recursion, we expect that the tree structure |
147 | // representing the rope is likely imbalanced with more nodes down the left side | |
148 | // (since appending to the string is likely more common) - and as such resolving | |
149 | // in this fashion should minimize work queue size. (If we built the queue forwards | |
14957cd0 | 150 | // we would likely have to place all of the constituent StringImpls into the |
f9bf01c6 A |
151 | // Vector before performing any concatenation, but by working backwards we likely |
152 | // only fill the queue with the number of substrings at any given level in a | |
14957cd0 | 153 | // rope-of-ropes.) |
6fe7ccc8 | 154 | void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const |
f9bf01c6 | 155 | { |
6fe7ccc8 | 156 | LChar* position = buffer + m_length; // We will be working backwards over the rope. |
93a37866 | 157 | Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method. |
6fe7ccc8 A |
158 | |
159 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { | |
160 | workQueue.append(m_fibers[i].get()); | |
161 | // Clearing here works only because there are no GC points in this method. | |
162 | m_fibers[i].clear(); | |
f9bf01c6 | 163 | } |
14957cd0 | 164 | |
6fe7ccc8 A |
165 | while (!workQueue.isEmpty()) { |
166 | JSString* currentFiber = workQueue.last(); | |
167 | workQueue.removeLast(); | |
b80e6193 | 168 | |
6fe7ccc8 A |
169 | if (currentFiber->isRope()) { |
170 | JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber); | |
171 | for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i) | |
172 | workQueue.append(currentFiberAsRope->m_fibers[i].get()); | |
b80e6193 | 173 | continue; |
b80e6193 | 174 | } |
b80e6193 | 175 | |
6fe7ccc8 A |
176 | StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl()); |
177 | unsigned length = string->length(); | |
178 | position -= length; | |
179 | StringImpl::copyChars(position, string->characters8(), length); | |
b80e6193 | 180 | } |
6fe7ccc8 A |
181 | |
182 | ASSERT(buffer == position); | |
183 | ASSERT(!isRope()); | |
b80e6193 | 184 | } |
f9bf01c6 | 185 | |
6fe7ccc8 | 186 | void JSRopeString::resolveRopeSlowCase(UChar* buffer) const |
4e4e5a6f | 187 | { |
6fe7ccc8 | 188 | UChar* position = buffer + m_length; // We will be working backwards over the rope. |
93a37866 | 189 | Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK. |
4e4e5a6f | 190 | |
6fe7ccc8 A |
191 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) |
192 | workQueue.append(m_fibers[i].get()); | |
4e4e5a6f | 193 | |
6fe7ccc8 A |
194 | while (!workQueue.isEmpty()) { |
195 | JSString* currentFiber = workQueue.last(); | |
196 | workQueue.removeLast(); | |
4e4e5a6f | 197 | |
6fe7ccc8 A |
198 | if (currentFiber->isRope()) { |
199 | JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber); | |
200 | for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i) | |
201 | workQueue.append(currentFiberAsRope->m_fibers[i].get()); | |
4e4e5a6f A |
202 | continue; |
203 | } | |
204 | ||
6fe7ccc8 A |
205 | StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl()); |
206 | unsigned length = string->length(); | |
207 | position -= length; | |
93a37866 A |
208 | if (string->is8Bit()) |
209 | StringImpl::copyChars(position, string->characters8(), length); | |
210 | else | |
211 | StringImpl::copyChars(position, string->characters16(), length); | |
4e4e5a6f A |
212 | } |
213 | ||
6fe7ccc8 A |
214 | ASSERT(buffer == position); |
215 | ASSERT(!isRope()); | |
216 | } | |
217 | ||
218 | void JSRopeString::outOfMemory(ExecState* exec) const | |
219 | { | |
220 | for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) | |
221 | m_fibers[i].clear(); | |
222 | ASSERT(isRope()); | |
93a37866 | 223 | ASSERT(m_value.isNull()); |
6fe7ccc8 A |
224 | if (exec) |
225 | throwOutOfMemoryError(exec); | |
4e4e5a6f A |
226 | } |
227 | ||
6fe7ccc8 | 228 | JSString* JSRopeString::getIndexSlowCase(ExecState* exec, unsigned i) |
4e4e5a6f A |
229 | { |
230 | ASSERT(isRope()); | |
231 | resolveRope(exec); | |
232 | // Return a safe no-value result, this should never be used, since the excetion will be thrown. | |
233 | if (exec->exception()) | |
93a37866 | 234 | return jsEmptyString(exec); |
4e4e5a6f | 235 | ASSERT(!isRope()); |
93a37866 | 236 | RELEASE_ASSERT(i < m_value.length()); |
4e4e5a6f A |
237 | return jsSingleCharacterSubstring(exec, m_value, i); |
238 | } | |
239 | ||
ba379fdc | 240 | JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const |
9dae56ea A |
241 | { |
242 | return const_cast<JSString*>(this); | |
243 | } | |
244 | ||
6fe7ccc8 | 245 | bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const |
9dae56ea | 246 | { |
f9bf01c6 | 247 | result = this; |
14957cd0 | 248 | number = jsToNumber(value(exec)); |
9dae56ea A |
249 | return false; |
250 | } | |
251 | ||
93a37866 | 252 | bool JSString::toBoolean() const |
9dae56ea | 253 | { |
4e4e5a6f | 254 | return m_length; |
9dae56ea A |
255 | } |
256 | ||
f9bf01c6 | 257 | double JSString::toNumber(ExecState* exec) const |
9dae56ea | 258 | { |
14957cd0 | 259 | return jsToNumber(value(exec)); |
9dae56ea A |
260 | } |
261 | ||
14957cd0 | 262 | inline StringObject* StringObject::create(ExecState* exec, JSGlobalObject* globalObject, JSString* string) |
9dae56ea | 263 | { |
93a37866 A |
264 | StringObject* object = new (NotNull, allocateCell<StringObject>(*exec->heap())) StringObject(exec->vm(), globalObject->stringObjectStructure()); |
265 | object->finishCreation(exec->vm(), string); | |
6fe7ccc8 | 266 | return object; |
9dae56ea A |
267 | } |
268 | ||
14957cd0 | 269 | JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const |
9dae56ea | 270 | { |
14957cd0 | 271 | return StringObject::create(exec, globalObject, const_cast<JSString*>(this)); |
9dae56ea A |
272 | } |
273 | ||
6fe7ccc8 | 274 | JSObject* JSString::toThisObject(JSCell* cell, ExecState* exec) |
9dae56ea | 275 | { |
6fe7ccc8 | 276 | return StringObject::create(exec, exec->lexicalGlobalObject(), jsCast<JSString*>(cell)); |
9dae56ea A |
277 | } |
278 | ||
93a37866 | 279 | bool JSString::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
9dae56ea | 280 | { |
6fe7ccc8 | 281 | JSString* thisObject = jsCast<JSString*>(cell); |
9dae56ea A |
282 | // The semantics here are really getPropertySlot, not getOwnPropertySlot. |
283 | // This function should only be called by JSValue::get. | |
6fe7ccc8 | 284 | if (thisObject->getStringPropertySlot(exec, propertyName, slot)) |
ba379fdc | 285 | return true; |
6fe7ccc8 | 286 | slot.setBase(thisObject); |
9dae56ea | 287 | JSObject* object; |
ba379fdc | 288 | for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) { |
9dae56ea | 289 | object = asObject(prototype); |
6fe7ccc8 | 290 | if (object->methodTable()->getOwnPropertySlot(object, exec, propertyName, slot)) |
9dae56ea A |
291 | return true; |
292 | } | |
293 | slot.setUndefined(); | |
294 | return true; | |
295 | } | |
296 | ||
93a37866 | 297 | bool JSString::getStringPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) |
9dae56ea | 298 | { |
f9bf01c6 | 299 | if (propertyName == exec->propertyNames().length) { |
14957cd0 | 300 | descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly); |
9dae56ea | 301 | return true; |
9dae56ea | 302 | } |
9dae56ea | 303 | |
93a37866 A |
304 | unsigned i = propertyName.asIndex(); |
305 | if (i < m_length) { | |
306 | ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail! | |
4e4e5a6f | 307 | descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly); |
f9bf01c6 | 308 | return true; |
9dae56ea | 309 | } |
f9bf01c6 A |
310 | |
311 | return false; | |
9dae56ea A |
312 | } |
313 | ||
6fe7ccc8 | 314 | bool JSString::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) |
f9bf01c6 | 315 | { |
6fe7ccc8 | 316 | JSString* thisObject = jsCast<JSString*>(cell); |
f9bf01c6 A |
317 | // The semantics here are really getPropertySlot, not getOwnPropertySlot. |
318 | // This function should only be called by JSValue::get. | |
6fe7ccc8 | 319 | if (thisObject->getStringPropertySlot(exec, propertyName, slot)) |
f9bf01c6 | 320 | return true; |
6fe7ccc8 | 321 | return JSString::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); |
9dae56ea A |
322 | } |
323 | ||
324 | } // namespace JSC |