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, 2014 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 "CallFrame.h"
27 #include "CommonIdentifiers.h"
28 #include "Identifier.h"
29 #include "PropertyDescriptor.h"
30 #include "PropertySlot.h"
31 #include "Structure.h"
38 class LLIntOffsetsExtractor
;
40 JSString
* jsEmptyString(VM
*);
41 JSString
* jsEmptyString(ExecState
*);
42 JSString
* jsString(VM
*, const String
&); // returns empty string if passed null string
43 JSString
* jsString(ExecState
*, const String
&); // returns empty string if passed null string
45 JSString
* jsSingleCharacterString(VM
*, UChar
);
46 JSString
* jsSingleCharacterString(ExecState
*, UChar
);
47 JSString
* jsSingleCharacterSubstring(ExecState
*, const String
&, unsigned offset
);
48 JSString
* jsSubstring(VM
*, const String
&, unsigned offset
, unsigned length
);
49 JSString
* jsSubstring(ExecState
*, const String
&, unsigned offset
, unsigned length
);
51 // Non-trivial strings are two or more characters long.
52 // These functions are faster than just calling jsString.
53 JSString
* jsNontrivialString(VM
*, const String
&);
54 JSString
* jsNontrivialString(ExecState
*, const String
&);
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 String
59 JSString
* jsOwnedString(VM
*, const String
&);
60 JSString
* jsOwnedString(ExecState
*, const String
&);
62 JSRopeString
* jsStringBuilder(VM
*);
64 class JSString
: public JSCell
{
68 friend class SpecializedThunkJIT
;
69 friend class JSRopeString
;
70 friend class MarkStack
;
71 friend class SlotVisitor
;
72 friend struct ThunkHelpers
;
76 static const bool needsDestruction
= true;
77 static const bool hasImmortalStructure
= true;
78 static void destroy(JSCell
*);
81 JSString(VM
& vm
, PassRefPtr
<StringImpl
> value
)
82 : JSCell(vm
, vm
.stringStructure
.get())
89 : JSCell(vm
, vm
.stringStructure
.get())
94 void finishCreation(VM
& vm
, size_t length
)
96 ASSERT(!m_value
.isNull());
97 Base::finishCreation(vm
);
99 setIs8Bit(m_value
.impl()->is8Bit());
100 vm
.m_newStringsSinceLastHashCons
++;
103 void finishCreation(VM
& vm
, size_t length
, size_t cost
)
105 ASSERT(!m_value
.isNull());
106 Base::finishCreation(vm
);
108 setIs8Bit(m_value
.impl()->is8Bit());
109 Heap::heap(this)->reportExtraMemoryCost(cost
);
110 vm
.m_newStringsSinceLastHashCons
++;
114 void finishCreation(VM
& vm
)
116 Base::finishCreation(vm
);
119 vm
.m_newStringsSinceLastHashCons
++;
123 static JSString
* create(VM
& vm
, PassRefPtr
<StringImpl
> value
)
126 int32_t length
= value
->length();
127 RELEASE_ASSERT(length
>= 0);
128 size_t cost
= value
->cost();
129 JSString
* newString
= new (NotNull
, allocateCell
<JSString
>(vm
.heap
)) JSString(vm
, value
);
130 newString
->finishCreation(vm
, length
, cost
);
133 static JSString
* createHasOtherOwner(VM
& vm
, PassRefPtr
<StringImpl
> value
)
136 size_t length
= value
->length();
137 JSString
* newString
= new (NotNull
, allocateCell
<JSString
>(vm
.heap
)) JSString(vm
, value
);
138 newString
->finishCreation(vm
, length
);
142 Identifier
toIdentifier(ExecState
*) const;
143 AtomicString
toAtomicString(ExecState
*) const;
144 AtomicStringImpl
* toExistingAtomicString(ExecState
*) const;
145 const String
& value(ExecState
*) const;
146 const String
& tryGetValue() const;
147 const StringImpl
* tryGetValueImpl() const;
148 unsigned length() const { return m_length
; }
150 JSValue
toPrimitive(ExecState
*, PreferredPrimitiveType
) const;
151 JS_EXPORT_PRIVATE
bool toBoolean() const;
152 bool getPrimitiveNumber(ExecState
*, double& number
, JSValue
&) const;
153 JSObject
* toObject(ExecState
*, JSGlobalObject
*) const;
154 double toNumber(ExecState
*) const;
156 bool getStringPropertySlot(ExecState
*, PropertyName
, PropertySlot
&);
157 bool getStringPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
158 bool getStringPropertyDescriptor(ExecState
*, PropertyName
, PropertyDescriptor
&);
160 bool canGetIndex(unsigned i
) { return i
< m_length
; }
161 JSString
* getIndex(ExecState
*, unsigned);
163 static Structure
* createStructure(VM
& vm
, JSGlobalObject
* globalObject
, JSValue proto
)
165 return Structure::create(vm
, globalObject
, proto
, TypeInfo(StringType
, StructureFlags
), info());
168 static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString
, m_length
); }
169 static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString
, m_flags
); }
170 static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString
, m_value
); }
174 static void dumpToStream(const JSCell
*, PrintStream
&);
175 static void visitChildren(JSCell
*, SlotVisitor
&);
178 HashConsLock
= 1u << 2,
179 IsHashConsSingleton
= 1u << 1,
184 static const unsigned StructureFlags
= OverridesGetOwnPropertySlot
| InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero
| StructureIsImmortal
;
186 friend class JSValue
;
188 bool isRope() const { return m_value
.isNull(); }
189 bool is8Bit() const { return m_flags
& Is8Bit
; }
190 void setIs8Bit(bool flag
)
197 bool shouldTryHashCons();
198 bool isHashConsSingleton() const { return m_flags
& IsHashConsSingleton
; }
199 void clearHashConsSingleton() { m_flags
&= ~IsHashConsSingleton
; }
200 void setHashConsSingleton() { m_flags
|= IsHashConsSingleton
; }
201 bool tryHashConsLock();
202 void releaseHashConsLock();
206 // A string is represented either by a String or a rope of fibers.
208 mutable String m_value
;
211 friend class LLIntOffsetsExtractor
;
213 static JSValue
toThis(JSCell
*, ExecState
*, ECMAMode
);
215 String
& string() { ASSERT(!isRope()); return m_value
; }
217 friend JSValue
jsString(ExecState
*, JSString
*, JSString
*);
218 friend JSString
* jsSubstring(ExecState
*, JSString
*, unsigned offset
, unsigned length
);
221 class JSRopeString
: public JSString
{
222 friend class JSString
;
224 friend JSRopeString
* jsStringBuilder(VM
*);
230 , m_jsString(jsStringBuilder(&vm
))
235 bool append(JSString
* jsString
)
237 if (m_index
== JSRopeString::s_maxInternalRopeLength
)
239 if (static_cast<int32_t>(m_jsString
->length() + jsString
->length()) < 0) {
240 m_jsString
= nullptr;
243 m_jsString
->append(m_vm
, m_index
++, jsString
);
247 JSRopeString
* release()
249 RELEASE_ASSERT(m_jsString
);
250 JSRopeString
* tmp
= m_jsString
;
255 unsigned length() const { return m_jsString
->m_length
; }
261 JSRopeString
* m_jsString
;
271 void finishCreation(VM
& vm
, JSString
* s1
, JSString
* s2
)
273 Base::finishCreation(vm
);
274 m_length
= s1
->length() + s2
->length();
275 setIs8Bit(s1
->is8Bit() && s2
->is8Bit());
276 m_fibers
[0].set(vm
, this, s1
);
277 m_fibers
[1].set(vm
, this, s2
);
280 void finishCreation(VM
& vm
, JSString
* s1
, JSString
* s2
, JSString
* s3
)
282 Base::finishCreation(vm
);
283 m_length
= s1
->length() + s2
->length() + s3
->length();
284 setIs8Bit(s1
->is8Bit() && s2
->is8Bit() && s3
->is8Bit());
285 m_fibers
[0].set(vm
, this, s1
);
286 m_fibers
[1].set(vm
, this, s2
);
287 m_fibers
[2].set(vm
, this, s3
);
290 void finishCreation(VM
& vm
)
292 JSString::finishCreation(vm
);
295 void append(VM
& vm
, size_t index
, JSString
* jsString
)
297 m_fibers
[index
].set(vm
, this, jsString
);
298 m_length
+= jsString
->m_length
;
299 RELEASE_ASSERT(static_cast<int32_t>(m_length
) >= 0);
300 setIs8Bit(is8Bit() && jsString
->is8Bit());
303 static JSRopeString
* createNull(VM
& vm
)
305 JSRopeString
* newString
= new (NotNull
, allocateCell
<JSRopeString
>(vm
.heap
)) JSRopeString(vm
);
306 newString
->finishCreation(vm
);
311 static JSString
* create(VM
& vm
, JSString
* s1
, JSString
* s2
)
313 JSRopeString
* newString
= new (NotNull
, allocateCell
<JSRopeString
>(vm
.heap
)) JSRopeString(vm
);
314 newString
->finishCreation(vm
, s1
, s2
);
317 static JSString
* create(VM
& vm
, JSString
* s1
, JSString
* s2
, JSString
* s3
)
319 JSRopeString
* newString
= new (NotNull
, allocateCell
<JSRopeString
>(vm
.heap
)) JSRopeString(vm
);
320 newString
->finishCreation(vm
, s1
, s2
, s3
);
324 void visitFibers(SlotVisitor
&);
326 static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString
, m_fibers
); }
328 static const unsigned s_maxInternalRopeLength
= 3;
331 friend JSValue
jsStringFromRegisterArray(ExecState
*, Register
*, unsigned);
332 friend JSValue
jsStringFromArguments(ExecState
*, JSValue
);
334 JS_EXPORT_PRIVATE
void resolveRope(ExecState
*) const;
335 JS_EXPORT_PRIVATE
void resolveRopeToAtomicString(ExecState
*) const;
336 JS_EXPORT_PRIVATE AtomicStringImpl
* resolveRopeToExistingAtomicString(ExecState
*) const;
337 void resolveRopeSlowCase8(LChar
*) const;
338 void resolveRopeSlowCase(UChar
*) const;
339 void outOfMemory(ExecState
*) const;
340 void resolveRopeInternal8(LChar
*) const;
341 void resolveRopeInternal16(UChar
*) const;
342 void clearFibers() const;
344 JS_EXPORT_PRIVATE JSString
* getIndexSlowCase(ExecState
*, unsigned);
346 mutable std::array
<WriteBarrier
<JSString
>, s_maxInternalRopeLength
> m_fibers
;
350 inline const StringImpl
* JSString::tryGetValueImpl() const
352 return m_value
.impl();
355 JSString
* asString(JSValue
);
357 inline JSString
* asString(JSValue value
)
359 ASSERT(value
.asCell()->isString());
360 return jsCast
<JSString
*>(value
.asCell());
363 inline JSString
* jsEmptyString(VM
* vm
)
365 return vm
->smallStrings
.emptyString();
368 ALWAYS_INLINE JSString
* jsSingleCharacterString(VM
* vm
, UChar c
)
370 if (c
<= maxSingleCharacterString
)
371 return vm
->smallStrings
.singleCharacterString(c
);
372 return JSString::create(*vm
, String(&c
, 1).impl());
375 ALWAYS_INLINE JSString
* jsSingleCharacterSubstring(ExecState
* exec
, const String
& s
, unsigned offset
)
377 VM
* vm
= &exec
->vm();
378 ASSERT(offset
< static_cast<unsigned>(s
.length()));
379 UChar c
= s
.characterAt(offset
);
380 if (c
<= maxSingleCharacterString
)
381 return vm
->smallStrings
.singleCharacterString(c
);
382 return JSString::create(*vm
, StringImpl::createSubstringSharingImpl(s
.impl(), offset
, 1));
385 inline JSString
* jsNontrivialString(VM
* vm
, const String
& s
)
387 ASSERT(s
.length() > 1);
388 return JSString::create(*vm
, s
.impl());
391 ALWAYS_INLINE Identifier
JSString::toIdentifier(ExecState
* exec
) const
393 return Identifier(exec
, toAtomicString(exec
));
396 ALWAYS_INLINE AtomicString
JSString::toAtomicString(ExecState
* exec
) const
399 static_cast<const JSRopeString
*>(this)->resolveRopeToAtomicString(exec
);
400 return AtomicString(m_value
);
403 ALWAYS_INLINE AtomicStringImpl
* JSString::toExistingAtomicString(ExecState
* exec
) const
406 return static_cast<const JSRopeString
*>(this)->resolveRopeToExistingAtomicString(exec
);
407 if (m_value
.impl()->isAtomic())
408 return static_cast<AtomicStringImpl
*>(m_value
.impl());
409 if (AtomicStringImpl
* existingAtomicString
= AtomicString::find(m_value
.impl())) {
410 m_value
= *existingAtomicString
;
411 return existingAtomicString
;
416 inline const String
& JSString::value(ExecState
* exec
) const
419 static_cast<const JSRopeString
*>(this)->resolveRope(exec
);
423 inline const String
& JSString::tryGetValue() const
426 static_cast<const JSRopeString
*>(this)->resolveRope(0);
430 inline JSString
* JSString::getIndex(ExecState
* exec
, unsigned i
)
432 ASSERT(canGetIndex(i
));
434 return static_cast<JSRopeString
*>(this)->getIndexSlowCase(exec
, i
);
435 ASSERT(i
< m_value
.length());
436 return jsSingleCharacterSubstring(exec
, m_value
, i
);
439 inline JSString
* jsString(VM
* vm
, const String
& s
)
441 int size
= s
.length();
443 return vm
->smallStrings
.emptyString();
445 UChar c
= s
.characterAt(0);
446 if (c
<= maxSingleCharacterString
)
447 return vm
->smallStrings
.singleCharacterString(c
);
449 return JSString::create(*vm
, s
.impl());
452 inline JSString
* jsSubstring(ExecState
* exec
, JSString
* s
, unsigned offset
, unsigned length
)
454 ASSERT(offset
<= static_cast<unsigned>(s
->length()));
455 ASSERT(length
<= static_cast<unsigned>(s
->length()));
456 ASSERT(offset
+ length
<= static_cast<unsigned>(s
->length()));
457 VM
* vm
= &exec
->vm();
459 return vm
->smallStrings
.emptyString();
460 return jsSubstring(vm
, s
->value(exec
), offset
, length
);
463 inline JSString
* jsSubstring8(VM
* vm
, const String
& s
, unsigned offset
, unsigned length
)
465 ASSERT(offset
<= static_cast<unsigned>(s
.length()));
466 ASSERT(length
<= static_cast<unsigned>(s
.length()));
467 ASSERT(offset
+ length
<= static_cast<unsigned>(s
.length()));
469 return vm
->smallStrings
.emptyString();
471 UChar c
= s
.characterAt(offset
);
472 if (c
<= maxSingleCharacterString
)
473 return vm
->smallStrings
.singleCharacterString(c
);
475 return JSString::createHasOtherOwner(*vm
, StringImpl::createSubstringSharingImpl8(s
.impl(), offset
, length
));
478 inline JSString
* jsSubstring(VM
* vm
, const String
& s
, unsigned offset
, unsigned length
)
480 ASSERT(offset
<= static_cast<unsigned>(s
.length()));
481 ASSERT(length
<= static_cast<unsigned>(s
.length()));
482 ASSERT(offset
+ length
<= static_cast<unsigned>(s
.length()));
484 return vm
->smallStrings
.emptyString();
486 UChar c
= s
.characterAt(offset
);
487 if (c
<= maxSingleCharacterString
)
488 return vm
->smallStrings
.singleCharacterString(c
);
490 return JSString::createHasOtherOwner(*vm
, StringImpl::createSubstringSharingImpl(s
.impl(), offset
, length
));
493 inline JSString
* jsOwnedString(VM
* vm
, const String
& s
)
495 int size
= s
.length();
497 return vm
->smallStrings
.emptyString();
499 UChar c
= s
.characterAt(0);
500 if (c
<= maxSingleCharacterString
)
501 return vm
->smallStrings
.singleCharacterString(c
);
503 return JSString::createHasOtherOwner(*vm
, s
.impl());
506 inline JSRopeString
* jsStringBuilder(VM
* vm
)
508 return JSRopeString::createNull(*vm
);
511 inline JSString
* jsEmptyString(ExecState
* exec
) { return jsEmptyString(&exec
->vm()); }
512 inline JSString
* jsString(ExecState
* exec
, const String
& s
) { return jsString(&exec
->vm(), s
); }
513 inline JSString
* jsSingleCharacterString(ExecState
* exec
, UChar c
) { return jsSingleCharacterString(&exec
->vm(), c
); }
514 inline JSString
* jsSubstring8(ExecState
* exec
, const String
& s
, unsigned offset
, unsigned length
) { return jsSubstring8(&exec
->vm(), s
, offset
, length
); }
515 inline JSString
* jsSubstring(ExecState
* exec
, const String
& s
, unsigned offset
, unsigned length
) { return jsSubstring(&exec
->vm(), s
, offset
, length
); }
516 inline JSString
* jsNontrivialString(ExecState
* exec
, const String
& s
) { return jsNontrivialString(&exec
->vm(), s
); }
517 inline JSString
* jsOwnedString(ExecState
* exec
, const String
& s
) { return jsOwnedString(&exec
->vm(), s
); }
519 JS_EXPORT_PRIVATE JSString
* jsStringWithCacheSlowCase(VM
&, StringImpl
&);
521 ALWAYS_INLINE JSString
* jsStringWithCache(ExecState
* exec
, const String
& s
)
524 StringImpl
* stringImpl
= s
.impl();
525 if (!stringImpl
|| !stringImpl
->length())
526 return jsEmptyString(&vm
);
528 if (stringImpl
->length() == 1) {
529 UChar singleCharacter
= (*stringImpl
)[0u];
530 if (singleCharacter
<= maxSingleCharacterString
)
531 return vm
.smallStrings
.singleCharacterString(static_cast<unsigned char>(singleCharacter
));
534 if (JSString
* lastCachedString
= vm
.lastCachedString
.get()) {
535 if (lastCachedString
->tryGetValueImpl() == stringImpl
)
536 return lastCachedString
;
539 return jsStringWithCacheSlowCase(vm
, *stringImpl
);
542 ALWAYS_INLINE JSString
* jsStringWithCache(ExecState
* exec
, const AtomicString
& s
)
544 return jsStringWithCache(exec
, s
.string());
547 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
549 if (propertyName
== exec
->propertyNames().length
) {
550 slot
.setValue(this, DontEnum
| DontDelete
| ReadOnly
, jsNumber(m_length
));
554 unsigned i
= propertyName
.asIndex();
556 ASSERT(i
!= PropertyName::NotAnIndex
); // No need for an explicit check, the above test would always fail!
557 slot
.setValue(this, DontDelete
| ReadOnly
, getIndex(exec
, i
));
564 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
566 if (propertyName
< m_length
) {
567 slot
.setValue(this, DontDelete
| ReadOnly
, getIndex(exec
, propertyName
));
574 inline bool isJSString(JSValue v
) { return v
.isCell() && v
.asCell()->type() == StringType
; }
576 // --- JSValue inlines ----------------------------
578 inline bool JSValue::toBoolean(ExecState
* exec
) const
583 return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
585 return asCell()->toBoolean(exec
);
586 return isTrue(); // false, null, and undefined all convert to false.
589 inline JSString
* JSValue::toString(ExecState
* exec
) const
592 return jsCast
<JSString
*>(asCell());
593 return toStringSlowCase(exec
);
596 inline String
JSValue::toWTFString(ExecState
* exec
) const
599 return static_cast<JSString
*>(asCell())->value(exec
);
600 return toWTFStringSlowCase(exec
);
603 ALWAYS_INLINE String
inlineJSValueNotStringtoString(const JSValue
& value
, ExecState
* exec
)
607 return vm
.numericStrings
.add(value
.asInt32());
608 if (value
.isDouble())
609 return vm
.numericStrings
.add(value
.asDouble());
611 return vm
.propertyNames
->trueKeyword
.string();
613 return vm
.propertyNames
->falseKeyword
.string();
615 return vm
.propertyNames
->nullKeyword
.string();
616 if (value
.isUndefined())
617 return vm
.propertyNames
->undefinedKeyword
.string();
618 return value
.toString(exec
)->value(exec
);
621 ALWAYS_INLINE String
JSValue::toWTFStringInline(ExecState
* exec
) const
624 return static_cast<JSString
*>(asCell())->value(exec
);
626 return inlineJSValueNotStringtoString(*this, exec
);