diff options
author | Steve Block <steveblock@google.com> | 2010-04-27 16:23:55 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-04-27 17:07:03 +0100 |
commit | 692e5dbf12901edacf14812a6fae25462920af42 (patch) | |
tree | d62802373a429e0a9dc093b6046c166b2c514285 /JavaScriptCore/runtime | |
parent | e24bea4efef1c414137d36a9778aa4e142e10c7d (diff) | |
download | external_webkit-692e5dbf12901edacf14812a6fae25462920af42.zip external_webkit-692e5dbf12901edacf14812a6fae25462920af42.tar.gz external_webkit-692e5dbf12901edacf14812a6fae25462920af42.tar.bz2 |
Merge webkit.org at r55033 : Initial merge by git
Change-Id: I98a4af828067cc243ec3dc5e5826154dd88074b5
Diffstat (limited to 'JavaScriptCore/runtime')
22 files changed, 765 insertions, 649 deletions
diff --git a/JavaScriptCore/runtime/ArrayPrototype.cpp b/JavaScriptCore/runtime/ArrayPrototype.cpp index b64abad..6d79581 100644 --- a/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -201,7 +201,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue if (i) buffer.append(','); if (RefPtr<UString::Rep> rep = strBuffer[i]) - buffer.append(rep->data(), rep->size()); + buffer.append(rep->data(), rep->length()); } ASSERT(buffer.size() == totalSize); return jsString(exec, UString::adopt(buffer)); diff --git a/JavaScriptCore/runtime/Identifier.cpp b/JavaScriptCore/runtime/Identifier.cpp index 97929e2..46fcd0b 100644 --- a/JavaScriptCore/runtime/Identifier.cpp +++ b/JavaScriptCore/runtime/Identifier.cpp @@ -79,7 +79,7 @@ void deleteIdentifierTable(IdentifierTable* table) bool Identifier::equal(const UString::Rep* r, const char* s) { - int length = r->size(); + int length = r->length(); const UChar* d = r->data(); for (int i = 0; i != length; ++i) if (d[i] != (unsigned char)s[i]) @@ -87,12 +87,12 @@ bool Identifier::equal(const UString::Rep* r, const char* s) return s[length] == 0; } -bool Identifier::equal(const UString::Rep* r, const UChar* s, int length) +bool Identifier::equal(const UString::Rep* r, const UChar* s, unsigned length) { - if (r->size() != length) + if (r->length() != length) return false; const UChar* d = r->data(); - for (int i = 0; i != length; ++i) + for (unsigned i = 0; i != length; ++i) if (d[i] != s[i]) return false; return true; @@ -209,7 +209,7 @@ PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const UChar* s, int le PassRefPtr<UString::Rep> Identifier::addSlowCase(JSGlobalData* globalData, UString::Rep* r) { ASSERT(!r->isIdentifier()); - if (r->size() == 1) { + if (r->length() == 1) { UChar c = r->data()[0]; if (c <= 0xFF) r = globalData->smallStrings.singleCharacterStringRep(c); @@ -220,7 +220,7 @@ PassRefPtr<UString::Rep> Identifier::addSlowCase(JSGlobalData* globalData, UStri return r; } } - if (!r->size()) { + if (!r->length()) { UString::Rep::empty().hash(); return &UString::Rep::empty(); } diff --git a/JavaScriptCore/runtime/Identifier.h b/JavaScriptCore/runtime/Identifier.h index 1d1bd18..73e2af8 100644 --- a/JavaScriptCore/runtime/Identifier.h +++ b/JavaScriptCore/runtime/Identifier.h @@ -76,7 +76,7 @@ namespace JSC { static void remove(UString::Rep*); static bool equal(const UString::Rep*, const char*); - static bool equal(const UString::Rep*, const UChar*, int length); + static bool equal(const UString::Rep*, const UChar*, unsigned length); static bool equal(const UString::Rep* a, const UString::Rep* b) { return JSC::equal(a, b); } static PassRefPtr<UString::Rep> add(ExecState*, const char*); // Only to be used with string literals. diff --git a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index 3ddac7c..0e1fbee 100644 --- a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -381,7 +381,7 @@ JSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec, JSObject*, JSValue, cons JSStringBuilder builder; UString str = args.at(0).toString(exec); const UChar* c = str.data(); - for (int k = 0; k < str.size(); k++, c++) { + for (unsigned k = 0; k < str.size(); k++, c++) { int u = c[0]; if (u > 255) { char tmp[7]; diff --git a/JavaScriptCore/runtime/JSONObject.cpp b/JavaScriptCore/runtime/JSONObject.cpp index acd9280..d69a8da 100644 --- a/JavaScriptCore/runtime/JSONObject.cpp +++ b/JavaScriptCore/runtime/JSONObject.cpp @@ -135,7 +135,7 @@ static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value) static inline UString gap(ExecState* exec, JSValue space) { - const int maxGapLength = 10; + const unsigned maxGapLength = 10; space = unwrapBoxedPrimitive(exec, space); // If the space value is a number, create a gap string with that number of spaces. @@ -456,7 +456,7 @@ inline bool Stringifier::willIndent() const inline void Stringifier::indent() { // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent. - int newSize = m_indent.size() + m_gap.size(); + unsigned newSize = m_indent.size() + m_gap.size(); if (newSize > m_repeatedGap.size()) m_repeatedGap = makeString(m_repeatedGap, m_gap); ASSERT(newSize <= m_repeatedGap.size()); diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp index d9500aa..61d3bb1 100644 --- a/JavaScriptCore/runtime/JSObject.cpp +++ b/JavaScriptCore/runtime/JSObject.cpp @@ -516,9 +516,12 @@ void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunct NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) { - if (JSObject* getterFunction = asGetterSetter(*location)->getter()) - slot.setGetterSlot(getterFunction); - else + if (JSObject* getterFunction = asGetterSetter(*location)->getter()) { + if (!structure()->isDictionary()) + slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); + else + slot.setGetterSlot(getterFunction); + } else slot.setUndefined(); } diff --git a/JavaScriptCore/runtime/JSString.cpp b/JavaScriptCore/runtime/JSString.cpp index 1e23a15..a72457e 100644 --- a/JavaScriptCore/runtime/JSString.cpp +++ b/JavaScriptCore/runtime/JSString.cpp @@ -31,48 +31,13 @@ namespace JSC { -void JSString::Rope::destructNonRecursive() -{ - Vector<Rope*, 32> workQueue; - Rope* rope = this; - - while (true) { - unsigned length = rope->ropeLength(); - for (unsigned i = 0; i < length; ++i) { - Fiber& fiber = rope->fibers(i); - if (fiber.isString()) - fiber.string()->deref(); - else { - Rope* nextRope = fiber.rope(); - if (nextRope->hasOneRef()) - workQueue.append(nextRope); - else - nextRope->deref(); - } - } - if (rope != this) - fastFree(rope); - - if (workQueue.isEmpty()) - return; - - rope = workQueue.last(); - workQueue.removeLast(); - } -} - -JSString::Rope::~Rope() -{ - destructNonRecursive(); -} - // Overview: this methods converts a JSString from holding a string in rope form // down to a simple UString representation. It does so by building up the string // backwards, since we want to avoid recursion, we expect that the tree structure // representing the rope is likely imbalanced with more nodes down the left side // (since appending to the string is likely more common) - and as such resolving // in this fashion should minimize work queue size. (If we built the queue forwards -// we would likely have to place all of the constituent UString::Reps into the +// we would likely have to place all of the constituent UStringImpls into the // Vector before performing any concatenation, but by working backwards we likely // only fill the queue with the number of substrings at any given level in a // rope-of-ropes.) @@ -82,39 +47,39 @@ void JSString::resolveRope(ExecState* exec) const // Allocate the buffer to hold the final string, position initially points to the end. UChar* buffer; - if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_stringLength, buffer)) + if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_length, buffer)) m_value = newImpl; else { - for (unsigned i = 0; i < m_ropeLength; ++i) { - m_fibers[i].deref(); - m_fibers[i] = static_cast<void*>(0); + for (unsigned i = 0; i < m_fiberCount; ++i) { + m_other.m_fibers[i]->deref(); + m_other.m_fibers[i] = 0; } - m_ropeLength = 0; + m_fiberCount = 0; ASSERT(!isRope()); ASSERT(m_value == UString()); throwOutOfMemoryError(exec); return; } - UChar* position = buffer + m_stringLength; + UChar* position = buffer + m_length; // Start with the current Rope. Vector<Rope::Fiber, 32> workQueue; Rope::Fiber currentFiber; - for (unsigned i = 0; i < (m_ropeLength - 1); ++i) - workQueue.append(m_fibers[i]); - currentFiber = m_fibers[m_ropeLength - 1]; + for (unsigned i = 0; i < (m_fiberCount - 1); ++i) + workQueue.append(m_other.m_fibers[i]); + currentFiber = m_other.m_fibers[m_fiberCount - 1]; while (true) { - if (currentFiber.isRope()) { - Rope* rope = currentFiber.rope(); + if (currentFiber->isRope()) { + Rope* rope = static_cast<URopeImpl*>(currentFiber); // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber' // (we will be working backwards over the rope). - unsigned ropeLengthMinusOne = rope->ropeLength() - 1; - for (unsigned i = 0; i < ropeLengthMinusOne; ++i) + unsigned fiberCountMinusOne = rope->fiberCount() - 1; + for (unsigned i = 0; i < fiberCountMinusOne; ++i) workQueue.append(rope->fibers(i)); - currentFiber = rope->fibers(ropeLengthMinusOne); + currentFiber = rope->fibers(fiberCountMinusOne); } else { - UString::Rep* string = currentFiber.string(); - unsigned length = string->size(); + UStringImpl* string = static_cast<UStringImpl*>(currentFiber); + unsigned length = string->length(); position -= length; UStringImpl::copyChars(position, string->data(), length); @@ -122,11 +87,11 @@ void JSString::resolveRope(ExecState* exec) const if (workQueue.isEmpty()) { // Create a string from the UChar buffer, clear the rope RefPtr. ASSERT(buffer == position); - for (unsigned i = 0; i < m_ropeLength; ++i) { - m_fibers[i].deref(); - m_fibers[i] = static_cast<void*>(0); + for (unsigned i = 0; i < m_fiberCount; ++i) { + m_other.m_fibers[i]->deref(); + m_other.m_fibers[i] = 0; } - m_ropeLength = 0; + m_fiberCount = 0; ASSERT(!isRope()); return; @@ -153,7 +118,7 @@ bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& resu bool JSString::toBoolean(ExecState*) const { - return m_stringLength; + return m_length; } double JSString::toNumber(ExecState* exec) const @@ -215,13 +180,13 @@ bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyNam bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { if (propertyName == exec->propertyNames().length) { - descriptor.setDescriptor(jsNumber(exec, m_stringLength), DontEnum | DontDelete | ReadOnly); + descriptor.setDescriptor(jsNumber(exec, m_length), DontEnum | DontDelete | ReadOnly); return true; } bool isStrictUInt32; unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < m_stringLength) { + if (isStrictUInt32 && i < m_length) { descriptor.setDescriptor(jsSingleCharacterSubstring(exec, value(exec), i), DontDelete | ReadOnly); return true; } diff --git a/JavaScriptCore/runtime/JSString.h b/JavaScriptCore/runtime/JSString.h index cff8e3a..0162282 100644 --- a/JavaScriptCore/runtime/JSString.h +++ b/JavaScriptCore/runtime/JSString.h @@ -67,109 +67,55 @@ namespace JSC { friend class JIT; friend class JSGlobalData; - // A Rope is a string composed of a set of substrings. - class Rope : public RefCounted<Rope> { - public: - // A Rope is composed from a set of smaller strings called Fibers. - // Each Fiber in a rope is either UString::Rep or another Rope. - class Fiber { - public: - Fiber() : m_value(0) {} - Fiber(UString::Rep* string) : m_value(reinterpret_cast<intptr_t>(string)) {} - Fiber(Rope* rope) : m_value(reinterpret_cast<intptr_t>(rope) | 1) {} - - Fiber(void* nonFiber) : m_value(reinterpret_cast<intptr_t>(nonFiber)) {} - - void deref() - { - if (isRope()) - rope()->deref(); - else - string()->deref(); - } - - Fiber& ref() - { - if (isString()) - string()->ref(); - else - rope()->ref(); - return *this; - } - - unsigned refAndGetLength() - { - if (isString()) { - UString::Rep* rep = string(); - return rep->ref()->size(); - } else { - Rope* r = rope(); - r->ref(); - return r->stringLength(); - } - } - - bool isRope() { return m_value & 1; } - Rope* rope() { return reinterpret_cast<Rope*>(m_value & ~1); } - bool isString() { return !isRope(); } - UString::Rep* string() { return reinterpret_cast<UString::Rep*>(m_value); } - - void* nonFiber() { return reinterpret_cast<void*>(m_value); } - private: - intptr_t m_value; - }; + typedef URopeImpl Rope; - // Creates a Rope comprising of 'ropeLength' Fibers. - // The Rope is constructed in an uninitialized state - initialize must be called for each Fiber in the Rope. - static PassRefPtr<Rope> createOrNull(unsigned ropeLength) + class RopeBuilder { + public: + RopeBuilder(unsigned fiberCount) + : m_index(0) + , m_rope(Rope::tryCreateUninitialized(fiberCount)) { - void* allocation; - if (tryFastMalloc(sizeof(Rope) + (ropeLength - 1) * sizeof(Fiber)).getValue(allocation)) - return adoptRef(new (allocation) Rope(ropeLength)); - return 0; } - ~Rope(); - void destructNonRecursive(); + bool isOutOfMemory() { return !m_rope; } - void append(unsigned &index, Fiber& fiber) + void append(Rope::Fiber& fiber) { - m_fibers[index++] = fiber; - m_stringLength += fiber.refAndGetLength(); + ASSERT(m_rope); + m_rope->initializeFiber(m_index, fiber); } - void append(unsigned &index, const UString& string) + void append(const UString& string) { - UString::Rep* rep = string.rep(); - m_fibers[index++] = Fiber(rep); - m_stringLength += rep->ref()->size(); + ASSERT(m_rope); + m_rope->initializeFiber(m_index, string.rep()); } - void append(unsigned& index, JSString* jsString) + void append(JSString* jsString) { if (jsString->isRope()) { - for (unsigned i = 0; i < jsString->m_ropeLength; ++i) - append(index, jsString->m_fibers[i]); + for (unsigned i = 0; i < jsString->m_fiberCount; ++i) + append(jsString->m_other.m_fibers[i]); } else - append(index, jsString->string()); + append(jsString->string()); + } + + PassRefPtr<Rope> release() + { + ASSERT(m_index == m_rope->fiberCount()); + return m_rope.release(); } - unsigned ropeLength() { return m_ropeLength; } - unsigned stringLength() { return m_stringLength; } - Fiber& fibers(unsigned index) { return m_fibers[index]; } + unsigned length() { return m_rope->length(); } private: - Rope(unsigned ropeLength) : m_ropeLength(ropeLength), m_stringLength(0) {} - void* operator new(size_t, void* inPlace) { return inPlace; } - - unsigned m_ropeLength; - unsigned m_stringLength; - Fiber m_fibers[1]; + unsigned m_index; + RefPtr<Rope> m_rope; }; ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value.size()) + , m_length(value.size()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { Heap::heap(this)->reportExtraMemoryCost(value.cost()); } @@ -177,72 +123,72 @@ namespace JSC { enum HasOtherOwnerType { HasOtherOwner }; JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value.size()) + , m_length(value.size()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { } JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value->size()) + , m_length(value->length()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { } - JSString(JSGlobalData* globalData, PassRefPtr<JSString::Rope> rope) + JSString(JSGlobalData* globalData, PassRefPtr<Rope> rope) : JSCell(globalData->stringStructure.get()) - , m_stringLength(rope->stringLength()) - , m_ropeLength(1) + , m_length(rope->length()) + , m_fiberCount(1) { - m_fibers[0] = rope.releaseRef(); + m_other.m_fibers[0] = rope.releaseRef(); } // This constructor constructs a new string by concatenating s1 & s2. - // This should only be called with ropeLength <= 3. - JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, JSString* s2) + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2) : JSCell(globalData->stringStructure.get()) - , m_stringLength(s1->length() + s2->length()) - , m_ropeLength(ropeLength) + , m_length(s1->length() + s2->length()) + , m_fiberCount(fiberCount) { - ASSERT(ropeLength <= s_maxInternalRopeLength); + ASSERT(fiberCount <= s_maxInternalRopeLength); unsigned index = 0; appendStringInConstruct(index, s1); appendStringInConstruct(index, s2); - ASSERT(ropeLength == index); + ASSERT(fiberCount == index); } // This constructor constructs a new string by concatenating s1 & s2. - // This should only be called with ropeLength <= 3. - JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, const UString& u2) + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2) : JSCell(globalData->stringStructure.get()) - , m_stringLength(s1->length() + u2.size()) - , m_ropeLength(ropeLength) + , m_length(s1->length() + u2.size()) + , m_fiberCount(fiberCount) { - ASSERT(ropeLength <= s_maxInternalRopeLength); + ASSERT(fiberCount <= s_maxInternalRopeLength); unsigned index = 0; appendStringInConstruct(index, s1); appendStringInConstruct(index, u2); - ASSERT(ropeLength == index); + ASSERT(fiberCount == index); } // This constructor constructs a new string by concatenating s1 & s2. - // This should only be called with ropeLength <= 3. - JSString(JSGlobalData* globalData, unsigned ropeLength, const UString& u1, JSString* s2) + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2) : JSCell(globalData->stringStructure.get()) - , m_stringLength(u1.size() + s2->length()) - , m_ropeLength(ropeLength) + , m_length(u1.size() + s2->length()) + , m_fiberCount(fiberCount) { - ASSERT(ropeLength <= s_maxInternalRopeLength); + ASSERT(fiberCount <= s_maxInternalRopeLength); unsigned index = 0; appendStringInConstruct(index, u1); appendStringInConstruct(index, s2); - ASSERT(ropeLength == index); + ASSERT(fiberCount == index); } // This constructor constructs a new string by concatenating v1, v2 & v3. - // This should only be called with ropeLength <= 3 ... which since every - // value must require a ropeLength of at least one implies that the length + // This should only be called with fiberCount <= 3 ... which since every + // value must require a fiberCount of at least one implies that the length // for each value must be exactly 1! JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3) : JSCell(exec->globalData().stringStructure.get()) - , m_stringLength(0) - , m_ropeLength(s_maxInternalRopeLength) + , m_length(0) + , m_fiberCount(s_maxInternalRopeLength) { unsigned index = 0; appendValueInConstructAndIncrementLength(exec, index, v1); @@ -253,26 +199,24 @@ namespace JSC { JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value.size()) + , m_length(value.size()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { // nasty hack because we can't union non-POD types - m_fibers[0] = reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(finalizer)); - m_fibers[1] = context; + m_other.m_finalizerCallback = finalizer; + m_other.m_finalizerContext = context; Heap::heap(this)->reportExtraMemoryCost(value.cost()); } ~JSString() { ASSERT(vptr() == JSGlobalData::jsStringVPtr); - for (unsigned i = 0; i < m_ropeLength; ++i) - m_fibers[i].deref(); + for (unsigned i = 0; i < m_fiberCount; ++i) + m_other.m_fibers[i]->deref(); - if (!m_ropeLength && m_fibers[0].nonFiber()) { - JSStringFinalizerCallback finalizer = reinterpret_cast<JSStringFinalizerCallback>(m_fibers[0].nonFiber()); - finalizer(this, m_fibers[1].nonFiber()); - } + if (!m_fiberCount && m_other.m_finalizerCallback) + m_other.m_finalizerCallback(this, m_other.m_finalizerContext); } const UString& value(ExecState* exec) const @@ -288,13 +232,13 @@ namespace JSC { ASSERT(isRope() == m_value.isNull()); return m_value; } - unsigned length() { return m_stringLength; } + unsigned length() { return m_length; } 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_stringLength; } + bool canGetIndex(unsigned i) { return i < m_length; } JSString* getIndex(ExecState*, unsigned); static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); } @@ -303,7 +247,7 @@ namespace JSC { enum VPtrStealingHackType { VPtrStealingHack }; JSString(VPtrStealingHackType) : JSCell(0) - , m_ropeLength(0) + , m_fiberCount(0) { } @@ -311,14 +255,19 @@ namespace JSC { void appendStringInConstruct(unsigned& index, const UString& string) { - m_fibers[index++] = Rope::Fiber(string.rep()->ref()); + UStringImpl* impl = string.rep(); + impl->ref(); + m_other.m_fibers[index++] = impl; } void appendStringInConstruct(unsigned& index, JSString* jsString) { if (jsString->isRope()) { - for (unsigned i = 0; i < jsString->m_ropeLength; ++i) - m_fibers[index++] = jsString->m_fibers[i].ref(); + for (unsigned i = 0; i < jsString->m_fiberCount; ++i) { + Rope::Fiber fiber = jsString->m_other.m_fibers[i]; + fiber->ref(); + m_other.m_fibers[index++] = fiber; + } } else appendStringInConstruct(index, jsString->string()); } @@ -328,13 +277,15 @@ namespace JSC { if (v.isString()) { ASSERT(asCell(v)->isString()); JSString* s = static_cast<JSString*>(asCell(v)); - ASSERT(s->ropeLength() == 1); + ASSERT(s->fiberCount() == 1); appendStringInConstruct(index, s); - m_stringLength += s->length(); + m_length += s->length(); } else { UString u(v.toString(exec)); - m_fibers[index++] = Rope::Fiber(u.rep()->ref()); - m_stringLength += u.size(); + UStringImpl* impl = u.rep(); + impl->ref(); + m_other.m_fibers[index++] = impl; + m_length += u.size(); } } @@ -357,14 +308,24 @@ namespace JSC { static const unsigned s_maxInternalRopeLength = 3; // A string is represented either by a UString or a Rope. - unsigned m_stringLength; + unsigned m_length; mutable UString m_value; - mutable unsigned m_ropeLength; - mutable Rope::Fiber m_fibers[s_maxInternalRopeLength]; + mutable unsigned m_fiberCount; + // This structure exists to support a temporary workaround for a GC issue. + struct JSStringFinalizerStruct { + JSStringFinalizerStruct() : m_finalizerCallback(0) {} + union { + mutable Rope::Fiber m_fibers[s_maxInternalRopeLength]; + struct { + JSStringFinalizerCallback m_finalizerCallback; + void* m_finalizerContext; + }; + }; + } m_other; - bool isRope() const { return m_ropeLength; } + bool isRope() const { return m_fiberCount; } UString& string() { ASSERT(!isRope()); return m_value; } - unsigned ropeLength() { return m_ropeLength ? m_ropeLength : 1; } + unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 1; } friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2); friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2); @@ -493,13 +454,13 @@ namespace JSC { ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { if (propertyName == exec->propertyNames().length) { - slot.setValue(jsNumber(exec, m_stringLength)); + slot.setValue(jsNumber(exec, m_length)); return true; } bool isStrictUInt32; unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < m_stringLength) { + if (isStrictUInt32 && i < m_length) { slot.setValue(jsSingleCharacterSubstring(exec, value(exec), i)); return true; } @@ -509,7 +470,7 @@ namespace JSC { ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { - if (propertyName < m_stringLength) { + if (propertyName < m_length) { slot.setValue(jsSingleCharacterSubstring(exec, value(exec), propertyName)); return true; } diff --git a/JavaScriptCore/runtime/NumberPrototype.cpp b/JavaScriptCore/runtime/NumberPrototype.cpp index fa32b86..5680eb1 100644 --- a/JavaScriptCore/runtime/NumberPrototype.cpp +++ b/JavaScriptCore/runtime/NumberPrototype.cpp @@ -265,11 +265,11 @@ JSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState* exec, JSObject*, JSValue z.append(m); m = z.build(); k = f + 1; - ASSERT(k == m.size()); + ASSERT(k == static_cast<int>(m.size())); } int kMinusf = k - f; - if (kMinusf < m.size()) + if (kMinusf < static_cast<int>(m.size())) return jsString(exec, makeString(s, m.substr(0, kMinusf), ".", m.substr(kMinusf))); return jsString(exec, makeString(s, m.substr(0, kMinusf))); } @@ -444,7 +444,7 @@ JSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec, JSObject*, JSV if (e == precision - 1) return jsString(exec, makeString(s, m)); if (e >= 0) { - if (e + 1 < m.size()) + if (e + 1 < static_cast<int>(m.size())) return jsString(exec, makeString(s, m.substr(0, e + 1), ".", m.substr(e + 1))); return jsString(exec, makeString(s, m)); } diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h index 9b27074..cc0d603 100644 --- a/JavaScriptCore/runtime/Operations.h +++ b/JavaScriptCore/runtime/Operations.h @@ -37,132 +37,167 @@ namespace JSC { ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) { - if (!s1->length()) + unsigned length1 = s1->length(); + if (!length1) return s2; - if (!s2->length()) + unsigned length2 = s2->length(); + if (!length2) return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); - unsigned ropeLength = s1->ropeLength() + s2->ropeLength(); + unsigned fiberCount = s1->fiberCount() + s2->fiberCount(); JSGlobalData* globalData = &exec->globalData(); - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, s1, s2); + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, s1, s2); - unsigned index = 0; - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - rope->append(index, s1); - rope->append(index, s2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + ropeBuilder.append(s1); + ropeBuilder.append(s2); + return new (globalData) JSString(globalData, ropeBuilder.release()); } ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2) { - unsigned ropeLength = 1 + s2->ropeLength(); + unsigned length1 = u1.size(); + if (!length1) + return s2; + unsigned length2 = s2->length(); + if (!length2) + return jsString(exec, u1); + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + unsigned fiberCount = 1 + s2->fiberCount(); JSGlobalData* globalData = &exec->globalData(); - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, u1, s2); + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, u1, s2); - unsigned index = 0; - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - rope->append(index, u1); - rope->append(index, s2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + ropeBuilder.append(u1); + ropeBuilder.append(s2); + return new (globalData) JSString(globalData, ropeBuilder.release()); } ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2) { - unsigned ropeLength = s1->ropeLength() + 1; + unsigned length1 = s1->length(); + if (!length1) + return jsString(exec, u2); + unsigned length2 = u2.size(); + if (!length2) + return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + unsigned fiberCount = s1->fiberCount() + 1; JSGlobalData* globalData = &exec->globalData(); - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, s1, u2); + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, s1, u2); - unsigned index = 0; - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - rope->append(index, s1); - rope->append(index, u2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + ropeBuilder.append(s1); + ropeBuilder.append(u2); + return new (globalData) JSString(globalData, ropeBuilder.release()); } ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) { ASSERT(count >= 3); - unsigned ropeLength = 0; + unsigned fiberCount = 0; for (unsigned i = 0; i < count; ++i) { JSValue v = strings[i].jsValue(); if (LIKELY(v.isString())) - ropeLength += asString(v)->ropeLength(); + fiberCount += asString(v)->fiberCount(); else - ++ropeLength; + ++fiberCount; } JSGlobalData* globalData = &exec->globalData(); - if (ropeLength == 3) + if (fiberCount == 3) return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue()); - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - unsigned index = 0; + unsigned length = 0; + bool overflow = false; + for (unsigned i = 0; i < count; ++i) { JSValue v = strings[i].jsValue(); if (LIKELY(v.isString())) - rope->append(index, asString(v)); + ropeBuilder.append(asString(v)); else - rope->append(index, v.toString(exec)); + ropeBuilder.append(v.toString(exec)); + + unsigned newLength = ropeBuilder.length(); + if (newLength < length) + overflow = true; + length = newLength; } - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + if (overflow) + return throwOutOfMemoryError(exec); + + return new (globalData) JSString(globalData, ropeBuilder.release()); } ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args) { - unsigned ropeLength = 0; + unsigned fiberCount = 0; if (LIKELY(thisValue.isString())) - ropeLength += asString(thisValue)->ropeLength(); + fiberCount += asString(thisValue)->fiberCount(); else - ++ropeLength; + ++fiberCount; for (unsigned i = 0; i < args.size(); ++i) { JSValue v = args.at(i); if (LIKELY(v.isString())) - ropeLength += asString(v)->ropeLength(); + fiberCount += asString(v)->fiberCount(); else - ++ropeLength; + ++fiberCount; } - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - unsigned index = 0; if (LIKELY(thisValue.isString())) - rope->append(index, asString(thisValue)); + ropeBuilder.append(asString(thisValue)); else - rope->append(index, thisValue.toString(exec)); + ropeBuilder.append(thisValue.toString(exec)); + + unsigned length = 0; + bool overflow = false; + for (unsigned i = 0; i < args.size(); ++i) { JSValue v = args.at(i); if (LIKELY(v.isString())) - rope->append(index, asString(v)); + ropeBuilder.append(asString(v)); else - rope->append(index, v.toString(exec)); + ropeBuilder.append(v.toString(exec)); + + unsigned newLength = ropeBuilder.length(); + if (newLength < length) + overflow = true; + length = newLength; } - ASSERT(index == ropeLength); + + if (overflow) + return throwOutOfMemoryError(exec); JSGlobalData* globalData = &exec->globalData(); - return new (globalData) JSString(globalData, rope.release()); + return new (globalData) JSString(globalData, ropeBuilder.release()); } // ECMA 11.9.3 diff --git a/JavaScriptCore/runtime/PropertySlot.cpp b/JavaScriptCore/runtime/PropertySlot.cpp index a0a2f48..8b6ceb9 100644 --- a/JavaScriptCore/runtime/PropertySlot.cpp +++ b/JavaScriptCore/runtime/PropertySlot.cpp @@ -35,10 +35,10 @@ JSValue PropertySlot::functionGetter(ExecState* exec, const Identifier&, const P CallData callData; CallType callType = slot.m_data.getterFunc->getCallData(callData); if (callType == CallTypeHost) - return callData.native.function(exec, slot.m_data.getterFunc, slot.slotBase(), exec->emptyList()); + return callData.native.function(exec, slot.m_data.getterFunc, slot.thisValue(), exec->emptyList()); ASSERT(callType == CallTypeJS); // FIXME: Can this be done more efficiently using the callData? - return asFunction(slot.m_data.getterFunc)->call(exec, slot.slotBase(), exec->emptyList()); + return asFunction(slot.m_data.getterFunc)->call(exec, slot.thisValue(), exec->emptyList()); } } // namespace JSC diff --git a/JavaScriptCore/runtime/PropertySlot.h b/JavaScriptCore/runtime/PropertySlot.h index 15d9034..a364e42 100644 --- a/JavaScriptCore/runtime/PropertySlot.h +++ b/JavaScriptCore/runtime/PropertySlot.h @@ -71,7 +71,9 @@ namespace JSC { return m_getValue(exec, Identifier::from(exec, propertyName), *this); } - bool isCacheable() const { return m_offset != WTF::notFound; } + bool isGetter() const { return m_isGetter; } + bool isCacheable() const { return m_isCacheable; } + bool isCacheableValue() const { return m_isCacheable && !m_isGetter; } size_t cachedOffset() const { ASSERT(isCacheable()); @@ -102,6 +104,8 @@ namespace JSC { m_slotBase = slotBase; m_data.valueSlot = valueSlot; m_offset = offset; + m_isCacheable = true; + m_isGetter = false; } void setValue(JSValue value) @@ -139,14 +143,28 @@ namespace JSC { m_slotBase = slotBase; m_data.index = index; } - + void setGetterSlot(JSObject* getterFunc) { ASSERT(getterFunc); + m_thisValue = m_slotBase; m_getValue = functionGetter; m_data.getterFunc = getterFunc; + m_isGetter = true; } - + + void setCacheableGetterSlot(JSValue slotBase, JSObject* getterFunc, unsigned offset) + { + ASSERT(getterFunc); + m_getValue = functionGetter; + m_thisValue = m_slotBase; + m_slotBase = slotBase; + m_data.getterFunc = getterFunc; + m_offset = offset; + m_isCacheable = true; + m_isGetter = true; + } + void setUndefined() { setValue(jsUndefined()); @@ -182,11 +200,14 @@ namespace JSC { { // Clear offset even in release builds, in case this PropertySlot has been used before. // (For other data members, we don't need to clear anything because reuse would meaningfully overwrite them.) - m_offset = WTF::notFound; + m_offset = 0; + m_isCacheable = false; + m_isGetter = false; } unsigned index() const { return m_data.index; } + JSValue thisValue() const { return m_thisValue; } private: static JSValue functionGetter(ExecState*, const Identifier&, const PropertySlot&); @@ -201,8 +222,11 @@ namespace JSC { } m_data; JSValue m_value; + JSValue m_thisValue; size_t m_offset; + bool m_isCacheable : 1; + bool m_isGetter : 1; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/RegExp.cpp b/JavaScriptCore/runtime/RegExp.cpp index 4e958f4..85d41ee 100644 --- a/JavaScriptCore/runtime/RegExp.cpp +++ b/JavaScriptCore/runtime/RegExp.cpp @@ -71,11 +71,11 @@ inline RegExp::RegExp(JSGlobalData* globalData, const UString& pattern, const US { // NOTE: The global flag is handled on a case-by-case basis by functions like // String::match and RegExpObject::match. - if (flags.find('g') != -1) + if (flags.find('g') != UString::NotFound) m_flagBits |= Global; - if (flags.find('i') != -1) + if (flags.find('i') != UString::NotFound) m_flagBits |= IgnoreCase; - if (flags.find('m') != -1) + if (flags.find('m') != UString::NotFound) m_flagBits |= Multiline; compile(globalData); @@ -117,7 +117,7 @@ int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) if (ovector) ovector->clear(); - if (startOffset > s.size() || s.isNull()) + if (static_cast<unsigned>(startOffset) > s.size() || s.isNull()) return -1; #if ENABLE(YARR_JIT) @@ -188,7 +188,7 @@ int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) if (ovector) ovector->clear(); - if (startOffset > s.size() || s.isNull()) + if (static_cast<unsigned>(startOffset) > s.size() || s.isNull()) return -1; #if ENABLE(WREC) diff --git a/JavaScriptCore/runtime/SmallStrings.cpp b/JavaScriptCore/runtime/SmallStrings.cpp index d9d4377..78bd4e4 100644 --- a/JavaScriptCore/runtime/SmallStrings.cpp +++ b/JavaScriptCore/runtime/SmallStrings.cpp @@ -83,7 +83,7 @@ void SmallStrings::markChildren(MarkStack& markStack) bool isAnyStringMarked = isMarked(m_emptyString); for (unsigned i = 0; i < numCharactersToStore && !isAnyStringMarked; ++i) - isAnyStringMarked |= isMarked(m_singleCharacterStrings[i]); + isAnyStringMarked = isMarked(m_singleCharacterStrings[i]); if (!isAnyStringMarked) { clear(); diff --git a/JavaScriptCore/runtime/StringPrototype.cpp b/JavaScriptCore/runtime/StringPrototype.cpp index 8c014ec..bef4083 100644 --- a/JavaScriptCore/runtime/StringPrototype.cpp +++ b/JavaScriptCore/runtime/StringPrototype.cpp @@ -150,7 +150,7 @@ bool StringPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier // ------------------------------ Functions -------------------------- -static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, int i) +static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, unsigned i) { Vector<UChar> substitutedReplacement; int offset = 0; @@ -206,7 +206,7 @@ static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacem i += 1 + advance; offset = i + 1; substitutedReplacement.append(source.data() + backrefStart, backrefLength); - } while ((i = replacement.find('$', i + 1)) != -1); + } while ((i = replacement.find('$', i + 1)) != UString::NotFound); if (replacement.size() - offset) substitutedReplacement.append(replacement.data() + offset, replacement.size() - offset); @@ -217,8 +217,8 @@ static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacem static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg) { - int i = replacement.find('$', 0); - if (UNLIKELY(i != -1)) + unsigned i = replacement.find('$', 0); + if (UNLIKELY(i != UString::NotFound)) return substituteBackreferencesSlow(replacement, source, ovector, reg, i); return replacement; } @@ -329,7 +329,7 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int lastIndex = 0; - int startPosition = 0; + unsigned startPosition = 0; Vector<StringRange, 16> sourceRanges; Vector<UString, 16> replacements; @@ -432,7 +432,7 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue if (!lastIndex && replacements.isEmpty()) return sourceVal; - if (lastIndex < source.size()) + if (static_cast<unsigned>(lastIndex) < source.size()) sourceRanges.append(StringRange(lastIndex, source.size() - lastIndex)); return jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()); @@ -441,9 +441,9 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue // Not a regular expression, so treat the pattern as a string. UString patternString = pattern.toString(exec); - int matchPos = source.find(patternString); + unsigned matchPos = source.find(patternString); - if (matchPos == -1) + if (matchPos == UString::NotFound) return sourceVal; int matchLen = patternString.size(); @@ -541,7 +541,10 @@ JSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue pos = static_cast<int>(dpos); } - return jsNumber(exec, s.find(u2, pos)); + unsigned result = s.find(u2, pos); + if (result == UString::NotFound) + return jsNumber(exec, -1); + return jsNumber(exec, result); } JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) @@ -563,7 +566,11 @@ JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSV else if (isnan(dpos)) dpos = len; #endif - return jsNumber(exec, s.rfind(u2, static_cast<int>(dpos))); + + unsigned result = s.rfind(u2, static_cast<unsigned>(dpos)); + if (result == UString::NotFound) + return jsNumber(exec, -1); + return jsNumber(exec, result); } JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) @@ -675,7 +682,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue t JSArray* result = constructEmptyArray(exec); unsigned i = 0; - int p0 = 0; + unsigned p0 = 0; unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec); if (a0.inherits(&RegExpObject::info)) { RegExp* reg = asRegExpObject(a0)->regExp(); @@ -683,7 +690,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue t // empty string matched by regexp -> empty array return result; } - int pos = 0; + unsigned pos = 0; while (i != limit && pos < s.size()) { Vector<int, 32> ovector; int mpos = reg->match(s, pos, &ovector); @@ -691,7 +698,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue t break; int mlen = ovector[1] - ovector[0]; pos = mpos + (mlen == 0 ? 1 : mlen); - if (mpos != p0 || mlen) { + if (static_cast<unsigned>(mpos) != p0 || mlen) { result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0)); p0 = mpos + mlen; } @@ -713,8 +720,9 @@ JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue t while (i != limit && p0 < s.size() - 1) result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++)); } else { - int pos; - while (i != limit && (pos = s.find(u2, p0)) >= 0) { + unsigned pos; + + while (i != limit && (pos = s.find(u2, p0)) != UString::NotFound) { result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0)); p0 = pos + u2.size(); } @@ -1022,12 +1030,12 @@ static inline bool isTrimWhitespace(UChar c) static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind) { UString str = thisValue.toThisString(exec); - int left = 0; + unsigned left = 0; if (trimKind & TrimLeft) { while (left < str.size() && isTrimWhitespace(str[left])) left++; } - int right = str.size(); + unsigned right = str.size(); if (trimKind & TrimRight) { while (right > left && isTrimWhitespace(str[right - 1])) right--; diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp index 546e2bf..ebf8a4c 100644 --- a/JavaScriptCore/runtime/Structure.cpp +++ b/JavaScriptCore/runtime/Structure.cpp @@ -79,6 +79,106 @@ static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); static int comparePropertyMapEntryIndices(const void* a, const void* b); +inline void Structure::setTransitionTable(TransitionTable* table) +{ + ASSERT(m_isUsingSingleSlot); +#ifndef NDEBUG + setSingleTransition(0); +#endif + m_isUsingSingleSlot = false; + m_transitions.m_table = table; + // This implicitly clears the flag that indicates we're using a single transition + ASSERT(!m_isUsingSingleSlot); +} + +// The contains and get methods accept imprecise matches, so if an unspecialised transition exists +// for the given key they will consider that transition to be a match. If a specialised transition +// exists and it matches the provided specificValue, get will return the specific transition. +inline bool Structure::transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + Structure* existingTransition = singleTransition(); + return existingTransition && existingTransition->m_nameInPrevious.get() == key.first + && existingTransition->m_attributesInPrevious == key.second + && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0); + } + TransitionTable::iterator find = transitionTable()->find(key); + if (find == transitionTable()->end()) + return false; + + return find->second.first || find->second.second->transitionedFor(specificValue); +} + +inline Structure* Structure::transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const +{ + if (m_isUsingSingleSlot) { + Structure* existingTransition = singleTransition(); + if (existingTransition && existingTransition->m_nameInPrevious.get() == key.first + && existingTransition->m_attributesInPrevious == key.second + && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) + return existingTransition; + return 0; + } + + Transition transition = transitionTable()->get(key); + if (transition.second && transition.second->transitionedFor(specificValue)) + return transition.second; + return transition.first; +} + +inline bool Structure::transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const +{ + if (m_isUsingSingleSlot) { + Structure* transition = singleTransition(); + return transition && transition->m_nameInPrevious == key.first + && transition->m_attributesInPrevious == key.second; + } + return transitionTable()->contains(key); +} + +inline void Structure::transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + ASSERT(transitionTableContains(key, specificValue)); + setSingleTransition(0); + return; + } + TransitionTable::iterator find = transitionTable()->find(key); + if (!specificValue) + find->second.first = 0; + else + find->second.second = 0; + if (!find->second.first && !find->second.second) + transitionTable()->remove(find); +} + +inline void Structure::transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + if (!singleTransition()) { + setSingleTransition(structure); + return; + } + Structure* existingTransition = singleTransition(); + TransitionTable* transitionTable = new TransitionTable; + setTransitionTable(transitionTable); + if (existingTransition) + transitionTableAdd(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); + } + if (!specificValue) { + TransitionTable::iterator find = transitionTable()->find(key); + if (find == transitionTable()->end()) + transitionTable()->add(key, Transition(structure, 0)); + else + find->second.first = structure; + } else { + // If we're adding a transition to a specific value, then there cannot be + // an existing transition + ASSERT(!transitionTable()->contains(key)); + transitionTable()->add(key, Transition(0, structure)); + } +} + void Structure::dumpStatistics() { #if DUMP_STRUCTURE_ID_STATISTICS @@ -136,7 +236,10 @@ Structure::Structure(JSValue prototype, const TypeInfo& typeInfo, unsigned anony , m_attributesInPrevious(0) , m_specificFunctionThrashCount(0) , m_anonymousSlotCount(anonymousSlotCount) + , m_isUsingSingleSlot(true) { + m_transitions.m_singleTransition = 0; + ASSERT(m_prototype); ASSERT(m_prototype.isObject() || m_prototype.isNull()); @@ -159,7 +262,7 @@ Structure::~Structure() { if (m_previous) { ASSERT(m_nameInPrevious); - m_previous->table.remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); + m_previous->transitionTableRemove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); } @@ -177,6 +280,9 @@ Structure::~Structure() fastFree(m_propertyTable); } + if (!m_isUsingSingleSlot) + delete transitionTable(); + #ifndef NDEBUG #if ENABLE(JSC_MULTIPLE_THREADS) MutexLocker protect(ignoreSetMutex); @@ -340,7 +446,7 @@ PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Struct ASSERT(!structure->isDictionary()); ASSERT(structure->typeInfo().type() == ObjectType); - if (Structure* existingTransition = structure->table.get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) { + if (Structure* existingTransition = structure->transitionTableGet(make_pair(propertyName.ustring().rep(), attributes), specificValue)) { ASSERT(existingTransition->m_offset != noOffset); offset = existingTransition->m_offset + existingTransition->m_anonymousSlotCount; ASSERT(offset >= structure->m_anonymousSlotCount); @@ -405,7 +511,7 @@ PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, con transition->m_offset = offset - structure->m_anonymousSlotCount; ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); - structure->table.add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue); + structure->transitionTableAdd(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue); return transition.release(); } @@ -852,7 +958,7 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCel bool Structure::hasTransition(UString::Rep* rep, unsigned attributes) { - return table.hasTransition(make_pair(rep, attributes)); + return transitionTableHasTransition(make_pair(rep, attributes)); } size_t Structure::remove(const Identifier& propertyName) diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h index 95cf94c..968443a 100644 --- a/JavaScriptCore/runtime/Structure.h +++ b/JavaScriptCore/runtime/Structure.h @@ -179,6 +179,20 @@ namespace JSC { // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both. return m_offset == noOffset ? 0 : m_offset + 1; } + + typedef std::pair<Structure*, Structure*> Transition; + typedef HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> TransitionTable; + + inline bool transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue); + inline void transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue); + inline void transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue); + inline bool transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const; + inline Structure* transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const; + + TransitionTable* transitionTable() const { ASSERT(!m_isUsingSingleSlot); return m_transitions.m_table; } + inline void setTransitionTable(TransitionTable* table); + Structure* singleTransition() const { ASSERT(m_isUsingSingleSlot); return m_transitions.m_singleTransition; } + void setSingleTransition(Structure* structure) { ASSERT(m_isUsingSingleSlot); m_transitions.m_singleTransition = structure; } bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; @@ -199,7 +213,11 @@ namespace JSC { RefPtr<UString::Rep> m_nameInPrevious; JSCell* m_specificValueInPrevious; - StructureTransitionTable table; + // 'm_isUsingSingleSlot' indicates whether we are using the single transition optimisation. + union { + TransitionTable* m_table; + Structure* m_singleTransition; + } m_transitions; WeakGCPtr<JSPropertyNameIterator> m_enumerationCache; @@ -224,7 +242,8 @@ namespace JSC { #endif unsigned m_specificFunctionThrashCount : 2; unsigned m_anonymousSlotCount : 5; - // 5 free bits + unsigned m_isUsingSingleSlot : 1; + // 4 free bits }; inline size_t Structure::get(const Identifier& propertyName) @@ -271,58 +290,7 @@ namespace JSC { return m_propertyTable->entries()[entryIndex - 1].offset; } } - - bool StructureTransitionTable::contains(const StructureTransitionTableHash::Key& key, JSCell* specificValue) - { - if (usingSingleTransitionSlot()) { - Structure* existingTransition = singleTransition(); - return existingTransition && existingTransition->m_nameInPrevious.get() == key.first - && existingTransition->m_attributesInPrevious == key.second - && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0); - } - TransitionTable::iterator find = table()->find(key); - if (find == table()->end()) - return false; - - return find->second.first || find->second.second->transitionedFor(specificValue); - } - Structure* StructureTransitionTable::get(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const - { - if (usingSingleTransitionSlot()) { - Structure* existingTransition = singleTransition(); - if (existingTransition && existingTransition->m_nameInPrevious.get() == key.first - && existingTransition->m_attributesInPrevious == key.second - && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) - return existingTransition; - return 0; - } - - Transition transition = table()->get(key); - if (transition.second && transition.second->transitionedFor(specificValue)) - return transition.second; - return transition.first; - } - - bool StructureTransitionTable::hasTransition(const StructureTransitionTableHash::Key& key) const - { - if (usingSingleTransitionSlot()) { - Structure* transition = singleTransition(); - return transition && transition->m_nameInPrevious == key.first - && transition->m_attributesInPrevious == key.second; - } - return table()->contains(key); - } - - void StructureTransitionTable::reifySingleTransition() - { - ASSERT(usingSingleTransitionSlot()); - Structure* existingTransition = singleTransition(); - TransitionTable* transitionTable = new TransitionTable; - setTransitionTable(transitionTable); - if (existingTransition) - add(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); - } } // namespace JSC #endif // Structure_h diff --git a/JavaScriptCore/runtime/StructureTransitionTable.h b/JavaScriptCore/runtime/StructureTransitionTable.h index 320dbdd..d1dc2d9 100644 --- a/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/JavaScriptCore/runtime/StructureTransitionTable.h @@ -30,7 +30,6 @@ #include <wtf/HashFunctions.h> #include <wtf/HashMap.h> #include <wtf/HashTraits.h> -#include <wtf/PtrAndFlags.h> #include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> @@ -67,99 +66,6 @@ namespace JSC { static bool isDeletedValue(const TraitType& value) { return FirstTraits::isDeletedValue(value.first); } }; - class StructureTransitionTable { - typedef std::pair<Structure*, Structure*> Transition; - typedef HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> TransitionTable; - public: - StructureTransitionTable() { - m_transitions.m_singleTransition.set(0); - m_transitions.m_singleTransition.setFlag(usingSingleSlot); - } - - ~StructureTransitionTable() { - if (!usingSingleTransitionSlot()) - delete table(); - } - - // The contains and get methods accept imprecise matches, so if an unspecialised transition exists - // for the given key they will consider that transition to be a match. If a specialised transition - // exists and it matches the provided specificValue, get will return the specific transition. - inline bool contains(const StructureTransitionTableHash::Key&, JSCell* specificValue); - inline Structure* get(const StructureTransitionTableHash::Key&, JSCell* specificValue) const; - inline bool hasTransition(const StructureTransitionTableHash::Key& key) const; - void remove(const StructureTransitionTableHash::Key& key, JSCell* specificValue) - { - if (usingSingleTransitionSlot()) { - ASSERT(contains(key, specificValue)); - setSingleTransition(0); - return; - } - TransitionTable::iterator find = table()->find(key); - if (!specificValue) - find->second.first = 0; - else - find->second.second = 0; - if (!find->second.first && !find->second.second) - table()->remove(find); - } - void add(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue) - { - if (usingSingleTransitionSlot()) { - if (!singleTransition()) { - setSingleTransition(structure); - return; - } - reifySingleTransition(); - } - if (!specificValue) { - TransitionTable::iterator find = table()->find(key); - if (find == table()->end()) - table()->add(key, Transition(structure, 0)); - else - find->second.first = structure; - } else { - // If we're adding a transition to a specific value, then there cannot be - // an existing transition - ASSERT(!table()->contains(key)); - table()->add(key, Transition(0, structure)); - } - } - - private: - TransitionTable* table() const { ASSERT(!usingSingleTransitionSlot()); return m_transitions.m_table; } - Structure* singleTransition() const { - ASSERT(usingSingleTransitionSlot()); - return m_transitions.m_singleTransition.get(); - } - bool usingSingleTransitionSlot() const { return m_transitions.m_singleTransition.isFlagSet(usingSingleSlot); } - void setSingleTransition(Structure* structure) - { - ASSERT(usingSingleTransitionSlot()); - m_transitions.m_singleTransition.set(structure); - } - - void setTransitionTable(TransitionTable* table) - { - ASSERT(usingSingleTransitionSlot()); -#ifndef NDEBUG - setSingleTransition(0); -#endif - m_transitions.m_table = table; - // This implicitly clears the flag that indicates we're using a single transition - ASSERT(!usingSingleTransitionSlot()); - } - inline void reifySingleTransition(); - - enum UsingSingleSlot { - usingSingleSlot - }; - // Last bit indicates whether we are using the single transition optimisation - union { - TransitionTable* m_table; - PtrAndFlagsBase<Structure, UsingSingleSlot> m_singleTransition; - } m_transitions; - }; - } // namespace JSC #endif // StructureTransitionTable_h diff --git a/JavaScriptCore/runtime/UString.cpp b/JavaScriptCore/runtime/UString.cpp index 4a89a23..1684ec2 100644 --- a/JavaScriptCore/runtime/UString.cpp +++ b/JavaScriptCore/runtime/UString.cpp @@ -167,12 +167,12 @@ UString::UString(const char* c) { } -UString::UString(const char* c, int length) +UString::UString(const char* c, unsigned length) : m_rep(Rep::create(c, length)) { } -UString::UString(const UChar* c, int length) +UString::UString(const UChar* c, unsigned length) { if (length == 0) m_rep = &Rep::empty(); @@ -206,7 +206,7 @@ UString UString::from(int i) *--p = '-'; } - return UString(p, static_cast<int>(end - p)); + return UString(p, static_cast<unsigned>(end - p)); } UString UString::from(long long i) @@ -239,7 +239,7 @@ UString UString::from(long long i) *--p = '-'; } - return UString(p, static_cast<int>(end - p)); + return UString(p, static_cast<unsigned>(end - p)); } UString UString::from(unsigned int u) @@ -257,7 +257,7 @@ UString UString::from(unsigned int u) } } - return UString(p, static_cast<int>(end - p)); + return UString(p, static_cast<unsigned>(end - p)); } UString UString::from(long l) @@ -286,7 +286,7 @@ UString UString::from(long l) *--p = '-'; } - return UString(p, static_cast<int>(end - p)); + return UString(p, end - p); } UString UString::from(double d) @@ -299,8 +299,8 @@ UString UString::from(double d) bool UString::getCString(CStringBuffer& buffer) const { - int length = size(); - int neededSize = length + 1; + unsigned length = size(); + unsigned neededSize = length + 1; buffer.resize(neededSize); char* buf = buffer.data(); @@ -324,8 +324,8 @@ char* UString::ascii() const { static char* asciiBuffer = 0; - int length = size(); - int neededSize = length + 1; + unsigned length = size(); + unsigned neededSize = length + 1; delete[] asciiBuffer; asciiBuffer = new char[neededSize]; @@ -355,7 +355,7 @@ bool UString::is8Bit() const return true; } -UChar UString::operator[](int pos) const +UChar UString::operator[](unsigned pos) const { if (pos >= size()) return '\0'; @@ -495,7 +495,7 @@ uint32_t UString::toStrictUInt32(bool* ok) const *ok = false; // Empty string is not OK. - int len = m_rep->size(); + unsigned len = m_rep->length(); if (len == 0) return 0; const UChar* p = m_rep->data(); @@ -539,12 +539,9 @@ uint32_t UString::toStrictUInt32(bool* ok) const } } -int UString::find(const UString& f, int pos) const +unsigned UString::find(const UString& f, unsigned pos) const { - int fsz = f.size(); - - if (pos < 0) - pos = 0; + unsigned fsz = f.size(); if (fsz == 1) { UChar ch = f[0]; @@ -553,16 +550,16 @@ int UString::find(const UString& f, int pos) const if (*c == ch) return static_cast<int>(c - data()); } - return -1; + return NotFound; } - int sz = size(); + unsigned sz = size(); if (sz < fsz) - return -1; + return NotFound; if (fsz == 0) return pos; const UChar* end = data() + sz - fsz; - int fsizeminusone = (fsz - 1) * sizeof(UChar); + unsigned fsizeminusone = (fsz - 1) * sizeof(UChar); const UChar* fdata = f.data(); unsigned short fchar = fdata[0]; ++fdata; @@ -571,48 +568,44 @@ int UString::find(const UString& f, int pos) const return static_cast<int>(c - data()); } - return -1; + return NotFound; } -int UString::find(UChar ch, int pos) const +unsigned UString::find(UChar ch, unsigned pos) const { - if (pos < 0) - pos = 0; const UChar* end = data() + size(); for (const UChar* c = data() + pos; c < end; c++) { if (*c == ch) return static_cast<int>(c - data()); } - return -1; + return NotFound; } -int UString::rfind(const UString& f, int pos) const +unsigned UString::rfind(const UString& f, unsigned pos) const { - int sz = size(); - int fsz = f.size(); + unsigned sz = size(); + unsigned fsz = f.size(); if (sz < fsz) - return -1; - if (pos < 0) - pos = 0; + return NotFound; if (pos > sz - fsz) pos = sz - fsz; if (fsz == 0) return pos; - int fsizeminusone = (fsz - 1) * sizeof(UChar); + unsigned fsizeminusone = (fsz - 1) * sizeof(UChar); const UChar* fdata = f.data(); for (const UChar* c = data() + pos; c >= data(); c--) { if (*c == *fdata && !memcmp(c + 1, fdata + 1, fsizeminusone)) return static_cast<int>(c - data()); } - return -1; + return NotFound; } -int UString::rfind(UChar ch, int pos) const +unsigned UString::rfind(UChar ch, unsigned pos) const { if (isEmpty()) - return -1; + return NotFound; if (pos + 1 >= size()) pos = size() - 1; for (const UChar* c = data() + pos; c >= data(); c--) { @@ -620,21 +613,18 @@ int UString::rfind(UChar ch, int pos) const return static_cast<int>(c - data()); } - return -1; + return NotFound; } -UString UString::substr(int pos, int len) const +UString UString::substr(unsigned pos, unsigned len) const { - int s = size(); + unsigned s = size(); - if (pos < 0) - pos = 0; - else if (pos >= s) + if (pos >= s) pos = s; - if (len < 0) - len = s; - if (pos + len >= s) - len = s - pos; + unsigned limit = s - pos; + if (len > limit) + len = limit; if (pos == 0 && len == s) return *this; @@ -661,12 +651,12 @@ bool operator==(const UString& s1, const char *s2) bool operator<(const UString& s1, const UString& s2) { - const int l1 = s1.size(); - const int l2 = s2.size(); - const int lmin = l1 < l2 ? l1 : l2; + const unsigned l1 = s1.size(); + const unsigned l2 = s2.size(); + const unsigned lmin = l1 < l2 ? l1 : l2; const UChar* c1 = s1.data(); const UChar* c2 = s2.data(); - int l = 0; + unsigned l = 0; while (l < lmin && *c1 == *c2) { c1++; c2++; @@ -680,12 +670,12 @@ bool operator<(const UString& s1, const UString& s2) bool operator>(const UString& s1, const UString& s2) { - const int l1 = s1.size(); - const int l2 = s2.size(); - const int lmin = l1 < l2 ? l1 : l2; + const unsigned l1 = s1.size(); + const unsigned l2 = s2.size(); + const unsigned lmin = l1 < l2 ? l1 : l2; const UChar* c1 = s1.data(); const UChar* c2 = s2.data(); - int l = 0; + unsigned l = 0; while (l < lmin && *c1 == *c2) { c1++; c2++; @@ -699,12 +689,12 @@ bool operator>(const UString& s1, const UString& s2) int compare(const UString& s1, const UString& s2) { - const int l1 = s1.size(); - const int l2 = s2.size(); - const int lmin = l1 < l2 ? l1 : l2; + const unsigned l1 = s1.size(); + const unsigned l2 = s2.size(); + const unsigned lmin = l1 < l2 ? l1 : l2; const UChar* c1 = s1.data(); const UChar* c2 = s2.data(); - int l = 0; + unsigned l = 0; while (l < lmin && *c1 == *c2) { c1++; c2++; @@ -722,12 +712,12 @@ int compare(const UString& s1, const UString& s2) bool equal(const UString::Rep* r, const UString::Rep* b) { - int length = r->size(); - if (length != b->size()) + unsigned length = r->length(); + if (length != b->length()) return false; const UChar* d = r->data(); const UChar* s = b->data(); - for (int i = 0; i != length; ++i) { + for (unsigned i = 0; i != length; ++i) { if (d[i] != s[i]) return false; } @@ -737,7 +727,7 @@ bool equal(const UString::Rep* r, const UString::Rep* b) CString UString::UTF8String(bool strict) const { // Allocate a buffer big enough to hold all the characters. - const int length = size(); + const unsigned length = size(); Vector<char, 1024> buffer(length * 3); // Convert to runs of 8-bit characters. diff --git a/JavaScriptCore/runtime/UString.h b/JavaScriptCore/runtime/UString.h index 7d9ec49..75b43b7 100644 --- a/JavaScriptCore/runtime/UString.h +++ b/JavaScriptCore/runtime/UString.h @@ -31,7 +31,6 @@ #include <wtf/CrossThreadRefCounted.h> #include <wtf/OwnFastMallocPtr.h> #include <wtf/PassRefPtr.h> -#include <wtf/PtrAndFlags.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> #include <wtf/unicode/Unicode.h> @@ -83,8 +82,8 @@ namespace JSC { public: UString(); UString(const char*); // Constructor for null-terminated string. - UString(const char*, int length); - UString(const UChar*, int length); + UString(const char*, unsigned length); + UString(const UChar*, unsigned length); UString(const Vector<UChar>& buffer); UString(const UString& s) @@ -133,13 +132,13 @@ namespace JSC { const UChar* data() const { return m_rep->data(); } bool isNull() const { return m_rep == s_nullRep; } - bool isEmpty() const { return !m_rep->size(); } + bool isEmpty() const { return !m_rep->length(); } bool is8Bit() const; - int size() const { return m_rep->size(); } + unsigned size() const { return m_rep->length(); } - UChar operator[](int pos) const; + UChar operator[](unsigned pos) const; double toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const; double toDouble(bool tolerateTrailingJunk) const; @@ -151,12 +150,13 @@ namespace JSC { unsigned toArrayIndex(bool* ok = 0) const; - int find(const UString& f, int pos = 0) const; - int find(UChar, int pos = 0) const; - int rfind(const UString& f, int pos) const; - int rfind(UChar, int pos) const; + static const unsigned NotFound = 0xFFFFFFFFu; + unsigned find(const UString& f, unsigned pos = 0) const; + unsigned find(UChar, unsigned pos = 0) const; + unsigned rfind(const UString& f, unsigned pos) const; + unsigned rfind(UChar, unsigned pos) const; - UString substr(int pos = 0, int len = -1) const; + UString substr(unsigned pos = 0, unsigned len = 0xFFFFFFFF) const; static const UString& null() { return *s_nullUString; } @@ -182,7 +182,7 @@ namespace JSC { ALWAYS_INLINE bool operator==(const UString& s1, const UString& s2) { - int size = s1.size(); + unsigned size = s1.size(); switch (size) { case 0: return !s2.size(); @@ -246,9 +246,7 @@ namespace JSC { // We'd rather not do shared substring append for small strings, since // this runs too much risk of a tiny initial string holding down a // huge buffer. - // FIXME: this should be size_t but that would cause warnings until we - // fix UString sizes to be size_t instead of int - static const int minShareSize = Heap::minExtraCost / sizeof(UChar); + static const unsigned minShareSize = Heap::minExtraCost / sizeof(UChar); struct IdentifierRepHash : PtrHash<RefPtr<JSC::UString::Rep> > { static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->existingHash(); } @@ -327,6 +325,14 @@ namespace JSC { unsigned m_length; }; + inline void sumWithOverflow(unsigned& total, unsigned addend, bool& overflow) + { + unsigned oldTotal = total; + total = oldTotal + addend; + if (total < oldTotal) + overflow = true; + } + template<typename StringType1, typename StringType2> PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2) { @@ -334,7 +340,11 @@ namespace JSC { StringTypeAdapter<StringType2> adapter2(string2); UChar* buffer; - unsigned length = adapter1.length() + adapter2.length(); + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + if (overflow) + return 0; PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); if (!resultImpl) return 0; @@ -355,7 +365,12 @@ namespace JSC { StringTypeAdapter<StringType3> adapter3(string3); UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length(); + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + if (overflow) + return 0; PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); if (!resultImpl) return 0; @@ -379,7 +394,13 @@ namespace JSC { StringTypeAdapter<StringType4> adapter4(string4); UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length(); + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + if (overflow) + return 0; PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); if (!resultImpl) return 0; @@ -406,7 +427,14 @@ namespace JSC { StringTypeAdapter<StringType5> adapter5(string5); UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length(); + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + if (overflow) + return 0; PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); if (!resultImpl) return 0; @@ -436,7 +464,15 @@ namespace JSC { StringTypeAdapter<StringType6> adapter6(string6); UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length(); + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + sumWithOverflow(length, adapter6.length(), overflow); + if (overflow) + return 0; PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); if (!resultImpl) return 0; @@ -469,7 +505,16 @@ namespace JSC { StringTypeAdapter<StringType7> adapter7(string7); UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length() + adapter7.length(); + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + sumWithOverflow(length, adapter6.length(), overflow); + sumWithOverflow(length, adapter7.length(), overflow); + if (overflow) + return 0; PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); if (!resultImpl) return 0; @@ -505,7 +550,17 @@ namespace JSC { StringTypeAdapter<StringType8> adapter8(string8); UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length() + adapter7.length() + adapter8.length(); + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + sumWithOverflow(length, adapter6.length(), overflow); + sumWithOverflow(length, adapter7.length(), overflow); + sumWithOverflow(length, adapter8.length(), overflow); + if (overflow) + return 0; PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); if (!resultImpl) return 0; diff --git a/JavaScriptCore/runtime/UStringImpl.cpp b/JavaScriptCore/runtime/UStringImpl.cpp index 9882007..b7d9a40 100644 --- a/JavaScriptCore/runtime/UStringImpl.cpp +++ b/JavaScriptCore/runtime/UStringImpl.cpp @@ -50,7 +50,7 @@ PassRefPtr<UStringImpl> UStringImpl::create(const char* c) return result; } -PassRefPtr<UStringImpl> UStringImpl::create(const char* c, int length) +PassRefPtr<UStringImpl> UStringImpl::create(const char* c, unsigned length) { ASSERT(c); @@ -59,12 +59,12 @@ PassRefPtr<UStringImpl> UStringImpl::create(const char* c, int length) UChar* d; PassRefPtr<UStringImpl> result = UStringImpl::createUninitialized(length, d); - for (int i = 0; i < length; i++) + for (unsigned i = 0; i < length; i++) d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend return result; } -PassRefPtr<UStringImpl> UStringImpl::create(const UChar* buffer, int length) +PassRefPtr<UStringImpl> UStringImpl::create(const UChar* buffer, unsigned length) { UChar* newBuffer; PassRefPtr<UStringImpl> impl = createUninitialized(length, newBuffer); @@ -75,12 +75,14 @@ PassRefPtr<UStringImpl> UStringImpl::create(const UChar* buffer, int length) SharedUChar* UStringImpl::baseSharedBuffer() { ASSERT((bufferOwnership() == BufferShared) - || ((bufferOwnership() == BufferOwned) && !m_dataBuffer.asPtr<void*>())); + || ((bufferOwnership() == BufferOwned) && !m_buffer)); - if (bufferOwnership() != BufferShared) - m_dataBuffer = UntypedPtrAndBitfield(SharedUChar::create(new OwnFastMallocPtr<UChar>(m_data)).releaseRef(), BufferShared); + if (bufferOwnership() != BufferShared) { + m_refCountAndFlags = (m_refCountAndFlags & ~s_refCountMaskBufferOwnership) | BufferShared; + m_bufferShared = SharedUChar::create(new OwnFastMallocPtr<UChar>(m_data)).releaseRef(); + } - return m_dataBuffer.asPtr<SharedUChar*>(); + return m_bufferShared; } SharedUChar* UStringImpl::sharedBuffer() @@ -108,12 +110,43 @@ UStringImpl::~UStringImpl() if (bufferOwnership() == BufferOwned) fastFree(m_data); else if (bufferOwnership() == BufferSubstring) - m_dataBuffer.asPtr<UStringImpl*>()->deref(); + m_bufferSubstring->deref(); else { ASSERT(bufferOwnership() == BufferShared); - m_dataBuffer.asPtr<SharedUChar*>()->deref(); + m_bufferShared->deref(); } } } +void URopeImpl::derefFibersNonRecursive(Vector<URopeImpl*, 32>& workQueue) +{ + unsigned length = fiberCount(); + for (unsigned i = 0; i < length; ++i) { + Fiber& fiber = fibers(i); + if (fiber->isRope()) { + URopeImpl* nextRope = static_cast<URopeImpl*>(fiber); + if (nextRope->hasOneRef()) + workQueue.append(nextRope); + else + nextRope->deref(); + } else + static_cast<UStringImpl*>(fiber)->deref(); + } +} + +void URopeImpl::destructNonRecursive() +{ + Vector<URopeImpl*, 32> workQueue; + + derefFibersNonRecursive(workQueue); + delete this; + + while (!workQueue.isEmpty()) { + URopeImpl* rope = workQueue.last(); + workQueue.removeLast(); + rope->derefFibersNonRecursive(workQueue); + delete rope; + } } + +} // namespace JSC diff --git a/JavaScriptCore/runtime/UStringImpl.h b/JavaScriptCore/runtime/UStringImpl.h index bbea0aa..142e01d 100644 --- a/JavaScriptCore/runtime/UStringImpl.h +++ b/JavaScriptCore/runtime/UStringImpl.h @@ -40,72 +40,93 @@ class IdentifierTable; typedef CrossThreadRefCounted<OwnFastMallocPtr<UChar> > SharedUChar; -class UntypedPtrAndBitfield { +class UStringOrRopeImpl : public Noncopyable { public: - UntypedPtrAndBitfield() {} + bool isRope() { return (m_refCountAndFlags & s_refCountIsRope) == s_refCountIsRope; } + unsigned length() const { return m_length; } - UntypedPtrAndBitfield(void* ptrValue, uintptr_t bitValue) - : m_value(reinterpret_cast<uintptr_t>(ptrValue) | bitValue) -#ifndef NDEBUG - , m_leaksPtr(ptrValue) -#endif - { - ASSERT(ptrValue == asPtr<void*>()); - ASSERT((*this & ~s_alignmentMask) == bitValue); - } + void ref() { m_refCountAndFlags += s_refCountIncrement; } + inline void deref(); + +protected: + enum BufferOwnership { + BufferInternal, + BufferOwned, + BufferSubstring, + BufferShared, + }; + + using Noncopyable::operator new; + void* operator new(size_t, void* inPlace) { return inPlace; } - template<typename T> - T asPtr() const { return reinterpret_cast<T>(m_value & s_alignmentMask); } + // For SmallStringStorage, which allocates an array and uses an in-place new. + UStringOrRopeImpl() { } - UntypedPtrAndBitfield& operator&=(uintptr_t bits) + UStringOrRopeImpl(unsigned length, BufferOwnership ownership) + : m_refCountAndFlags(s_refCountIncrement | s_refCountFlagShouldReportedCost | ownership) + , m_length(length) { - m_value &= bits | s_alignmentMask; - return *this; + ASSERT(!isRope()); } - UntypedPtrAndBitfield& operator|=(uintptr_t bits) + enum StaticStringConstructType { ConstructStaticString }; + UStringOrRopeImpl(unsigned length, StaticStringConstructType) + : m_refCountAndFlags(s_refCountFlagStatic | BufferOwned) + , m_length(length) { - m_value |= bits & ~s_alignmentMask; - return *this; + ASSERT(!isRope()); } - uintptr_t operator&(uintptr_t mask) const + enum RopeConstructType { ConstructRope }; + UStringOrRopeImpl(RopeConstructType) + : m_refCountAndFlags(s_refCountIncrement | s_refCountIsRope) + , m_length(0) { - return m_value & mask & ~s_alignmentMask; + ASSERT(isRope()); } -private: - static const uintptr_t s_alignmentMask = ~static_cast<uintptr_t>(0x7); - uintptr_t m_value; -#ifndef NDEBUG - void* m_leaksPtr; // Only used to allow tools like leaks on OSX to detect that the memory is referenced. -#endif + // The bottom 5 bits hold flags, the top 27 bits hold the ref count. + // When dereferencing UStringImpls we check for the ref count AND the + // static bit both being zero - static strings are never deleted. + static const unsigned s_refCountMask = 0xFFFFFFE0; + static const unsigned s_refCountIncrement = 0x20; + static const unsigned s_refCountFlagStatic = 0x10; + static const unsigned s_refCountFlagShouldReportedCost = 0x8; + static const unsigned s_refCountFlagIsIdentifier = 0x4; + static const unsigned s_refCountMaskBufferOwnership = 0x3; + // Use an otherwise invalid permutation of flags (static & shouldReportedCost - + // static strings do not set shouldReportedCost in the constructor, and this bit + // is only ever cleared, not set) to identify objects that are ropes. + static const unsigned s_refCountIsRope = s_refCountFlagStatic | s_refCountFlagShouldReportedCost; + + unsigned m_refCountAndFlags; + unsigned m_length; }; -class UStringImpl : Noncopyable { +class UStringImpl : public UStringOrRopeImpl { public: template<size_t inlineCapacity> static PassRefPtr<UStringImpl> adopt(Vector<UChar, inlineCapacity>& vector) { if (unsigned length = vector.size()) { ASSERT(vector.data()); - return adoptRef(new UStringImpl(vector.releaseBuffer(), length, BufferOwned)); + return adoptRef(new UStringImpl(vector.releaseBuffer(), length)); } return &empty(); } static PassRefPtr<UStringImpl> create(const char* c); - static PassRefPtr<UStringImpl> create(const char* c, int length); - static PassRefPtr<UStringImpl> create(const UChar* buffer, int length); + static PassRefPtr<UStringImpl> create(const char* c, unsigned length); + static PassRefPtr<UStringImpl> create(const UChar* buffer, unsigned length); - static PassRefPtr<UStringImpl> create(PassRefPtr<UStringImpl> rep, int offset, int length) + static PassRefPtr<UStringImpl> create(PassRefPtr<UStringImpl> rep, unsigned offset, unsigned length) { ASSERT(rep); rep->checkConsistency(); return adoptRef(new UStringImpl(rep->m_data + offset, length, rep->bufferOwnerString())); } - static PassRefPtr<UStringImpl> create(PassRefPtr<SharedUChar> sharedBuffer, UChar* buffer, int length) + static PassRefPtr<UStringImpl> create(PassRefPtr<SharedUChar> sharedBuffer, UChar* buffer, unsigned length) { return adoptRef(new UStringImpl(buffer, length, sharedBuffer)); } @@ -121,7 +142,7 @@ public: CRASH(); UStringImpl* resultImpl = static_cast<UStringImpl*>(fastMalloc(sizeof(UChar) * length + sizeof(UStringImpl))); output = reinterpret_cast<UChar*>(resultImpl + 1); - return adoptRef(new(resultImpl) UStringImpl(output, length, BufferInternal)); + return adoptRef(new(resultImpl) UStringImpl(length)); } static PassRefPtr<UStringImpl> tryCreateUninitialized(unsigned length, UChar*& output) @@ -137,31 +158,36 @@ public: if (!tryFastMalloc(sizeof(UChar) * length + sizeof(UStringImpl)).getValue(resultImpl)) return 0; output = reinterpret_cast<UChar*>(resultImpl + 1); - return adoptRef(new(resultImpl) UStringImpl(output, length, BufferInternal)); + return adoptRef(new(resultImpl) UStringImpl(length)); } SharedUChar* sharedBuffer(); UChar* data() const { return m_data; } - int size() const { return m_length; } size_t cost() { // For substrings, return the cost of the base string. if (bufferOwnership() == BufferSubstring) - return m_dataBuffer.asPtr<UStringImpl*>()->cost(); + return m_bufferSubstring->cost(); - if (m_dataBuffer & s_reportedCostBit) - return 0; - m_dataBuffer |= s_reportedCostBit; - return m_length; + if (m_refCountAndFlags & s_refCountFlagShouldReportedCost) { + m_refCountAndFlags &= ~s_refCountFlagShouldReportedCost; + return m_length; + } + return 0; } unsigned hash() const { if (!m_hash) m_hash = computeHash(data(), m_length); return m_hash; } unsigned existingHash() const { ASSERT(m_hash); return m_hash; } // fast path for Identifiers void setHash(unsigned hash) { ASSERT(hash == computeHash(data(), m_length)); m_hash = hash; } // fast path for Identifiers - bool isIdentifier() const { return m_isIdentifier; } - void setIsIdentifier(bool isIdentifier) { m_isIdentifier = isIdentifier; } + bool isIdentifier() const { return m_refCountAndFlags & s_refCountFlagIsIdentifier; } + void setIsIdentifier(bool isIdentifier) + { + if (isIdentifier) + m_refCountAndFlags |= s_refCountFlagIsIdentifier; + else + m_refCountAndFlags &= ~s_refCountFlagIsIdentifier; + } - UStringImpl* ref() { m_refCount += s_refCountIncrement; return this; } - ALWAYS_INLINE void deref() { if (!(m_refCount -= s_refCountIncrement)) delete this; } + ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & (s_refCountMask | s_refCountFlagStatic))) delete this; } static void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) { @@ -172,8 +198,8 @@ public: memcpy(destination, source, numCharacters * sizeof(UChar)); } - static unsigned computeHash(const UChar* s, int length) { ASSERT(length >= 0); return WTF::stringHash(s, length); } - static unsigned computeHash(const char* s, int length) { ASSERT(length >= 0); return WTF::stringHash(s, length); } + static unsigned computeHash(const UChar* s, unsigned length) { return WTF::stringHash(s, length); } + static unsigned computeHash(const char* s, unsigned length) { return WTF::stringHash(s, length); } static unsigned computeHash(const char* s) { return WTF::stringHash(s); } static UStringImpl& empty() { return *s_empty; } @@ -187,109 +213,145 @@ public: } private: - enum BufferOwnership { - BufferInternal, - BufferOwned, - BufferSubstring, - BufferShared, - }; - // For SmallStringStorage, which allocates an array and uses an in-place new. UStringImpl() { } - // Used to construct normal strings with an internal or external buffer. - UStringImpl(UChar* data, int length, BufferOwnership ownership) - : m_data(data) - , m_length(length) - , m_refCount(s_refCountIncrement) + // Used to construct normal strings with an internal buffer. + UStringImpl(unsigned length) + : UStringOrRopeImpl(length, BufferInternal) + , m_data(reinterpret_cast<UChar*>(this + 1)) + , m_buffer(0) + , m_hash(0) + { + checkConsistency(); + } + + // Used to construct normal strings with an external buffer. + UStringImpl(UChar* data, unsigned length) + : UStringOrRopeImpl(length, BufferOwned) + , m_data(data) + , m_buffer(0) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, ownership) { - ASSERT((ownership == BufferInternal) || (ownership == BufferOwned)); checkConsistency(); } // Used to construct static strings, which have an special refCount that can never hit zero. // This means that the static string will never be destroyed, which is important because // static strings will be shared across threads & ref-counted in a non-threadsafe manner. - enum StaticStringConstructType { ConstructStaticString }; - UStringImpl(UChar* data, int length, StaticStringConstructType) - : m_data(data) - , m_length(length) - , m_refCount(s_staticRefCountInitialValue) + UStringImpl(UChar* data, unsigned length, StaticStringConstructType) + : UStringOrRopeImpl(length, ConstructStaticString) + , m_data(data) + , m_buffer(0) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, BufferOwned) { checkConsistency(); } // Used to create new strings that are a substring of an existing string. - UStringImpl(UChar* data, int length, PassRefPtr<UStringImpl> base) - : m_data(data) - , m_length(length) - , m_refCount(s_refCountIncrement) + UStringImpl(UChar* data, unsigned length, PassRefPtr<UStringImpl> base) + : UStringOrRopeImpl(length, BufferSubstring) + , m_data(data) + , m_bufferSubstring(base.releaseRef()) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(base.releaseRef(), BufferSubstring) { // Do use static strings as a base for substrings; UntypedPtrAndBitfield assumes // that all pointers will be at least 8-byte aligned, we cannot guarantee that of // UStringImpls that are not heap allocated. - ASSERT(m_dataBuffer.asPtr<UStringImpl*>()->size()); - ASSERT(!m_dataBuffer.asPtr<UStringImpl*>()->isStatic()); + ASSERT(m_bufferSubstring->length()); + ASSERT(!m_bufferSubstring->isStatic()); checkConsistency(); } // Used to construct new strings sharing an existing shared buffer. - UStringImpl(UChar* data, int length, PassRefPtr<SharedUChar> sharedBuffer) - : m_data(data) - , m_length(length) - , m_refCount(s_refCountIncrement) + UStringImpl(UChar* data, unsigned length, PassRefPtr<SharedUChar> sharedBuffer) + : UStringOrRopeImpl(length, BufferShared) + , m_data(data) + , m_bufferShared(sharedBuffer.releaseRef()) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(sharedBuffer.releaseRef(), BufferShared) { checkConsistency(); } - using Noncopyable::operator new; - void* operator new(size_t, void* inPlace) { return inPlace; } - ~UStringImpl(); // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. - static const int s_minLengthToShare = 10; + static const unsigned s_minLengthToShare = 10; static const unsigned s_copyCharsInlineCutOff = 20; - static const uintptr_t s_bufferOwnershipMask = 3; - static const uintptr_t s_reportedCostBit = 4; - // We initialize and increment/decrement the refCount for all normal (non-static) strings by the value 2. - // We initialize static strings with an odd number (specifically, 1), such that the refCount cannot reach zero. - static const int s_refCountIncrement = 2; - static const int s_staticRefCountInitialValue = 1; - - UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } - const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } + + UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_bufferSubstring : this; } + const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_bufferSubstring : this; } SharedUChar* baseSharedBuffer(); - unsigned bufferOwnership() const { return m_dataBuffer & s_bufferOwnershipMask; } - bool isStatic() const { return m_refCount & 1; } + unsigned bufferOwnership() const { return m_refCountAndFlags & s_refCountMaskBufferOwnership; } + bool isStatic() const { return m_refCountAndFlags & s_refCountFlagStatic; } // unshared data UChar* m_data; - int m_length; - unsigned m_refCount; - mutable unsigned m_hash : 31; - mutable unsigned m_isIdentifier : 1; - UntypedPtrAndBitfield m_dataBuffer; + union { + void* m_buffer; + UStringImpl* m_bufferSubstring; + SharedUChar* m_bufferShared; + }; + mutable unsigned m_hash; JS_EXPORTDATA static UStringImpl* s_empty; friend class JIT; friend class SmallStringsStorage; + friend class UStringOrRopeImpl; friend void initializeUString(); }; +class URopeImpl : public UStringOrRopeImpl { +public: + // A URopeImpl is composed from a set of smaller strings called Fibers. + // Each Fiber in a rope is either UStringImpl or another URopeImpl. + typedef UStringOrRopeImpl* Fiber; + + // Creates a URopeImpl comprising of 'fiberCount' Fibers. + // The URopeImpl is constructed in an uninitialized state - initialize must be called for each Fiber in the URopeImpl. + static PassRefPtr<URopeImpl> tryCreateUninitialized(unsigned fiberCount) + { + void* allocation; + if (tryFastMalloc(sizeof(URopeImpl) + (fiberCount - 1) * sizeof(Fiber)).getValue(allocation)) + return adoptRef(new (allocation) URopeImpl(fiberCount)); + return 0; + } + + void initializeFiber(unsigned &index, Fiber fiber) + { + m_fibers[index++] = fiber; + fiber->ref(); + m_length += fiber->length(); + } + + unsigned fiberCount() { return m_fiberCount; } + Fiber& fibers(unsigned index) { return m_fibers[index]; } + + ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & s_refCountMask)) destructNonRecursive(); } + +private: + URopeImpl(unsigned fiberCount) : UStringOrRopeImpl(ConstructRope), m_fiberCount(fiberCount) {} + + void destructNonRecursive(); + void derefFibersNonRecursive(Vector<URopeImpl*, 32>& workQueue); + + bool hasOneRef() { return (m_refCountAndFlags & s_refCountMask) == s_refCountIncrement; } + + unsigned m_fiberCount; + Fiber m_fibers[1]; + + friend class UStringOrRopeImpl; +}; + +inline void UStringOrRopeImpl::deref() +{ + if (isRope()) + static_cast<URopeImpl*>(this)->deref(); + else + static_cast<UStringImpl*>(this)->deref(); +} + bool equal(const UStringImpl*, const UStringImpl*); } |