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.
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.
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.
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.
26 #include "JSGlobalObject.h"
27 #include "JSGlobalObjectFunctions.h"
29 #include "Operations.h"
30 #include "StringObject.h"
31 #include "StringPrototype.h"
35 static const unsigned substringFromRopeCutoff
= 4;
37 const ClassInfo
JSString::s_info
= { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString
) };
39 void JSRopeString::RopeBuilder::expand()
41 ASSERT(m_index
== JSRopeString::s_maxInternalRopeLength
);
42 JSString
* jsString
= m_jsString
;
43 m_jsString
= jsStringBuilder(&m_vm
);
48 void JSString::destroy(JSCell
* cell
)
50 JSString
* thisObject
= static_cast<JSString
*>(cell
);
51 thisObject
->JSString::~JSString();
54 void JSString::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
56 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
57 Base::visitChildren(thisObject
, visitor
);
59 MARK_LOG_MESSAGE1("[%u]: ", thisObject
->length());
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());
67 MARK_LOG_MESSAGE1("[16 %p]", ourImpl
->characters16());
69 MARK_LOG_MESSAGE0("[rope]: ");
72 if (thisObject
->isRope())
73 static_cast<JSRopeString
*>(thisObject
)->visitFibers(visitor
);
76 void JSRopeString::visitFibers(SlotVisitor
& visitor
)
78 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
79 visitor
.append(&m_fibers
[i
]);
82 void JSRopeString::resolveRope(ExecState
* exec
) const
88 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
89 Heap::heap(this)->reportExtraMemoryCost(newImpl
->cost());
90 m_value
= newImpl
.release();
96 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
97 if (m_fibers
[i
]->isRope())
98 return resolveRopeSlowCase8(buffer
);
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
);
109 ASSERT((buffer
+ m_length
) == position
);
116 if (RefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
)) {
117 Heap::heap(this)->reportExtraMemoryCost(newImpl
->cost());
118 m_value
= newImpl
.release();
124 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
) {
125 if (m_fibers
[i
]->isRope())
126 return resolveRopeSlowCase(buffer
);
129 UChar
* position
= buffer
;
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();
133 if (string
->is8Bit())
134 StringImpl::copyChars(position
, string
->characters8(), length
);
136 StringImpl::copyChars(position
, string
->characters16(), length
);
140 ASSERT((buffer
+ m_length
) == position
);
144 // Overview: These functions convert a JSString from holding a string in rope form
145 // down to a simple String representation. It does so by building up the string
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
150 // we would likely have to place all of the constituent StringImpls into the
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
154 void JSRopeString::resolveRopeSlowCase8(LChar
* buffer
) const
156 LChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
157 Vector
<JSString
*, 32, UnsafeVectorOverflow
> workQueue
; // Putting strings into a Vector is only OK because there are no GC points in this method.
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.
165 while (!workQueue
.isEmpty()) {
166 JSString
* currentFiber
= workQueue
.last();
167 workQueue
.removeLast();
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());
176 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
->m_value
.impl());
177 unsigned length
= string
->length();
179 StringImpl::copyChars(position
, string
->characters8(), length
);
182 ASSERT(buffer
== position
);
186 void JSRopeString::resolveRopeSlowCase(UChar
* buffer
) const
188 UChar
* position
= buffer
+ m_length
; // We will be working backwards over the rope.
189 Vector
<JSString
*, 32, UnsafeVectorOverflow
> workQueue
; // These strings are kept alive by the parent rope, so using a Vector is OK.
191 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
192 workQueue
.append(m_fibers
[i
].get());
194 while (!workQueue
.isEmpty()) {
195 JSString
* currentFiber
= workQueue
.last();
196 workQueue
.removeLast();
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());
205 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
->m_value
.impl());
206 unsigned length
= string
->length();
208 if (string
->is8Bit())
209 StringImpl::copyChars(position
, string
->characters8(), length
);
211 StringImpl::copyChars(position
, string
->characters16(), length
);
214 ASSERT(buffer
== position
);
218 void JSRopeString::outOfMemory(ExecState
* exec
) const
220 for (size_t i
= 0; i
< s_maxInternalRopeLength
&& m_fibers
[i
]; ++i
)
223 ASSERT(m_value
.isNull());
225 throwOutOfMemoryError(exec
);
228 JSString
* JSRopeString::getIndexSlowCase(ExecState
* exec
, unsigned i
)
232 // Return a safe no-value result, this should never be used, since the excetion will be thrown.
233 if (exec
->exception())
234 return jsEmptyString(exec
);
236 RELEASE_ASSERT(i
< m_value
.length());
237 return jsSingleCharacterSubstring(exec
, m_value
, i
);
240 JSValue
JSString::toPrimitive(ExecState
*, PreferredPrimitiveType
) const
242 return const_cast<JSString
*>(this);
245 bool JSString::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
248 number
= jsToNumber(value(exec
));
252 bool JSString::toBoolean() const
257 double JSString::toNumber(ExecState
* exec
) const
259 return jsToNumber(value(exec
));
262 inline StringObject
* StringObject::create(ExecState
* exec
, JSGlobalObject
* globalObject
, JSString
* string
)
264 StringObject
* object
= new (NotNull
, allocateCell
<StringObject
>(*exec
->heap())) StringObject(exec
->vm(), globalObject
->stringObjectStructure());
265 object
->finishCreation(exec
->vm(), string
);
269 JSObject
* JSString::toObject(ExecState
* exec
, JSGlobalObject
* globalObject
) const
271 return StringObject::create(exec
, globalObject
, const_cast<JSString
*>(this));
274 JSObject
* JSString::toThisObject(JSCell
* cell
, ExecState
* exec
)
276 return StringObject::create(exec
, exec
->lexicalGlobalObject(), jsCast
<JSString
*>(cell
));
279 bool JSString::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
281 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
282 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
283 // This function should only be called by JSValue::get.
284 if (thisObject
->getStringPropertySlot(exec
, propertyName
, slot
))
286 slot
.setBase(thisObject
);
288 for (JSValue prototype
= exec
->lexicalGlobalObject()->stringPrototype(); !prototype
.isNull(); prototype
= object
->prototype()) {
289 object
= asObject(prototype
);
290 if (object
->methodTable()->getOwnPropertySlot(object
, exec
, propertyName
, slot
))
297 bool JSString::getStringPropertyDescriptor(ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
299 if (propertyName
== exec
->propertyNames().length
) {
300 descriptor
.setDescriptor(jsNumber(m_length
), DontEnum
| DontDelete
| ReadOnly
);
304 unsigned i
= propertyName
.asIndex();
306 ASSERT(i
!= PropertyName::NotAnIndex
); // No need for an explicit check, the above test would always fail!
307 descriptor
.setDescriptor(getIndex(exec
, i
), DontDelete
| ReadOnly
);
314 bool JSString::getOwnPropertySlotByIndex(JSCell
* cell
, ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
316 JSString
* thisObject
= jsCast
<JSString
*>(cell
);
317 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
318 // This function should only be called by JSValue::get.
319 if (thisObject
->getStringPropertySlot(exec
, propertyName
, slot
))
321 return JSString::getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, propertyName
), slot
);