2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 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.
25 #include "CallFrame.h"
26 #include "CommonIdentifiers.h"
27 #include "Identifier.h"
28 #include "PropertyDescriptor.h"
29 #include "PropertySlot.h"
30 #include "Structure.h"
36 class LLIntOffsetsExtractor
;
38 JSString
* jsEmptyString(JSGlobalData
*);
39 JSString
* jsEmptyString(ExecState
*);
40 JSString
* jsString(JSGlobalData
*, const UString
&); // returns empty string if passed null string
41 JSString
* jsString(ExecState
*, const UString
&); // returns empty string if passed null string
43 JSString
* jsSingleCharacterString(JSGlobalData
*, UChar
);
44 JSString
* jsSingleCharacterString(ExecState
*, UChar
);
45 JSString
* jsSingleCharacterSubstring(ExecState
*, const UString
&, unsigned offset
);
46 JSString
* jsSubstring(JSGlobalData
*, const UString
&, unsigned offset
, unsigned length
);
47 JSString
* jsSubstring(ExecState
*, const UString
&, unsigned offset
, unsigned length
);
49 // Non-trivial strings are two or more characters long.
50 // These functions are faster than just calling jsString.
51 JSString
* jsNontrivialString(JSGlobalData
*, const UString
&);
52 JSString
* jsNontrivialString(ExecState
*, const UString
&);
53 JSString
* jsNontrivialString(JSGlobalData
*, const char*);
54 JSString
* jsNontrivialString(ExecState
*, const char*);
56 // Should be used for strings that are owned by an object that will
57 // likely outlive the JSValue this makes, such as the parse tree or a
58 // DOM object that contains a UString
59 JSString
* jsOwnedString(JSGlobalData
*, const UString
&);
60 JSString
* jsOwnedString(ExecState
*, const UString
&);
62 JSRopeString
* jsStringBuilder(JSGlobalData
*);
64 class JSString
: public JSCell
{
67 friend class JSGlobalData
;
68 friend class SpecializedThunkJIT
;
69 friend class JSRopeString
;
70 friend struct ThunkHelpers
;
74 static void destroy(JSCell
*);
77 JSString(JSGlobalData
& globalData
, PassRefPtr
<StringImpl
> value
)
78 : JSCell(globalData
, globalData
.stringStructure
.get())
83 JSString(JSGlobalData
& globalData
)
84 : JSCell(globalData
, globalData
.stringStructure
.get())
88 void finishCreation(JSGlobalData
& globalData
, size_t length
)
90 ASSERT(!m_value
.isNull());
91 Base::finishCreation(globalData
);
93 m_is8Bit
= m_value
.impl()->is8Bit();
96 void finishCreation(JSGlobalData
& globalData
, size_t length
, size_t cost
)
98 ASSERT(!m_value
.isNull());
99 Base::finishCreation(globalData
);
101 m_is8Bit
= m_value
.impl()->is8Bit();
102 Heap::heap(this)->reportExtraMemoryCost(cost
);
106 void finishCreation(JSGlobalData
& globalData
)
108 Base::finishCreation(globalData
);
114 static JSString
* create(JSGlobalData
& globalData
, PassRefPtr
<StringImpl
> value
)
117 size_t length
= value
->length();
118 size_t cost
= value
->cost();
119 JSString
* newString
= new (NotNull
, allocateCell
<JSString
>(globalData
.heap
)) JSString(globalData
, value
);
120 newString
->finishCreation(globalData
, length
, cost
);
123 static JSString
* createHasOtherOwner(JSGlobalData
& globalData
, PassRefPtr
<StringImpl
> value
)
126 size_t length
= value
->length();
127 JSString
* newString
= new (NotNull
, allocateCell
<JSString
>(globalData
.heap
)) JSString(globalData
, value
);
128 newString
->finishCreation(globalData
, length
);
132 const UString
& value(ExecState
*) const;
133 const UString
& tryGetValue() const;
134 unsigned length() { return m_length
; }
136 JSValue
toPrimitive(ExecState
*, PreferredPrimitiveType
) const;
137 JS_EXPORT_PRIVATE
bool toBoolean(ExecState
*) const;
138 bool getPrimitiveNumber(ExecState
*, double& number
, JSValue
&) const;
139 JSObject
* toObject(ExecState
*, JSGlobalObject
*) const;
140 double toNumber(ExecState
*) const;
142 bool getStringPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
143 bool getStringPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
144 bool getStringPropertyDescriptor(ExecState
*, const Identifier
& propertyName
, PropertyDescriptor
&);
146 bool canGetIndex(unsigned i
) { return i
< m_length
; }
147 JSString
* getIndex(ExecState
*, unsigned);
149 static Structure
* createStructure(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
, JSValue proto
)
151 return Structure::create(globalData
, globalObject
, proto
, TypeInfo(StringType
, OverridesGetOwnPropertySlot
), &s_info
);
154 static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString
, m_length
); }
155 static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString
, m_value
); }
157 static JS_EXPORTDATA
const ClassInfo s_info
;
159 static void visitChildren(JSCell
*, SlotVisitor
&);
162 bool isRope() const { return m_value
.isNull(); }
163 bool is8Bit() const { return m_is8Bit
; }
165 // A string is represented either by a UString or a rope of fibers.
168 mutable UString m_value
;
171 friend class LLIntOffsetsExtractor
;
173 static JSObject
* toThisObject(JSCell
*, ExecState
*);
175 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
176 static bool getOwnPropertySlot(JSCell
*, ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
177 static bool getOwnPropertySlotByIndex(JSCell
*, ExecState
*, unsigned propertyName
, PropertySlot
&);
179 UString
& string() { ASSERT(!isRope()); return m_value
; }
181 friend JSValue
jsString(ExecState
*, JSString
*, JSString
*);
182 friend JSString
* jsSubstring(ExecState
*, JSString
*, unsigned offset
, unsigned length
);
185 class JSRopeString
: public JSString
{
186 friend class JSString
;
188 friend JSRopeString
* jsStringBuilder(JSGlobalData
*);
192 RopeBuilder(JSGlobalData
& globalData
)
193 : m_globalData(globalData
)
194 , m_jsString(jsStringBuilder(&globalData
))
199 void append(JSString
* jsString
)
201 if (m_index
== JSRopeString::s_maxInternalRopeLength
)
203 m_jsString
->m_fibers
[m_index
++].set(m_globalData
, m_jsString
, jsString
);
204 m_jsString
->m_length
+= jsString
->m_length
;
205 m_jsString
->m_is8Bit
= m_jsString
->m_is8Bit
&& jsString
->m_is8Bit
;
208 JSRopeString
* release()
210 JSRopeString
* tmp
= m_jsString
;
215 unsigned length() { return m_jsString
->m_length
; }
220 JSGlobalData
& m_globalData
;
221 JSRopeString
* m_jsString
;
226 JSRopeString(JSGlobalData
& globalData
)
227 : JSString(globalData
)
231 void finishCreation(JSGlobalData
& globalData
, JSString
* s1
, JSString
* s2
)
233 Base::finishCreation(globalData
);
234 m_length
= s1
->length() + s2
->length();
235 m_is8Bit
= (s1
->is8Bit() && s2
->is8Bit());
236 m_fibers
[0].set(globalData
, this, s1
);
237 m_fibers
[1].set(globalData
, this, s2
);
240 void finishCreation(JSGlobalData
& globalData
, JSString
* s1
, JSString
* s2
, JSString
* s3
)
242 Base::finishCreation(globalData
);
243 m_length
= s1
->length() + s2
->length() + s3
->length();
244 m_is8Bit
= (s1
->is8Bit() && s2
->is8Bit() && s3
->is8Bit());
245 m_fibers
[0].set(globalData
, this, s1
);
246 m_fibers
[1].set(globalData
, this, s2
);
247 m_fibers
[2].set(globalData
, this, s3
);
250 void finishCreation(JSGlobalData
& globalData
)
252 JSString::finishCreation(globalData
);
255 static JSRopeString
* createNull(JSGlobalData
& globalData
)
257 JSRopeString
* newString
= new (NotNull
, allocateCell
<JSRopeString
>(globalData
.heap
)) JSRopeString(globalData
);
258 newString
->finishCreation(globalData
);
263 static JSString
* create(JSGlobalData
& globalData
, JSString
* s1
, JSString
* s2
)
265 JSRopeString
* newString
= new (NotNull
, allocateCell
<JSRopeString
>(globalData
.heap
)) JSRopeString(globalData
);
266 newString
->finishCreation(globalData
, s1
, s2
);
269 static JSString
* create(JSGlobalData
& globalData
, JSString
* s1
, JSString
* s2
, JSString
* s3
)
271 JSRopeString
* newString
= new (NotNull
, allocateCell
<JSRopeString
>(globalData
.heap
)) JSRopeString(globalData
);
272 newString
->finishCreation(globalData
, s1
, s2
, s3
);
276 void visitFibers(SlotVisitor
&);
279 friend JSValue
jsString(ExecState
*, Register
*, unsigned);
280 friend JSValue
jsStringFromArguments(ExecState
*, JSValue
);
282 JS_EXPORT_PRIVATE
void resolveRope(ExecState
*) const;
283 void resolveRopeSlowCase8(LChar
*) const;
284 void resolveRopeSlowCase(UChar
*) const;
285 void outOfMemory(ExecState
*) const;
287 JSString
* getIndexSlowCase(ExecState
*, unsigned);
289 static const unsigned s_maxInternalRopeLength
= 3;
291 mutable FixedArray
<WriteBarrier
<JSString
>, s_maxInternalRopeLength
> m_fibers
;
294 JSString
* asString(JSValue
);
296 inline JSString
* asString(JSValue value
)
298 ASSERT(value
.asCell()->isString());
299 return jsCast
<JSString
*>(value
.asCell());
302 inline JSString
* jsEmptyString(JSGlobalData
* globalData
)
304 return globalData
->smallStrings
.emptyString(globalData
);
307 ALWAYS_INLINE JSString
* jsSingleCharacterString(JSGlobalData
* globalData
, UChar c
)
309 if (c
<= maxSingleCharacterString
)
310 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
311 return JSString::create(*globalData
, UString(&c
, 1).impl());
314 ALWAYS_INLINE JSString
* jsSingleCharacterSubstring(ExecState
* exec
, const UString
& s
, unsigned offset
)
316 JSGlobalData
* globalData
= &exec
->globalData();
317 ASSERT(offset
< static_cast<unsigned>(s
.length()));
319 if (c
<= maxSingleCharacterString
)
320 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
321 return JSString::create(*globalData
, StringImpl::create(s
.impl(), offset
, 1));
324 inline JSString
* jsNontrivialString(JSGlobalData
* globalData
, const char* s
)
329 return JSString::create(*globalData
, UString(s
).impl());
332 inline JSString
* jsNontrivialString(JSGlobalData
* globalData
, const UString
& s
)
334 ASSERT(s
.length() > 1);
335 return JSString::create(*globalData
, s
.impl());
338 inline const UString
& JSString::value(ExecState
* exec
) const
341 static_cast<const JSRopeString
*>(this)->resolveRope(exec
);
345 inline const UString
& JSString::tryGetValue() const
348 static_cast<const JSRopeString
*>(this)->resolveRope(0);
352 inline JSString
* JSString::getIndex(ExecState
* exec
, unsigned i
)
354 ASSERT(canGetIndex(i
));
356 return static_cast<JSRopeString
*>(this)->getIndexSlowCase(exec
, i
);
357 ASSERT(i
< m_value
.length());
358 return jsSingleCharacterSubstring(exec
, m_value
, i
);
361 inline JSString
* jsString(JSGlobalData
* globalData
, const UString
& s
)
363 int size
= s
.length();
365 return globalData
->smallStrings
.emptyString(globalData
);
368 if (c
<= maxSingleCharacterString
)
369 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
371 return JSString::create(*globalData
, s
.impl());
374 inline JSString
* jsSubstring(ExecState
* exec
, JSString
* s
, unsigned offset
, unsigned length
)
376 ASSERT(offset
<= static_cast<unsigned>(s
->length()));
377 ASSERT(length
<= static_cast<unsigned>(s
->length()));
378 ASSERT(offset
+ length
<= static_cast<unsigned>(s
->length()));
379 JSGlobalData
* globalData
= &exec
->globalData();
381 return globalData
->smallStrings
.emptyString(globalData
);
382 return jsSubstring(globalData
, s
->value(exec
), offset
, length
);
385 inline JSString
* jsSubstring8(JSGlobalData
* globalData
, const UString
& s
, unsigned offset
, unsigned length
)
387 ASSERT(offset
<= static_cast<unsigned>(s
.length()));
388 ASSERT(length
<= static_cast<unsigned>(s
.length()));
389 ASSERT(offset
+ length
<= static_cast<unsigned>(s
.length()));
391 return globalData
->smallStrings
.emptyString(globalData
);
394 if (c
<= maxSingleCharacterString
)
395 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
397 return JSString::createHasOtherOwner(*globalData
, StringImpl::create8(s
.impl(), offset
, length
));
400 inline JSString
* jsSubstring(JSGlobalData
* globalData
, const UString
& s
, unsigned offset
, unsigned length
)
402 ASSERT(offset
<= static_cast<unsigned>(s
.length()));
403 ASSERT(length
<= static_cast<unsigned>(s
.length()));
404 ASSERT(offset
+ length
<= static_cast<unsigned>(s
.length()));
406 return globalData
->smallStrings
.emptyString(globalData
);
409 if (c
<= maxSingleCharacterString
)
410 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
412 return JSString::createHasOtherOwner(*globalData
, StringImpl::create(s
.impl(), offset
, length
));
415 inline JSString
* jsOwnedString(JSGlobalData
* globalData
, const UString
& s
)
417 int size
= s
.length();
419 return globalData
->smallStrings
.emptyString(globalData
);
422 if (c
<= maxSingleCharacterString
)
423 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
425 return JSString::createHasOtherOwner(*globalData
, s
.impl());
428 inline JSRopeString
* jsStringBuilder(JSGlobalData
* globalData
)
430 return JSRopeString::createNull(*globalData
);
433 inline JSString
* jsEmptyString(ExecState
* exec
) { return jsEmptyString(&exec
->globalData()); }
434 inline JSString
* jsString(ExecState
* exec
, const UString
& s
) { return jsString(&exec
->globalData(), s
); }
435 inline JSString
* jsSingleCharacterString(ExecState
* exec
, UChar c
) { return jsSingleCharacterString(&exec
->globalData(), c
); }
436 inline JSString
* jsSubstring8(ExecState
* exec
, const UString
& s
, unsigned offset
, unsigned length
) { return jsSubstring8(&exec
->globalData(), s
, offset
, length
); }
437 inline JSString
* jsSubstring(ExecState
* exec
, const UString
& s
, unsigned offset
, unsigned length
) { return jsSubstring(&exec
->globalData(), s
, offset
, length
); }
438 inline JSString
* jsNontrivialString(ExecState
* exec
, const UString
& s
) { return jsNontrivialString(&exec
->globalData(), s
); }
439 inline JSString
* jsNontrivialString(ExecState
* exec
, const char* s
) { return jsNontrivialString(&exec
->globalData(), s
); }
440 inline JSString
* jsOwnedString(ExecState
* exec
, const UString
& s
) { return jsOwnedString(&exec
->globalData(), s
); }
442 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
444 if (propertyName
== exec
->propertyNames().length
) {
445 slot
.setValue(jsNumber(m_length
));
450 unsigned i
= propertyName
.toUInt32(isStrictUInt32
);
451 if (isStrictUInt32
&& i
< m_length
) {
452 slot
.setValue(getIndex(exec
, i
));
459 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
461 if (propertyName
< m_length
) {
462 slot
.setValue(getIndex(exec
, propertyName
));
469 inline bool isJSString(JSValue v
) { return v
.isCell() && v
.asCell()->classInfo() == &JSString::s_info
; }
471 inline bool JSCell::toBoolean(ExecState
* exec
) const
474 return static_cast<const JSString
*>(this)->toBoolean(exec
);
475 return !structure()->typeInfo().masqueradesAsUndefined();
478 // --- JSValue inlines ----------------------------
480 inline bool JSValue::toBoolean(ExecState
* exec
) const
485 return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
487 return asCell()->toBoolean(exec
);
488 return isTrue(); // false, null, and undefined all convert to false.
491 inline JSString
* JSValue::toString(ExecState
* exec
) const
494 return jsCast
<JSString
*>(asCell());
495 return toStringSlowCase(exec
);
498 inline UString
JSValue::toUString(ExecState
* exec
) const
501 return static_cast<JSString
*>(asCell())->value(exec
);
502 return toUStringSlowCase(exec
);
505 ALWAYS_INLINE UString
inlineJSValueNotStringtoUString(const JSValue
& value
, ExecState
* exec
)
507 JSGlobalData
& globalData
= exec
->globalData();
509 return globalData
.numericStrings
.add(value
.asInt32());
510 if (value
.isDouble())
511 return globalData
.numericStrings
.add(value
.asDouble());
513 return globalData
.propertyNames
->trueKeyword
.ustring();
515 return globalData
.propertyNames
->falseKeyword
.ustring();
517 return globalData
.propertyNames
->nullKeyword
.ustring();
518 if (value
.isUndefined())
519 return globalData
.propertyNames
->undefinedKeyword
.ustring();
520 return value
.toString(exec
)->value(exec
);
523 ALWAYS_INLINE UString
JSValue::toUStringInline(ExecState
* exec
) const
526 return static_cast<JSString
*>(asCell())->value(exec
);
528 return inlineJSValueNotStringtoUString(*this, exec
);