]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSString.cpp
JavaScriptCore-721.26.tar.gz
[apple/javascriptcore.git] / runtime / JSString.cpp
CommitLineData
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"
27#include "JSObject.h"
f9bf01c6 28#include "Operations.h"
9dae56ea
A
29#include "StringObject.h"
30#include "StringPrototype.h"
31
32namespace JSC {
b80e6193
A
33
34static const unsigned resolveRopeForSubstringCutoff = 4;
9dae56ea 35
f9bf01c6
A
36// Overview: this methods converts a JSString from holding a string in rope form
37// down to a simple UString representation. It does so by building up the string
38// backwards, since we want to avoid recursion, we expect that the tree structure
39// representing the rope is likely imbalanced with more nodes down the left side
40// (since appending to the string is likely more common) - and as such resolving
41// in this fashion should minimize work queue size. (If we built the queue forwards
4e4e5a6f 42// we would likely have to place all of the constituent UStringImpls into the
f9bf01c6
A
43// Vector before performing any concatenation, but by working backwards we likely
44// only fill the queue with the number of substrings at any given level in a
45// rope-of-ropes.)
46void JSString::resolveRope(ExecState* exec) const
47{
48 ASSERT(isRope());
49
50 // Allocate the buffer to hold the final string, position initially points to the end.
51 UChar* buffer;
4e4e5a6f 52 if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_length, buffer))
f9bf01c6
A
53 m_value = newImpl;
54 else {
4e4e5a6f
A
55 for (unsigned i = 0; i < m_fiberCount; ++i) {
56 RopeImpl::deref(m_other.m_fibers[i]);
57 m_other.m_fibers[i] = 0;
f9bf01c6 58 }
4e4e5a6f 59 m_fiberCount = 0;
f9bf01c6
A
60 ASSERT(!isRope());
61 ASSERT(m_value == UString());
4e4e5a6f
A
62 if (exec)
63 throwOutOfMemoryError(exec);
f9bf01c6
A
64 return;
65 }
4e4e5a6f
A
66 UChar* position = buffer + m_length;
67
68 // Start with the current RopeImpl.
69 Vector<RopeImpl::Fiber, 32> workQueue;
70 RopeImpl::Fiber currentFiber;
71 for (unsigned i = 0; i < (m_fiberCount - 1); ++i)
72 workQueue.append(m_other.m_fibers[i]);
73 currentFiber = m_other.m_fibers[m_fiberCount - 1];
f9bf01c6 74 while (true) {
4e4e5a6f
A
75 if (RopeImpl::isRope(currentFiber)) {
76 RopeImpl* rope = static_cast<RopeImpl*>(currentFiber);
f9bf01c6
A
77 // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber'
78 // (we will be working backwards over the rope).
4e4e5a6f
A
79 unsigned fiberCountMinusOne = rope->fiberCount() - 1;
80 for (unsigned i = 0; i < fiberCountMinusOne; ++i)
81 workQueue.append(rope->fibers()[i]);
82 currentFiber = rope->fibers()[fiberCountMinusOne];
f9bf01c6 83 } else {
4e4e5a6f
A
84 UStringImpl* string = static_cast<UStringImpl*>(currentFiber);
85 unsigned length = string->length();
f9bf01c6 86 position -= length;
4e4e5a6f 87 UStringImpl::copyChars(position, string->characters(), length);
f9bf01c6
A
88
89 // Was this the last item in the work queue?
90 if (workQueue.isEmpty()) {
91 // Create a string from the UChar buffer, clear the rope RefPtr.
92 ASSERT(buffer == position);
4e4e5a6f
A
93 for (unsigned i = 0; i < m_fiberCount; ++i) {
94 RopeImpl::deref(m_other.m_fibers[i]);
95 m_other.m_fibers[i] = 0;
f9bf01c6 96 }
4e4e5a6f 97 m_fiberCount = 0;
f9bf01c6
A
98
99 ASSERT(!isRope());
100 return;
101 }
102
103 // No! - set the next item up to process.
104 currentFiber = workQueue.last();
105 workQueue.removeLast();
106 }
107 }
108}
b80e6193
A
109
110// This function construsts a substring out of a rope without flattening by reusing the existing fibers.
111// This can reduce memory usage substantially. Since traversing ropes is slow the function will revert
112// back to flattening if the rope turns out to be long.
113JSString* JSString::substringFromRope(ExecState* exec, unsigned substringStart, unsigned substringLength)
114{
115 ASSERT(isRope());
116
117 JSGlobalData* globalData = &exec->globalData();
118
119 UString substringFibers[3];
120
121 unsigned fiberCount = 0;
122 unsigned substringFiberCount = 0;
123 unsigned substringEnd = substringStart + substringLength;
124 unsigned fiberEnd = 0;
125
126 RopeIterator end;
127 for (RopeIterator it(m_other.m_fibers, m_fiberCount); it != end; ++it) {
128 ++fiberCount;
129 UStringImpl* fiberString = *it;
130 unsigned fiberStart = fiberEnd;
131 fiberEnd = fiberStart + fiberString->length();
132 if (fiberEnd <= substringStart)
133 continue;
134 unsigned copyStart = std::max(substringStart, fiberStart);
135 unsigned copyEnd = std::min(substringEnd, fiberEnd);
136 if (copyStart == fiberStart && copyEnd == fiberEnd)
137 substringFibers[substringFiberCount++] = UString(fiberString);
138 else
139 substringFibers[substringFiberCount++] = UString(UStringImpl::create(fiberString, copyStart - fiberStart, copyEnd - copyStart));
140 if (fiberEnd >= substringEnd)
141 break;
142 if (fiberCount > resolveRopeForSubstringCutoff || substringFiberCount >= 3) {
143 // This turned out to be a really inefficient rope. Just flatten it.
144 resolveRope(exec);
145 return jsSubstring(&exec->globalData(), m_value, substringStart, substringLength);
146 }
147 }
148 ASSERT(substringFiberCount && substringFiberCount <= 3);
149
150 if (substringLength == 1) {
151 ASSERT(substringFiberCount == 1);
152 UChar c = substringFibers[0].data()[0];
153 if (c <= 0xFF)
154 return globalData->smallStrings.singleCharacterString(globalData, c);
155 }
156 if (substringFiberCount == 1)
157 return new (globalData) JSString(globalData, substringFibers[0]);
158 if (substringFiberCount == 2)
159 return new (globalData) JSString(globalData, substringFibers[0], substringFibers[1]);
160 return new (globalData) JSString(globalData, substringFibers[0], substringFibers[1], substringFibers[2]);
161}
f9bf01c6 162
4e4e5a6f
A
163JSValue JSString::replaceCharacter(ExecState* exec, UChar character, const UString& replacement)
164{
165 if (!isRope()) {
166 unsigned matchPosition = m_value.find(character);
167 if (matchPosition == UString::NotFound)
168 return JSValue(this);
169 return jsString(exec, m_value.substr(0, matchPosition), replacement, m_value.substr(matchPosition + 1));
170 }
171
172 RopeIterator end;
173
174 // Count total fibers and find matching string.
175 size_t fiberCount = 0;
176 UStringImpl* matchString = 0;
177 int matchPosition = -1;
178 for (RopeIterator it(m_other.m_fibers, m_fiberCount); it != end; ++it) {
179 ++fiberCount;
180 if (matchString)
181 continue;
182
183 UStringImpl* string = *it;
184 matchPosition = string->find(character);
185 if (matchPosition == -1)
186 continue;
187 matchString = string;
188 }
189
190 if (!matchString)
191 return this;
192
193 RopeBuilder builder(replacement.size() ? fiberCount + 2 : fiberCount + 1);
194 if (UNLIKELY(builder.isOutOfMemory()))
195 return throwOutOfMemoryError(exec);
196
197 for (RopeIterator it(m_other.m_fibers, m_fiberCount); it != end; ++it) {
198 UStringImpl* string = *it;
199 if (string != matchString) {
200 builder.append(UString(string));
201 continue;
202 }
203
204 builder.append(UString(string).substr(0, matchPosition));
205 if (replacement.size())
206 builder.append(replacement);
207 builder.append(UString(string).substr(matchPosition + 1));
208 matchString = 0;
209 }
210
211 JSGlobalData* globalData = &exec->globalData();
212 return JSValue(new (globalData) JSString(globalData, builder.release()));
213}
214
215JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i)
216{
217 ASSERT(isRope());
218 resolveRope(exec);
219 // Return a safe no-value result, this should never be used, since the excetion will be thrown.
220 if (exec->exception())
221 return jsString(exec, "");
222 ASSERT(!isRope());
223 ASSERT(i < m_value.size());
224 return jsSingleCharacterSubstring(exec, m_value, i);
225}
226
ba379fdc 227JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
9dae56ea
A
228{
229 return const_cast<JSString*>(this);
230}
231
f9bf01c6 232bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
9dae56ea 233{
f9bf01c6
A
234 result = this;
235 number = value(exec).toDouble();
9dae56ea
A
236 return false;
237}
238
239bool JSString::toBoolean(ExecState*) const
240{
4e4e5a6f 241 return m_length;
9dae56ea
A
242}
243
f9bf01c6 244double JSString::toNumber(ExecState* exec) const
9dae56ea 245{
f9bf01c6 246 return value(exec).toDouble();
9dae56ea
A
247}
248
f9bf01c6 249UString JSString::toString(ExecState* exec) const
9dae56ea 250{
f9bf01c6 251 return value(exec);
9dae56ea
A
252}
253
9dae56ea
A
254inline StringObject* StringObject::create(ExecState* exec, JSString* string)
255{
256 return new (exec) StringObject(exec->lexicalGlobalObject()->stringObjectStructure(), string);
257}
258
259JSObject* JSString::toObject(ExecState* exec) const
260{
261 return StringObject::create(exec, const_cast<JSString*>(this));
262}
263
264JSObject* JSString::toThisObject(ExecState* exec) const
265{
266 return StringObject::create(exec, const_cast<JSString*>(this));
267}
268
269bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
270{
271 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
272 // This function should only be called by JSValue::get.
273 if (getStringPropertySlot(exec, propertyName, slot))
274 return true;
ba379fdc
A
275 if (propertyName == exec->propertyNames().underscoreProto) {
276 slot.setValue(exec->lexicalGlobalObject()->stringPrototype());
277 return true;
278 }
9dae56ea
A
279 slot.setBase(this);
280 JSObject* object;
ba379fdc 281 for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) {
9dae56ea
A
282 object = asObject(prototype);
283 if (object->getOwnPropertySlot(exec, propertyName, slot))
284 return true;
285 }
286 slot.setUndefined();
287 return true;
288}
289
f9bf01c6 290bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
9dae56ea 291{
f9bf01c6 292 if (propertyName == exec->propertyNames().length) {
4e4e5a6f 293 descriptor.setDescriptor(jsNumber(exec, m_length), DontEnum | DontDelete | ReadOnly);
9dae56ea 294 return true;
9dae56ea 295 }
9dae56ea 296
f9bf01c6
A
297 bool isStrictUInt32;
298 unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
4e4e5a6f
A
299 if (isStrictUInt32 && i < m_length) {
300 descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly);
f9bf01c6 301 return true;
9dae56ea 302 }
f9bf01c6
A
303
304 return false;
9dae56ea
A
305}
306
f9bf01c6 307bool JSString::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
9dae56ea 308{
f9bf01c6
A
309 if (getStringPropertyDescriptor(exec, propertyName, descriptor))
310 return true;
311 if (propertyName != exec->propertyNames().underscoreProto)
312 return false;
313 descriptor.setDescriptor(exec->lexicalGlobalObject()->stringPrototype(), DontEnum);
314 return true;
315}
316
317bool JSString::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
318{
319 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
320 // This function should only be called by JSValue::get.
321 if (getStringPropertySlot(exec, propertyName, slot))
322 return true;
323 return JSString::getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
9dae56ea
A
324}
325
326} // namespace JSC