diff options
author | Konstantin Tokarev <annulen@yandex.ru> | 2016-08-25 19:20:41 +0300 |
---|---|---|
committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-02-02 12:30:55 +0000 |
commit | 6882a04fb36642862b11efe514251d32070c3d65 (patch) | |
tree | b7959826000b061fd5ccc7512035c7478742f7b0 /Source/JavaScriptCore/runtime/JSString.h | |
parent | ab6df191029eeeb0b0f16f127d553265659f739e (diff) | |
download | qtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz |
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f
Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSString.h')
-rw-r--r-- | Source/JavaScriptCore/runtime/JSString.h | 441 |
1 files changed, 318 insertions, 123 deletions
diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h index fc383b2f4..6dba78621 100644 --- a/Source/JavaScriptCore/runtime/JSString.h +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -22,12 +22,15 @@ #ifndef JSString_h #define JSString_h + #include "CallFrame.h" #include "CommonIdentifiers.h" #include "Identifier.h" #include "PropertyDescriptor.h" #include "PropertySlot.h" #include "Structure.h" +#include <array> +#include <wtf/text/StringView.h> namespace JSC { @@ -42,7 +45,6 @@ JSString* jsString(ExecState*, const String&); // returns empty string if passed JSString* jsSingleCharacterString(VM*, UChar); JSString* jsSingleCharacterString(ExecState*, UChar); -JSString* jsSingleCharacterSubstring(ExecState*, const String&, unsigned offset); JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length); JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length); @@ -50,6 +52,7 @@ JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned lengt // These functions are faster than just calling jsString. JSString* jsNontrivialString(VM*, const String&); JSString* jsNontrivialString(ExecState*, const String&); +JSString* jsNontrivialString(ExecState*, String&&); // Should be used for strings that are owned by an object that will // likely outlive the JSValue this makes, such as the parse tree or a @@ -59,6 +62,15 @@ JSString* jsOwnedString(ExecState*, const String&); JSRopeString* jsStringBuilder(VM*); +bool isJSString(JSCell*); +bool isJSString(JSValue); +JSString* asString(JSValue); + +struct StringViewWithUnderlyingString { + StringView view; + String underlyingString; +}; + class JSString : public JSCell { public: friend class JIT; @@ -70,9 +82,9 @@ public: friend struct ThunkHelpers; typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal; static const bool needsDestruction = true; - static const bool hasImmortalStructure = true; static void destroy(JSCell*); private: @@ -95,7 +107,6 @@ private: Base::finishCreation(vm); m_length = length; setIs8Bit(m_value.impl()->is8Bit()); - vm.m_newStringsSinceLastHashCons++; } void finishCreation(VM& vm, size_t length, size_t cost) @@ -104,8 +115,7 @@ private: Base::finishCreation(vm); m_length = length; setIs8Bit(m_value.impl()->is8Bit()); - Heap::heap(this)->reportExtraMemoryCost(cost); - vm.m_newStringsSinceLastHashCons++; + Heap::heap(this)->reportExtraMemoryAllocated(cost); } protected: @@ -114,9 +124,8 @@ protected: Base::finishCreation(vm); m_length = 0; setIs8Bit(true); - vm.m_newStringsSinceLastHashCons++; } - + public: static JSString* create(VM& vm, PassRefPtr<StringImpl> value) { @@ -137,16 +146,25 @@ public: return newString; } + Identifier toIdentifier(ExecState*) const; + AtomicString toAtomicString(ExecState*) const; + RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const; + + class SafeView; + SafeView view(ExecState*) const; + StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const; + const String& value(ExecState*) const; const String& tryGetValue() const; - unsigned length() { return m_length; } + const StringImpl* tryGetValueImpl() const; + unsigned length() const { return m_length; } JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; - JS_EXPORT_PRIVATE bool toBoolean() const; + bool toBoolean() const { return !!m_length; } bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; JSObject* toObject(ExecState*, JSGlobalObject*) const; double toNumber(ExecState*) const; - + bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&); bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); @@ -154,70 +172,60 @@ public: bool canGetIndex(unsigned i) { return i < m_length; } JSString* getIndex(ExecState*, unsigned); - static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) - { - return Structure::create(vm, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero), &s_info); - } + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); } static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); } static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); } - static JS_EXPORTDATA const ClassInfo s_info; + DECLARE_EXPORT_INFO; + static void dumpToStream(const JSCell*, PrintStream&); + static size_t estimatedSize(JSCell*); static void visitChildren(JSCell*, SlotVisitor&); enum { - HashConsLock = 1u << 2, - IsHashConsSingleton = 1u << 1, Is8Bit = 1u }; protected: friend class JSValue; - + bool isRope() const { return m_value.isNull(); } + bool isSubstring() const; bool is8Bit() const { return m_flags & Is8Bit; } - void setIs8Bit(bool flag) + void setIs8Bit(bool flag) const { if (flag) m_flags |= Is8Bit; else m_flags &= ~Is8Bit; } - bool shouldTryHashCons(); - bool isHashConsSingleton() const { return m_flags & IsHashConsSingleton; } - void clearHashConsSingleton() { m_flags &= ~IsHashConsSingleton; } - void setHashConsSingleton() { m_flags |= IsHashConsSingleton; } - bool tryHashConsLock(); - void releaseHashConsLock(); - unsigned m_flags; - + mutable unsigned m_flags; + // A string is represented either by a String or a rope of fibers. unsigned m_length; mutable String m_value; private: friend class LLIntOffsetsExtractor; - - static JSObject* toThisObject(JSCell*, ExecState*); - // Actually getPropertySlot, not getOwnPropertySlot (see JSCell). - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static JSValue toThis(JSCell*, ExecState*, ECMAMode); String& string() { ASSERT(!isRope()); return m_value; } + StringView unsafeView(ExecState&) const; friend JSValue jsString(ExecState*, JSString*, JSString*); friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length); }; -class JSRopeString : public JSString { +class JSRopeString final : public JSString { friend class JSString; friend JSRopeString* jsStringBuilder(VM*); +public: class RopeBuilder { public: RopeBuilder(VM& vm) @@ -232,7 +240,7 @@ class JSRopeString : public JSString { if (m_index == JSRopeString::s_maxInternalRopeLength) expand(); if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) { - m_jsString = 0; + m_jsString = nullptr; return false; } m_jsString->append(m_vm, m_index++, jsString); @@ -247,16 +255,16 @@ class JSRopeString : public JSString { return tmp; } - unsigned length() { return m_jsString->m_length; } + unsigned length() const { return m_jsString->m_length; } private: void expand(); - + VM& m_vm; JSRopeString* m_jsString; size_t m_index; }; - + private: JSRopeString(VM& vm) : JSString(vm) @@ -268,28 +276,60 @@ private: Base::finishCreation(vm); m_length = s1->length() + s2->length(); setIs8Bit(s1->is8Bit() && s2->is8Bit()); - m_fibers[0].set(vm, this, s1); - m_fibers[1].set(vm, this, s2); + setIsSubstring(false); + fiber(0).set(vm, this, s1); + fiber(1).set(vm, this, s2); + fiber(2).clear(); } - + void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3) { Base::finishCreation(vm); m_length = s1->length() + s2->length() + s3->length(); setIs8Bit(s1->is8Bit() && s2->is8Bit() && s3->is8Bit()); - m_fibers[0].set(vm, this, s1); - m_fibers[1].set(vm, this, s2); - m_fibers[2].set(vm, this, s3); + setIsSubstring(false); + fiber(0).set(vm, this, s1); + fiber(1).set(vm, this, s2); + fiber(2).set(vm, this, s3); + } + + void finishCreation(ExecState& exec, JSString& base, unsigned offset, unsigned length) + { + VM& vm = exec.vm(); + Base::finishCreation(vm); + ASSERT(!sumOverflows<int32_t>(offset, length)); + ASSERT(offset + length <= base.length()); + m_length = length; + setIs8Bit(base.is8Bit()); + setIsSubstring(true); + if (base.isSubstring()) { + JSRopeString& baseRope = static_cast<JSRopeString&>(base); + substringBase().set(vm, this, baseRope.substringBase().get()); + substringOffset() = baseRope.substringOffset() + offset; + } else { + substringBase().set(vm, this, &base); + substringOffset() = offset; + + // For now, let's not allow substrings with a rope base. + // Resolve non-substring rope bases so we don't have to deal with it. + // FIXME: Evaluate if this would be worth adding more branches. + if (base.isRope()) + static_cast<JSRopeString&>(base).resolveRope(&exec); + } } void finishCreation(VM& vm) { JSString::finishCreation(vm); + setIsSubstring(false); + fiber(0).clear(); + fiber(1).clear(); + fiber(2).clear(); } void append(VM& vm, size_t index, JSString* jsString) { - m_fibers[index].set(vm, this, jsString); + fiber(index).set(vm, this, jsString); m_length += jsString->m_length; RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0); setIs8Bit(is8Bit() && jsString->is8Bit()); @@ -316,27 +356,107 @@ public: return newString; } + static JSString* create(ExecState& exec, JSString& base, unsigned offset, unsigned length) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(exec.vm().heap)) JSRopeString(exec.vm()); + newString->finishCreation(exec, base, offset, length); + return newString; + } + void visitFibers(SlotVisitor&); - - static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); } + + static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); } static const unsigned s_maxInternalRopeLength = 3; - + private: - friend JSValue jsString(ExecState*, Register*, unsigned); + friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned); friend JSValue jsStringFromArguments(ExecState*, JSValue); JS_EXPORT_PRIVATE void resolveRope(ExecState*) const; + JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const; + JS_EXPORT_PRIVATE RefPtr<AtomicStringImpl> resolveRopeToExistingAtomicString(ExecState*) const; void resolveRopeSlowCase8(LChar*) const; void resolveRopeSlowCase(UChar*) const; void outOfMemory(ExecState*) const; - - JSString* getIndexSlowCase(ExecState*, unsigned); + void resolveRopeInternal8(LChar*) const; + void resolveRopeInternal8NoSubstring(LChar*) const; + void resolveRopeInternal16(UChar*) const; + void resolveRopeInternal16NoSubstring(UChar*) const; + void clearFibers() const; + StringView unsafeView(ExecState&) const; + StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const; + + WriteBarrierBase<JSString>& fiber(unsigned i) const + { + ASSERT(!isSubstring()); + ASSERT(i < s_maxInternalRopeLength); + return u[i].string; + } + + WriteBarrierBase<JSString>& substringBase() const + { + return u[1].string; + } - mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers; + uintptr_t& substringOffset() const + { + return u[2].number; + } + + static uintptr_t notSubstringSentinel() + { + return 0; + } + + static uintptr_t substringSentinel() + { + return 1; + } + + bool isSubstring() const + { + return u[0].number == substringSentinel(); + } + + void setIsSubstring(bool isSubstring) + { + u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel(); + } + + mutable union { + uintptr_t number; + WriteBarrierBase<JSString> string; + } u[s_maxInternalRopeLength]; }; -JSString* asString(JSValue); +class JSString::SafeView { +public: + explicit SafeView(ExecState&, const JSString&); + StringView get() const; + + bool is8Bit() const { return m_string->is8Bit(); } + unsigned length() const { return m_string->length(); } + const LChar* characters8() const { return get().characters8(); } + const UChar* characters16() const { return get().characters16(); } + UChar operator[](unsigned index) const { return get()[index]; } + +private: + ExecState& m_state; + + // The following pointer is marked "volatile" to make the compiler leave it on the stack + // or in a register as long as this object is alive, even after the last use of the pointer. + // That's needed to prevent garbage collecting the string and possibly deleting the block + // with the characters in it, and then using the StringView after that. + const JSString* volatile m_string; +}; + +JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&); + +inline const StringImpl* JSString::tryGetValueImpl() const +{ + return m_value.impl(); +} inline JSString* asString(JSValue value) { @@ -352,24 +472,41 @@ inline JSString* jsEmptyString(VM* vm) ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c) { if (c <= maxSingleCharacterString) - return vm->smallStrings.singleCharacterString(vm, c); + return vm->smallStrings.singleCharacterString(c); return JSString::create(*vm, String(&c, 1).impl()); } -ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const String& s, unsigned offset) +inline JSString* jsNontrivialString(VM* vm, const String& s) { - VM* vm = &exec->vm(); - ASSERT(offset < static_cast<unsigned>(s.length())); - UChar c = s.characterAt(offset); - if (c <= maxSingleCharacterString) - return vm->smallStrings.singleCharacterString(vm, c); - return JSString::create(*vm, StringImpl::create(s.impl(), offset, 1)); + ASSERT(s.length() > 1); + return JSString::create(*vm, s.impl()); } -inline JSString* jsNontrivialString(VM* vm, const String& s) +inline JSString* jsNontrivialString(VM* vm, String&& s) { ASSERT(s.length() > 1); - return JSString::create(*vm, s.impl()); + return JSString::create(*vm, s.releaseImpl()); +} + +ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const +{ + return Identifier::fromString(exec, toAtomicString(exec)); +} + +ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const +{ + if (isRope()) + static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec); + return AtomicString(m_value); +} + +ALWAYS_INLINE RefPtr<AtomicStringImpl> JSString::toExistingAtomicString(ExecState* exec) const +{ + if (isRope()) + return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec); + if (m_value.impl()->isAtomic()) + return static_cast<AtomicStringImpl*>(m_value.impl()); + return AtomicStringImpl::lookUp(m_value.impl()); } inline const String& JSString::value(ExecState* exec) const @@ -389,10 +526,7 @@ inline const String& JSString::tryGetValue() const inline JSString* JSString::getIndex(ExecState* exec, unsigned i) { ASSERT(canGetIndex(i)); - if (isRope()) - return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i); - ASSERT(i < m_value.length()); - return jsSingleCharacterSubstring(exec, m_value, i); + return jsSingleCharacterString(exec, unsafeView(*exec)[i]); } inline JSString* jsString(VM* vm, const String& s) @@ -403,7 +537,7 @@ inline JSString* jsString(VM* vm, const String& s) if (size == 1) { UChar c = s.characterAt(0); if (c <= maxSingleCharacterString) - return vm->smallStrings.singleCharacterString(vm, c); + return vm->smallStrings.singleCharacterString(c); } return JSString::create(*vm, s.impl()); } @@ -413,25 +547,12 @@ inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsi ASSERT(offset <= static_cast<unsigned>(s->length())); ASSERT(length <= static_cast<unsigned>(s->length())); ASSERT(offset + length <= static_cast<unsigned>(s->length())); - VM* vm = &exec->vm(); - if (!length) - return vm->smallStrings.emptyString(); - return jsSubstring(vm, s->value(exec), offset, length); -} - -inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length) -{ - ASSERT(offset <= static_cast<unsigned>(s.length())); - ASSERT(length <= static_cast<unsigned>(s.length())); - ASSERT(offset + length <= static_cast<unsigned>(s.length())); + VM& vm = exec->vm(); if (!length) - return vm->smallStrings.emptyString(); - if (length == 1) { - UChar c = s.characterAt(offset); - if (c <= maxSingleCharacterString) - return vm->smallStrings.singleCharacterString(vm, c); - } - return JSString::createHasOtherOwner(*vm, StringImpl::create8(s.impl(), offset, length)); + return vm.smallStrings.emptyString(); + if (!offset && length == s->length()) + return s; + return JSRopeString::create(*exec, *s, offset, length); } inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length) @@ -444,9 +565,9 @@ inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned if (length == 1) { UChar c = s.characterAt(offset); if (c <= maxSingleCharacterString) - return vm->smallStrings.singleCharacterString(vm, c); + return vm->smallStrings.singleCharacterString(c); } - return JSString::createHasOtherOwner(*vm, StringImpl::create(s.impl(), offset, length)); + return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, length)); } inline JSString* jsOwnedString(VM* vm, const String& s) @@ -457,7 +578,7 @@ inline JSString* jsOwnedString(VM* vm, const String& s) if (size == 1) { UChar c = s.characterAt(0); if (c <= maxSingleCharacterString) - return vm->smallStrings.singleCharacterString(vm, c); + return vm->smallStrings.singleCharacterString(c); } return JSString::createHasOtherOwner(*vm, s.impl()); } @@ -470,42 +591,133 @@ inline JSRopeString* jsStringBuilder(VM* vm) inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); } inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); } inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); } -inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); } inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); } inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); } +inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTFMove(s)); } inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); } +ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s) +{ + VM& vm = exec->vm(); + StringImpl* stringImpl = s.impl(); + if (!stringImpl || !stringImpl->length()) + return jsEmptyString(&vm); + + if (stringImpl->length() == 1) { + UChar singleCharacter = (*stringImpl)[0u]; + if (singleCharacter <= maxSingleCharacterString) + return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter)); + } + + if (JSString* lastCachedString = vm.lastCachedString.get()) { + if (lastCachedString->tryGetValueImpl() == stringImpl) + return lastCachedString; + } + + return jsStringWithCacheSlowCase(vm, *stringImpl); +} + +ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const AtomicString& s) +{ + return jsStringWithCache(exec, s.string()); +} + ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) { if (propertyName == exec->propertyNames().length) { - slot.setValue(jsNumber(m_length)); + slot.setValue(this, DontEnum | DontDelete | ReadOnly, jsNumber(m_length)); return true; } - unsigned i = propertyName.asIndex(); - if (i < m_length) { - ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail! - slot.setValue(getIndex(exec, i)); + Optional<uint32_t> index = parseIndex(propertyName); + if (index && index.value() < m_length) { + slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, index.value())); return true; } return false; } - + ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { if (propertyName < m_length) { - slot.setValue(getIndex(exec, propertyName)); + slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, propertyName)); return true; } return false; } -inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; } +inline bool isJSString(JSCell* cell) +{ + return cell->type() == StringType; +} + +inline bool isJSString(JSValue v) +{ + return v.isCell() && isJSString(v.asCell()); +} + +ALWAYS_INLINE StringView JSRopeString::unsafeView(ExecState& state) const +{ + if (isSubstring()) { + if (is8Bit()) + return StringView(substringBase()->m_value.characters8() + substringOffset(), m_length); + return StringView(substringBase()->m_value.characters16() + substringOffset(), m_length); + } + resolveRope(&state); + return m_value; +} + +ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState& state) const +{ + if (isSubstring()) { + auto& base = substringBase()->m_value; + if (is8Bit()) + return { { base.characters8() + substringOffset(), m_length }, base }; + return { { base.characters16() + substringOffset(), m_length }, base }; + } + resolveRope(&state); + return { m_value, m_value }; +} + +ALWAYS_INLINE StringView JSString::unsafeView(ExecState& state) const +{ + if (isRope()) + return static_cast<const JSRopeString*>(this)->unsafeView(state); + return m_value; +} + +ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState& state) const +{ + if (isRope()) + return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(state); + return { m_value, m_value }; +} + +inline bool JSString::isSubstring() const +{ + return isRope() && static_cast<const JSRopeString*>(this)->isSubstring(); +} + +inline JSString::SafeView::SafeView(ExecState& state, const JSString& string) + : m_state(state) + , m_string(&string) +{ +} + +inline StringView JSString::SafeView::get() const +{ + return m_string->unsafeView(m_state); +} + +ALWAYS_INLINE JSString::SafeView JSString::view(ExecState* exec) const +{ + return SafeView(*exec, *this); +} // --- JSValue inlines ---------------------------- - + inline bool JSValue::toBoolean(ExecState* exec) const { if (isInt32()) @@ -521,40 +733,23 @@ inline JSString* JSValue::toString(ExecState* exec) const { if (isString()) return jsCast<JSString*>(asCell()); - return toStringSlowCase(exec); + bool returnEmptyStringOnError = true; + return toStringSlowCase(exec, returnEmptyStringOnError); } -inline String JSValue::toWTFString(ExecState* exec) const +inline JSString* JSValue::toStringOrNull(ExecState* exec) const { if (isString()) - return static_cast<JSString*>(asCell())->value(exec); - return toWTFStringSlowCase(exec); + return jsCast<JSString*>(asCell()); + bool returnEmptyStringOnError = false; + return toStringSlowCase(exec, returnEmptyStringOnError); } -ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec) -{ - VM& vm = exec->vm(); - if (value.isInt32()) - return vm.numericStrings.add(value.asInt32()); - if (value.isDouble()) - return vm.numericStrings.add(value.asDouble()); - if (value.isTrue()) - return vm.propertyNames->trueKeyword.string(); - if (value.isFalse()) - return vm.propertyNames->falseKeyword.string(); - if (value.isNull()) - return vm.propertyNames->nullKeyword.string(); - if (value.isUndefined()) - return vm.propertyNames->undefinedKeyword.string(); - return value.toString(exec)->value(exec); -} - -ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const +inline String JSValue::toWTFString(ExecState* exec) const { if (isString()) return static_cast<JSString*>(asCell())->value(exec); - - return inlineJSValueNotStringtoString(*this, exec); + return toWTFStringSlowCase(exec); } } // namespace JSC |