]>
git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSString.cpp
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 };
39 void JSString::resolveRope(ExecState
* exec
) const
44 if (PassRefPtr
<StringImpl
> newImpl
= StringImpl::tryCreateUninitialized(m_length
, buffer
))
51 RopeImpl::Fiber currentFiber
= m_fibers
[0];
53 if ((m_fiberCount
> 2) || (RopeImpl::isRope(currentFiber
))
54 || ((m_fiberCount
== 2) && (RopeImpl::isRope(m_fibers
[1])))) {
55 resolveRopeSlowCase(exec
, buffer
);
59 UChar
* position
= buffer
;
60 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
);
61 unsigned length
= string
->length();
62 StringImpl::copyChars(position
, string
->characters(), length
);
64 if (m_fiberCount
> 1) {
66 currentFiber
= m_fibers
[1];
67 string
= static_cast<StringImpl
*>(currentFiber
);
68 length
= string
->length();
69 StringImpl::copyChars(position
, string
->characters(), length
);
73 ASSERT((buffer
+ m_length
) == position
);
74 for (unsigned i
= 0; i
< m_fiberCount
; ++i
) {
75 RopeImpl::deref(m_fibers
[i
]);
83 // Overview: this methods converts a JSString from holding a string in rope form
84 // down to a simple UString representation. It does so by building up the string
85 // backwards, since we want to avoid recursion, we expect that the tree structure
86 // representing the rope is likely imbalanced with more nodes down the left side
87 // (since appending to the string is likely more common) - and as such resolving
88 // in this fashion should minimize work queue size. (If we built the queue forwards
89 // we would likely have to place all of the constituent StringImpls into the
90 // Vector before performing any concatenation, but by working backwards we likely
91 // only fill the queue with the number of substrings at any given level in a
93 void JSString::resolveRopeSlowCase(ExecState
* exec
, UChar
* buffer
) const
97 UChar
* position
= buffer
+ m_length
;
99 // Start with the current RopeImpl.
100 Vector
<RopeImpl::Fiber
, 32> workQueue
;
101 RopeImpl::Fiber currentFiber
;
102 for (unsigned i
= 0; i
< (m_fiberCount
- 1); ++i
)
103 workQueue
.append(m_fibers
[i
]);
104 currentFiber
= m_fibers
[m_fiberCount
- 1];
106 if (RopeImpl::isRope(currentFiber
)) {
107 RopeImpl
* rope
= static_cast<RopeImpl
*>(currentFiber
);
108 // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber'
109 // (we will be working backwards over the rope).
110 unsigned fiberCountMinusOne
= rope
->fiberCount() - 1;
111 for (unsigned i
= 0; i
< fiberCountMinusOne
; ++i
)
112 workQueue
.append(rope
->fibers()[i
]);
113 currentFiber
= rope
->fibers()[fiberCountMinusOne
];
115 StringImpl
* string
= static_cast<StringImpl
*>(currentFiber
);
116 unsigned length
= string
->length();
118 StringImpl::copyChars(position
, string
->characters(), length
);
120 // Was this the last item in the work queue?
121 if (workQueue
.isEmpty()) {
122 // Create a string from the UChar buffer, clear the rope RefPtr.
123 ASSERT(buffer
== position
);
124 for (unsigned i
= 0; i
< m_fiberCount
; ++i
) {
125 RopeImpl::deref(m_fibers
[i
]);
134 // No! - set the next item up to process.
135 currentFiber
= workQueue
.last();
136 workQueue
.removeLast();
141 void JSString::outOfMemory(ExecState
* exec
) const
143 for (unsigned i
= 0; i
< m_fiberCount
; ++i
) {
144 RopeImpl::deref(m_fibers
[i
]);
149 ASSERT(m_value
== UString());
151 throwOutOfMemoryError(exec
);
154 // This function construsts a substring out of a rope without flattening by reusing the existing fibers.
155 // This can reduce memory usage substantially. Since traversing ropes is slow the function will revert
156 // back to flattening if the rope turns out to be long.
157 JSString
* JSString::substringFromRope(ExecState
* exec
, unsigned substringStart
, unsigned substringLength
)
160 ASSERT(substringLength
);
162 JSGlobalData
* globalData
= &exec
->globalData();
164 UString substringFibers
[3];
166 unsigned fiberCount
= 0;
167 unsigned substringFiberCount
= 0;
168 unsigned substringEnd
= substringStart
+ substringLength
;
169 unsigned fiberEnd
= 0;
172 for (RopeIterator
it(m_fibers
.data(), m_fiberCount
); it
!= end
; ++it
) {
174 StringImpl
* fiberString
= *it
;
175 unsigned fiberStart
= fiberEnd
;
176 fiberEnd
= fiberStart
+ fiberString
->length();
177 if (fiberEnd
<= substringStart
)
179 unsigned copyStart
= std::max(substringStart
, fiberStart
);
180 unsigned copyEnd
= std::min(substringEnd
, fiberEnd
);
181 if (copyStart
== fiberStart
&& copyEnd
== fiberEnd
)
182 substringFibers
[substringFiberCount
++] = UString(fiberString
);
184 substringFibers
[substringFiberCount
++] = UString(StringImpl::create(fiberString
, copyStart
- fiberStart
, copyEnd
- copyStart
));
185 if (fiberEnd
>= substringEnd
)
187 if (fiberCount
> substringFromRopeCutoff
|| substringFiberCount
>= 3) {
188 // This turned out to be a really inefficient rope. Just flatten it.
190 return jsSubstring(&exec
->globalData(), m_value
, substringStart
, substringLength
);
193 ASSERT(substringFiberCount
&& substringFiberCount
<= 3);
195 if (substringLength
== 1) {
196 ASSERT(substringFiberCount
== 1);
197 UChar c
= substringFibers
[0].characters()[0];
198 if (c
<= maxSingleCharacterString
)
199 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
201 if (substringFiberCount
== 1)
202 return new (globalData
) JSString(globalData
, substringFibers
[0]);
203 if (substringFiberCount
== 2)
204 return new (globalData
) JSString(globalData
, substringFibers
[0], substringFibers
[1]);
205 return new (globalData
) JSString(globalData
, substringFibers
[0], substringFibers
[1], substringFibers
[2]);
208 JSValue
JSString::replaceCharacter(ExecState
* exec
, UChar character
, const UString
& replacement
)
211 size_t matchPosition
= m_value
.find(character
);
212 if (matchPosition
== notFound
)
213 return JSValue(this);
214 return jsString(exec
, m_value
.substringSharingImpl(0, matchPosition
), replacement
, m_value
.substringSharingImpl(matchPosition
+ 1));
219 // Count total fibers and find matching string.
220 size_t fiberCount
= 0;
221 StringImpl
* matchString
= 0;
222 size_t matchPosition
= notFound
;
223 for (RopeIterator
it(m_fibers
.data(), m_fiberCount
); it
!= end
; ++it
) {
228 StringImpl
* string
= *it
;
229 matchPosition
= string
->find(character
);
230 if (matchPosition
== notFound
)
232 matchString
= string
;
238 RopeBuilder
builder(replacement
.length() ? fiberCount
+ 2 : fiberCount
+ 1);
239 if (UNLIKELY(builder
.isOutOfMemory()))
240 return throwOutOfMemoryError(exec
);
242 for (RopeIterator
it(m_fibers
.data(), m_fiberCount
); it
!= end
; ++it
) {
243 StringImpl
* string
= *it
;
244 if (string
!= matchString
) {
245 builder
.append(UString(string
));
249 builder
.append(UString(string
).substringSharingImpl(0, matchPosition
));
250 if (replacement
.length())
251 builder
.append(replacement
);
252 builder
.append(UString(string
).substringSharingImpl(matchPosition
+ 1));
256 JSGlobalData
* globalData
= &exec
->globalData();
257 return JSValue(new (globalData
) JSString(globalData
, builder
.release()));
260 JSString
* JSString::getIndexSlowCase(ExecState
* exec
, unsigned i
)
264 // Return a safe no-value result, this should never be used, since the excetion will be thrown.
265 if (exec
->exception())
266 return jsString(exec
, "");
268 ASSERT(i
< m_value
.length());
269 return jsSingleCharacterSubstring(exec
, m_value
, i
);
272 JSValue
JSString::toPrimitive(ExecState
*, PreferredPrimitiveType
) const
274 return const_cast<JSString
*>(this);
277 bool JSString::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
)
280 number
= jsToNumber(value(exec
));
284 bool JSString::toBoolean(ExecState
*) const
289 double JSString::toNumber(ExecState
* exec
) const
291 return jsToNumber(value(exec
));
294 UString
JSString::toString(ExecState
* exec
) const
299 inline StringObject
* StringObject::create(ExecState
* exec
, JSGlobalObject
* globalObject
, JSString
* string
)
301 return new (exec
) StringObject(exec
->globalData(), globalObject
->stringObjectStructure(), string
);
304 JSObject
* JSString::toObject(ExecState
* exec
, JSGlobalObject
* globalObject
) const
306 return StringObject::create(exec
, globalObject
, const_cast<JSString
*>(this));
309 JSObject
* JSString::toThisObject(ExecState
* exec
) const
311 return StringObject::create(exec
, exec
->lexicalGlobalObject(), const_cast<JSString
*>(this));
314 bool JSString::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
316 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
317 // This function should only be called by JSValue::get.
318 if (getStringPropertySlot(exec
, propertyName
, slot
))
320 if (propertyName
== exec
->propertyNames().underscoreProto
) {
321 slot
.setValue(exec
->lexicalGlobalObject()->stringPrototype());
326 for (JSValue prototype
= exec
->lexicalGlobalObject()->stringPrototype(); !prototype
.isNull(); prototype
= object
->prototype()) {
327 object
= asObject(prototype
);
328 if (object
->getOwnPropertySlot(exec
, propertyName
, slot
))
335 bool JSString::getStringPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
337 if (propertyName
== exec
->propertyNames().length
) {
338 descriptor
.setDescriptor(jsNumber(m_length
), DontEnum
| DontDelete
| ReadOnly
);
343 unsigned i
= propertyName
.toUInt32(isStrictUInt32
);
344 if (isStrictUInt32
&& i
< m_length
) {
345 descriptor
.setDescriptor(getIndex(exec
, i
), DontDelete
| ReadOnly
);
352 bool JSString::getOwnPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
354 if (getStringPropertyDescriptor(exec
, propertyName
, descriptor
))
356 if (propertyName
!= exec
->propertyNames().underscoreProto
)
358 descriptor
.setDescriptor(exec
->lexicalGlobalObject()->stringPrototype(), DontEnum
);
362 bool JSString::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
364 // The semantics here are really getPropertySlot, not getOwnPropertySlot.
365 // This function should only be called by JSValue::get.
366 if (getStringPropertySlot(exec
, propertyName
, slot
))
368 return JSString::getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);