#ifndef JSString_h
#define JSString_h
-
#include "CallFrame.h"
#include "CommonIdentifiers.h"
#include "Identifier.h"
-#include "JSNumberCell.h"
+#include "PropertyDescriptor.h"
#include "PropertySlot.h"
+#include "Structure.h"
namespace JSC {
class JSString;
+ class JSRopeString;
+ class LLIntOffsetsExtractor;
JSString* jsEmptyString(JSGlobalData*);
JSString* jsEmptyString(ExecState*);
JSString* jsSingleCharacterString(JSGlobalData*, UChar);
JSString* jsSingleCharacterString(ExecState*, UChar);
- JSString* jsSingleCharacterSubstring(JSGlobalData*, const UString&, unsigned offset);
JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset);
JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length);
JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length);
JSString* jsOwnedString(JSGlobalData*, const UString&);
JSString* jsOwnedString(ExecState*, const UString&);
+ JSRopeString* jsStringBuilder(JSGlobalData*);
+
class JSString : public JSCell {
+ public:
friend class JIT;
- friend class VPtrSet;
+ friend class JSGlobalData;
+ friend class SpecializedThunkJIT;
+ friend class JSRopeString;
+ friend struct ThunkHelpers;
- public:
- JSString(JSGlobalData* globalData, const UString& value)
- : JSCell(globalData->stringStructure.get())
+ typedef JSCell Base;
+
+ static void destroy(JSCell*);
+
+ private:
+ JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
+ : JSCell(globalData, globalData.stringStructure.get())
, m_value(value)
{
- Heap::heap(this)->reportExtraMemoryCost(value.cost());
}
- enum HasOtherOwnerType { HasOtherOwner };
- JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType)
- : JSCell(globalData->stringStructure.get())
- , m_value(value)
+ JSString(JSGlobalData& globalData)
+ : JSCell(globalData, globalData.stringStructure.get())
{
}
- JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType)
- : JSCell(globalData->stringStructure.get())
- , m_value(value)
+
+ void finishCreation(JSGlobalData& globalData, size_t length)
+ {
+ ASSERT(!m_value.isNull());
+ Base::finishCreation(globalData);
+ m_length = length;
+ m_is8Bit = m_value.impl()->is8Bit();
+ }
+
+ void finishCreation(JSGlobalData& globalData, size_t length, size_t cost)
{
+ ASSERT(!m_value.isNull());
+ Base::finishCreation(globalData);
+ m_length = length;
+ m_is8Bit = m_value.impl()->is8Bit();
+ Heap::heap(this)->reportExtraMemoryCost(cost);
+ }
+
+ protected:
+ void finishCreation(JSGlobalData& globalData)
+ {
+ Base::finishCreation(globalData);
+ m_length = 0;
+ m_is8Bit = true;
}
- const UString& value() const { return m_value; }
+ public:
+ static JSString* create(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
+ {
+ ASSERT(value);
+ size_t length = value->length();
+ size_t cost = value->cost();
+ JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
+ newString->finishCreation(globalData, length, cost);
+ return newString;
+ }
+ static JSString* createHasOtherOwner(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
+ {
+ ASSERT(value);
+ size_t length = value->length();
+ JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
+ newString->finishCreation(globalData, length);
+ return newString;
+ }
+
+ const UString& value(ExecState*) const;
+ const UString& tryGetValue() const;
+ unsigned length() { return m_length; }
+ JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
+ JS_EXPORT_PRIVATE bool toBoolean(ExecState*) const;
+ bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
+ JSObject* toObject(ExecState*, JSGlobalObject*) const;
+ double toNumber(ExecState*) const;
+
bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
+ bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
+
+ bool canGetIndex(unsigned i) { return i < m_length; }
+ JSString* getIndex(ExecState*, unsigned);
+
+ static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
+ {
+ return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot), &s_info);
+ }
+
+ static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
+ static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
+
+ static JS_EXPORTDATA const ClassInfo s_info;
+
+ static void visitChildren(JSCell*, SlotVisitor&);
- bool canGetIndex(unsigned i) { return i < static_cast<unsigned>(m_value.size()); }
- JSString* getIndex(JSGlobalData*, unsigned);
+ protected:
+ bool isRope() const { return m_value.isNull(); }
+ bool is8Bit() const { return m_is8Bit; }
- static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, NeedsThisConversion)); }
+ // A string is represented either by a UString or a rope of fibers.
+ bool m_is8Bit : 1;
+ unsigned m_length;
+ mutable UString m_value;
+
+ private:
+ friend class LLIntOffsetsExtractor;
+
+ static JSObject* toThisObject(JSCell*, ExecState*);
+ // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
+ static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
+ static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
+
+ UString& string() { ASSERT(!isRope()); return m_value; }
+
+ friend JSValue jsString(ExecState*, JSString*, JSString*);
+ friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
+ };
+
+ class JSRopeString : public JSString {
+ friend class JSString;
+
+ friend JSRopeString* jsStringBuilder(JSGlobalData*);
+
+ class RopeBuilder {
+ public:
+ RopeBuilder(JSGlobalData& globalData)
+ : m_globalData(globalData)
+ , m_jsString(jsStringBuilder(&globalData))
+ , m_index(0)
+ {
+ }
+
+ void append(JSString* jsString)
+ {
+ if (m_index == JSRopeString::s_maxInternalRopeLength)
+ expand();
+ m_jsString->m_fibers[m_index++].set(m_globalData, m_jsString, jsString);
+ m_jsString->m_length += jsString->m_length;
+ m_jsString->m_is8Bit = m_jsString->m_is8Bit && jsString->m_is8Bit;
+ }
+
+ JSRopeString* release()
+ {
+ JSRopeString* tmp = m_jsString;
+ m_jsString = 0;
+ return tmp;
+ }
+
+ unsigned length() { return m_jsString->m_length; }
+
+ private:
+ void expand();
+
+ JSGlobalData& m_globalData;
+ JSRopeString* m_jsString;
+ size_t m_index;
+ };
+
private:
- enum VPtrStealingHackType { VPtrStealingHack };
- JSString(VPtrStealingHackType)
- : JSCell(0)
+ JSRopeString(JSGlobalData& globalData)
+ : JSString(globalData)
{
}
- virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
- virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
- virtual bool toBoolean(ExecState*) const;
- virtual double toNumber(ExecState*) const;
- virtual JSObject* toObject(ExecState*) const;
- virtual UString toString(ExecState*) const;
+ void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2)
+ {
+ Base::finishCreation(globalData);
+ m_length = s1->length() + s2->length();
+ m_is8Bit = (s1->is8Bit() && s2->is8Bit());
+ m_fibers[0].set(globalData, this, s1);
+ m_fibers[1].set(globalData, this, s2);
+ }
+
+ void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
+ {
+ Base::finishCreation(globalData);
+ m_length = s1->length() + s2->length() + s3->length();
+ m_is8Bit = (s1->is8Bit() && s2->is8Bit() && s3->is8Bit());
+ m_fibers[0].set(globalData, this, s1);
+ m_fibers[1].set(globalData, this, s2);
+ m_fibers[2].set(globalData, this, s3);
+ }
- virtual JSObject* toThisObject(ExecState*) const;
- virtual UString toThisString(ExecState*) const;
- virtual JSString* toThisJSString(ExecState*);
+ void finishCreation(JSGlobalData& globalData)
+ {
+ JSString::finishCreation(globalData);
+ }
- // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
- virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
- virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
+ static JSRopeString* createNull(JSGlobalData& globalData)
+ {
+ JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData);
+ newString->finishCreation(globalData);
+ return newString;
+ }
+
+ public:
+ static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2)
+ {
+ JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData);
+ newString->finishCreation(globalData, s1, s2);
+ return newString;
+ }
+ static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
+ {
+ JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData);
+ newString->finishCreation(globalData, s1, s2, s3);
+ return newString;
+ }
+
+ void visitFibers(SlotVisitor&);
- UString m_value;
+ private:
+ friend JSValue jsString(ExecState*, Register*, unsigned);
+ friend JSValue jsStringFromArguments(ExecState*, JSValue);
+
+ JS_EXPORT_PRIVATE void resolveRope(ExecState*) const;
+ void resolveRopeSlowCase8(LChar*) const;
+ void resolveRopeSlowCase(UChar*) const;
+ void outOfMemory(ExecState*) const;
+
+ JSString* getIndexSlowCase(ExecState*, unsigned);
+
+ static const unsigned s_maxInternalRopeLength = 3;
+
+ mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
};
JSString* asString(JSValue);
inline JSString* asString(JSValue value)
{
- ASSERT(asCell(value)->isString());
- return static_cast<JSString*>(asCell(value));
+ ASSERT(value.asCell()->isString());
+ return jsCast<JSString*>(value.asCell());
}
inline JSString* jsEmptyString(JSGlobalData* globalData)
return globalData->smallStrings.emptyString(globalData);
}
- inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
+ ALWAYS_INLINE JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
{
- if (c <= 0xFF)
+ if (c <= maxSingleCharacterString)
return globalData->smallStrings.singleCharacterString(globalData, c);
- return new (globalData) JSString(globalData, UString(&c, 1));
+ return JSString::create(*globalData, UString(&c, 1).impl());
}
- inline JSString* jsSingleCharacterSubstring(JSGlobalData* globalData, const UString& s, unsigned offset)
+ ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
{
- ASSERT(offset < static_cast<unsigned>(s.size()));
- UChar c = s.data()[offset];
- if (c <= 0xFF)
+ JSGlobalData* globalData = &exec->globalData();
+ ASSERT(offset < static_cast<unsigned>(s.length()));
+ UChar c = s[offset];
+ if (c <= maxSingleCharacterString)
return globalData->smallStrings.singleCharacterString(globalData, c);
- return new (globalData) JSString(globalData, UString::Rep::create(s.rep(), offset, 1));
+ return JSString::create(*globalData, StringImpl::create(s.impl(), offset, 1));
}
inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
ASSERT(s);
ASSERT(s[0]);
ASSERT(s[1]);
- return new (globalData) JSString(globalData, s);
+ return JSString::create(*globalData, UString(s).impl());
}
inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
{
- ASSERT(s.size() > 1);
- return new (globalData) JSString(globalData, s);
+ ASSERT(s.length() > 1);
+ return JSString::create(*globalData, s.impl());
+ }
+
+ inline const UString& JSString::value(ExecState* exec) const
+ {
+ if (isRope())
+ static_cast<const JSRopeString*>(this)->resolveRope(exec);
+ return m_value;
+ }
+
+ inline const UString& JSString::tryGetValue() const
+ {
+ if (isRope())
+ static_cast<const JSRopeString*>(this)->resolveRope(0);
+ return m_value;
}
- inline JSString* JSString::getIndex(JSGlobalData* globalData, unsigned i)
+ inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
{
ASSERT(canGetIndex(i));
- return jsSingleCharacterSubstring(globalData, m_value, i);
+ if (isRope())
+ return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i);
+ ASSERT(i < m_value.length());
+ return jsSingleCharacterSubstring(exec, m_value, i);
+ }
+
+ inline JSString* jsString(JSGlobalData* globalData, const UString& s)
+ {
+ int size = s.length();
+ if (!size)
+ return globalData->smallStrings.emptyString(globalData);
+ if (size == 1) {
+ UChar c = s[0];
+ if (c <= maxSingleCharacterString)
+ return globalData->smallStrings.singleCharacterString(globalData, c);
+ }
+ return JSString::create(*globalData, s.impl());
+ }
+
+ inline JSString* jsSubstring(ExecState* exec, JSString* 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()));
+ JSGlobalData* globalData = &exec->globalData();
+ if (!length)
+ return globalData->smallStrings.emptyString(globalData);
+ return jsSubstring(globalData, s->value(exec), offset, length);
+ }
+
+ inline JSString* jsSubstring8(JSGlobalData* globalData, const UString& 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()));
+ if (!length)
+ return globalData->smallStrings.emptyString(globalData);
+ if (length == 1) {
+ UChar c = s[offset];
+ if (c <= maxSingleCharacterString)
+ return globalData->smallStrings.singleCharacterString(globalData, c);
+ }
+ return JSString::createHasOtherOwner(*globalData, StringImpl::create8(s.impl(), offset, length));
+ }
+
+ inline JSString* jsSubstring(JSGlobalData* globalData, const UString& 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()));
+ if (!length)
+ return globalData->smallStrings.emptyString(globalData);
+ if (length == 1) {
+ UChar c = s[offset];
+ if (c <= maxSingleCharacterString)
+ return globalData->smallStrings.singleCharacterString(globalData, c);
+ }
+ return JSString::createHasOtherOwner(*globalData, StringImpl::create(s.impl(), offset, length));
+ }
+
+ inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
+ {
+ int size = s.length();
+ if (!size)
+ return globalData->smallStrings.emptyString(globalData);
+ if (size == 1) {
+ UChar c = s[0];
+ if (c <= maxSingleCharacterString)
+ return globalData->smallStrings.singleCharacterString(globalData, c);
+ }
+ return JSString::createHasOtherOwner(*globalData, s.impl());
+ }
+
+ inline JSRopeString* jsStringBuilder(JSGlobalData* globalData)
+ {
+ return JSRopeString::createNull(*globalData);
}
inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
- inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { return jsSingleCharacterSubstring(&exec->globalData(), s, offset); }
+ inline JSString* jsSubstring8(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->globalData(), s, offset, length); }
inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
if (propertyName == exec->propertyNames().length) {
- slot.setValue(jsNumber(exec, m_value.size()));
+ slot.setValue(jsNumber(m_length));
return true;
}
bool isStrictUInt32;
- unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
- if (isStrictUInt32 && i < static_cast<unsigned>(m_value.size())) {
- slot.setValue(jsSingleCharacterSubstring(exec, m_value, i));
+ unsigned i = propertyName.toUInt32(isStrictUInt32);
+ if (isStrictUInt32 && i < m_length) {
+ slot.setValue(getIndex(exec, i));
return true;
}
ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
{
- if (propertyName < static_cast<unsigned>(m_value.size())) {
- slot.setValue(jsSingleCharacterSubstring(exec, m_value, propertyName));
+ if (propertyName < m_length) {
+ slot.setValue(getIndex(exec, propertyName));
return true;
}
return false;
}
- inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
+ inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; }
+
+ inline bool JSCell::toBoolean(ExecState* exec) const
+ {
+ if (isString())
+ return static_cast<const JSString*>(this)->toBoolean(exec);
+ return !structure()->typeInfo().masqueradesAsUndefined();
+ }
// --- JSValue inlines ----------------------------
+
+ inline bool JSValue::toBoolean(ExecState* exec) const
+ {
+ if (isInt32())
+ return asInt32();
+ if (isDouble())
+ return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
+ if (isCell())
+ return asCell()->toBoolean(exec);
+ return isTrue(); // false, null, and undefined all convert to false.
+ }
- inline JSString* JSValue::toThisJSString(ExecState* exec)
+ inline JSString* JSValue::toString(ExecState* exec) const
{
- return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec));
+ if (isString())
+ return jsCast<JSString*>(asCell());
+ return toStringSlowCase(exec);
+ }
+
+ inline UString JSValue::toUString(ExecState* exec) const
+ {
+ if (isString())
+ return static_cast<JSString*>(asCell())->value(exec);
+ return toUStringSlowCase(exec);
+ }
+
+ ALWAYS_INLINE UString inlineJSValueNotStringtoUString(const JSValue& value, ExecState* exec)
+ {
+ JSGlobalData& globalData = exec->globalData();
+ if (value.isInt32())
+ return globalData.numericStrings.add(value.asInt32());
+ if (value.isDouble())
+ return globalData.numericStrings.add(value.asDouble());
+ if (value.isTrue())
+ return globalData.propertyNames->trueKeyword.ustring();
+ if (value.isFalse())
+ return globalData.propertyNames->falseKeyword.ustring();
+ if (value.isNull())
+ return globalData.propertyNames->nullKeyword.ustring();
+ if (value.isUndefined())
+ return globalData.propertyNames->undefinedKeyword.ustring();
+ return value.toString(exec)->value(exec);
+ }
+
+ ALWAYS_INLINE UString JSValue::toUStringInline(ExecState* exec) const
+ {
+ if (isString())
+ return static_cast<JSString*>(asCell())->value(exec);
+
+ return inlineJSValueNotStringtoUString(*this, exec);
}
} // namespace JSC