]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSString.cpp
JavaScriptCore-1097.3.3.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"
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
33namespace JSC {
b80e6193 34
14957cd0
A
35static const unsigned substringFromRopeCutoff = 4;
36
6fe7ccc8 37const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) };
14957cd0 38
6fe7ccc8
A
39void 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
48void JSString::destroy(JSCell* cell)
49{
50 JSString* thisObject = jsCast<JSString*>(cell);
51 thisObject->JSString::~JSString();
52}
53
54void 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
63void 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
69void 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 138void 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 170void 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
199void 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 209JSString* 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 221JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
9dae56ea
A
222{
223 return const_cast<JSString*>(this);
224}
225
6fe7ccc8 226bool 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
233bool JSString::toBoolean(ExecState*) const
234{
4e4e5a6f 235 return m_length;
9dae56ea
A
236}
237
f9bf01c6 238double JSString::toNumber(ExecState* exec) const
9dae56ea 239{
14957cd0 240 return jsToNumber(value(exec));
9dae56ea
A
241}
242
14957cd0 243inline 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 250JSObject* 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 255JSObject* JSString::toThisObject(JSCell* cell, ExecState* exec)
9dae56ea 256{
6fe7ccc8 257 return StringObject::create(exec, exec->lexicalGlobalObject(), jsCast<JSString*>(cell));
9dae56ea
A
258}
259
6fe7ccc8 260bool 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 278bool 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 295bool 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