Commit fbf6d9a0 authored by ggaren@apple.com's avatar ggaren@apple.com

Switched ropes from malloc memory to GC memory

https://bugs.webkit.org/show_bug.cgi?id=70364

Reviewed by Gavin Barraclough.

~1% SunSpider speedup. Neutral elsewhere. Removes one cause for strings
having C++ destructors.

* heap/MarkStack.cpp:
(JSC::visitChildren): Call the JSString visitChildren function now,
since it's no longer a no-op.

* runtime/JSString.cpp:
(JSC::JSString::~JSString): Moved this destructor out of line because
it's called virtually, so there's no value to inlining.

(JSC::JSString::RopeBuilder::expand): Switched RopeBuilder to be a thin
initializing wrapper around JSString. JSString now represents ropes
directly, rather than relying on an underlying malloc object.

(JSC::JSString::visitChildren): Visit our rope fibers, since they're GC
objects now.

(JSC::JSString::resolveRope):
(JSC::JSString::resolveRopeSlowCase):
(JSC::JSString::outOfMemory): Updated for operating on JSStrings instead
of malloc objects.

(JSC::JSString::replaceCharacter): Removed optimizations for substringing
ropes and replacing subsections of ropes. We want to reimplement versions
of these optimizations in the future, but this patch already has good
performance without them.

* runtime/JSString.h:
(JSC::RopeBuilder::JSString):
(JSC::RopeBuilder::finishCreation):
(JSC::RopeBuilder::createNull):
(JSC::RopeBuilder::create):
(JSC::RopeBuilder::createHasOtherOwner):
(JSC::jsSingleCharacterString):
(JSC::jsSingleCharacterSubstring):
(JSC::jsNontrivialString):
(JSC::jsString):
(JSC::jsSubstring):
(JSC::jsOwnedString): Lots of mechanical changes here. The two important
things are: (1) The fibers in JSString::m_fibers are JSStrings now, not
malloc objects; (2) I simplified the JSString constructor interface to
only accept PassRefPtr<StringImpl>, instead of variations on that like
UString, reducing refcount churn.

* runtime/JSValue.h:
* runtime/JSValue.cpp:
(JSC::JSValue::toPrimitiveString): Updated this function to return a
JSString instead of a UString, since that's what clients want now.

* runtime/Operations.cpp:
(JSC::jsAddSlowCase):
* runtime/Operations.h:
(JSC::jsString):
* runtime/SmallStrings.cpp:
(JSC::SmallStrings::createEmptyString): Updated for interface changes above.

* runtime/StringConstructor.cpp:
(JSC::constructWithStringConstructor):
* runtime/StringObject.h:
(JSC::StringObject::create): Don't create a new JSString if we already
have a JSString.

* runtime/StringPrototype.cpp:
(JSC::stringProtoFuncConcat): Updated for interface changes above.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@97827 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a9d12e73
2011-10-18 Geoffrey Garen <ggaren@apple.com>
Switched ropes from malloc memory to GC memory
https://bugs.webkit.org/show_bug.cgi?id=70364
Reviewed by Gavin Barraclough.
~1% SunSpider speedup. Neutral elsewhere. Removes one cause for strings
having C++ destructors.
* heap/MarkStack.cpp:
(JSC::visitChildren): Call the JSString visitChildren function now,
since it's no longer a no-op.
* runtime/JSString.cpp:
(JSC::JSString::~JSString): Moved this destructor out of line because
it's called virtually, so there's no value to inlining.
(JSC::JSString::RopeBuilder::expand): Switched RopeBuilder to be a thin
initializing wrapper around JSString. JSString now represents ropes
directly, rather than relying on an underlying malloc object.
(JSC::JSString::visitChildren): Visit our rope fibers, since they're GC
objects now.
(JSC::JSString::resolveRope):
(JSC::JSString::resolveRopeSlowCase):
(JSC::JSString::outOfMemory): Updated for operating on JSStrings instead
of malloc objects.
(JSC::JSString::replaceCharacter): Removed optimizations for substringing
ropes and replacing subsections of ropes. We want to reimplement versions
of these optimizations in the future, but this patch already has good
performance without them.
* runtime/JSString.h:
(JSC::RopeBuilder::JSString):
(JSC::RopeBuilder::finishCreation):
(JSC::RopeBuilder::createNull):
(JSC::RopeBuilder::create):
(JSC::RopeBuilder::createHasOtherOwner):
(JSC::jsSingleCharacterString):
(JSC::jsSingleCharacterSubstring):
(JSC::jsNontrivialString):
(JSC::jsString):
(JSC::jsSubstring):
(JSC::jsOwnedString): Lots of mechanical changes here. The two important
things are: (1) The fibers in JSString::m_fibers are JSStrings now, not
malloc objects; (2) I simplified the JSString constructor interface to
only accept PassRefPtr<StringImpl>, instead of variations on that like
UString, reducing refcount churn.
* runtime/JSValue.h:
* runtime/JSValue.cpp:
(JSC::JSValue::toPrimitiveString): Updated this function to return a
JSString instead of a UString, since that's what clients want now.
* runtime/Operations.cpp:
(JSC::jsAddSlowCase):
* runtime/Operations.h:
(JSC::jsString):
* runtime/SmallStrings.cpp:
(JSC::SmallStrings::createEmptyString): Updated for interface changes above.
* runtime/StringConstructor.cpp:
(JSC::constructWithStringConstructor):
* runtime/StringObject.h:
(JSC::StringObject::create): Don't create a new JSString if we already
have a JSString.
* runtime/StringPrototype.cpp:
(JSC::stringProtoFuncConcat): Updated for interface changes above.
2011-10-18 Gavin Barraclough <barraclough@apple.com>
Errrk, fix partial commit of r97825!
......@@ -102,8 +102,10 @@ ALWAYS_INLINE static void visitChildren(SlotVisitor& visitor, const JSCell* cell
ASSERT(Heap::isMarked(cell));
if (cell->vptr() == jsStringVPtr)
if (cell->vptr() == jsStringVPtr) {
JSString::visitChildren(const_cast<JSCell*>(cell), visitor);
return;
}
if (cell->vptr() == jsFinalObjectVPtr) {
JSObject::visitChildren(const_cast<JSCell*>(cell), visitor);
......
......@@ -74,7 +74,8 @@
macro(value) \
macro(valueOf) \
macro(writable) \
macro(displayName)
macro(displayName) \
macro(undefined)
#define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \
macro(null) \
......
......@@ -36,6 +36,27 @@ static const unsigned substringFromRopeCutoff = 4;
const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) };
void JSString::RopeBuilder::expand()
{
ASSERT(m_jsString->m_fiberCount == JSString::s_maxInternalRopeLength);
JSString* jsString = m_jsString;
m_jsString = jsStringBuilder(&m_globalData);
append(jsString);
}
JSString::~JSString()
{
ASSERT(vptr() == JSGlobalData::jsStringVPtr);
}
void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
JSString* thisObject = static_cast<JSString*>(cell);
Base::visitChildren(thisObject, visitor);
for (size_t i = 0; i < thisObject->m_fiberCount; ++i)
visitor.append(&thisObject->m_fibers[i]);
}
void JSString::resolveRope(ExecState* exec) const
{
ASSERT(isRope());
......@@ -48,35 +69,21 @@ void JSString::resolveRope(ExecState* exec) const
return;
}
RopeImpl::Fiber currentFiber = m_fibers[0];
if ((m_fiberCount > 2) || (RopeImpl::isRope(currentFiber))
|| ((m_fiberCount == 2) && (RopeImpl::isRope(m_fibers[1])))) {
resolveRopeSlowCase(exec, buffer);
return;
for (size_t i = 0; i < m_fiberCount; ++i) {
if (m_fibers[i]->isRope())
return resolveRopeSlowCase(exec, buffer);
}
UChar* position = buffer;
StringImpl* string = static_cast<StringImpl*>(currentFiber);
unsigned length = string->length();
StringImpl::copyChars(position, string->characters(), length);
if (m_fiberCount > 1) {
position += length;
currentFiber = m_fibers[1];
string = static_cast<StringImpl*>(currentFiber);
length = string->length();
for (size_t i = 0; i < m_fiberCount; ++i) {
StringImpl* string = m_fibers[i]->m_value.impl();
unsigned length = string->length();
StringImpl::copyChars(position, string->characters(), length);
position += length;
}
ASSERT((buffer + m_length) == position);
for (unsigned i = 0; i < m_fiberCount; ++i) {
RopeImpl::deref(m_fibers[i]);
m_fibers[i] = 0;
}
m_fiberCount = 0;
m_fiberCount = 0;
ASSERT(!isRope());
}
......@@ -96,42 +103,32 @@ void JSString::resolveRopeSlowCase(ExecState* exec, UChar* buffer) const
UChar* position = buffer + m_length;
// Start with the current RopeImpl.
Vector<RopeImpl::Fiber, 32> workQueue;
RopeImpl::Fiber currentFiber;
Vector<JSString*, 32> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK.
JSString* currentFiber;
for (unsigned i = 0; i < (m_fiberCount - 1); ++i)
workQueue.append(m_fibers[i]);
currentFiber = m_fibers[m_fiberCount - 1];
workQueue.append(m_fibers[i].get());
currentFiber = m_fibers[m_fiberCount - 1].get();
while (true) {
if (RopeImpl::isRope(currentFiber)) {
RopeImpl* rope = static_cast<RopeImpl*>(currentFiber);
if (currentFiber->isRope()) {
// 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 fiberCountMinusOne = rope->fiberCount() - 1;
unsigned fiberCountMinusOne = currentFiber->fiberCount() - 1;
for (unsigned i = 0; i < fiberCountMinusOne; ++i)
workQueue.append(rope->fibers()[i]);
currentFiber = rope->fibers()[fiberCountMinusOne];
workQueue.append(currentFiber->m_fibers[i].get());
currentFiber = currentFiber->m_fibers[fiberCountMinusOne].get();
} else {
StringImpl* string = static_cast<StringImpl*>(currentFiber);
StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
unsigned length = string->length();
position -= length;
StringImpl::copyChars(position, string->characters(), length);
// Was this the last item in the work queue?
if (workQueue.isEmpty()) {
// Create a string from the UChar buffer, clear the rope RefPtr.
ASSERT(buffer == position);
for (unsigned i = 0; i < m_fiberCount; ++i) {
RopeImpl::deref(m_fibers[i]);
m_fibers[i] = 0;
}
m_fiberCount = 0;
ASSERT(buffer == position);
ASSERT(!isRope());
return;
}
// No! - set the next item up to process.
currentFiber = workQueue.last();
workQueue.removeLast();
}
......@@ -140,10 +137,6 @@ void JSString::resolveRopeSlowCase(ExecState* exec, UChar* buffer) const
void JSString::outOfMemory(ExecState* exec) const
{
for (unsigned i = 0; i < m_fiberCount; ++i) {
RopeImpl::deref(m_fibers[i]);
m_fibers[i] = 0;
}
m_fiberCount = 0;
ASSERT(!isRope());
ASSERT(m_value == UString());
......@@ -151,110 +144,12 @@ void JSString::outOfMemory(ExecState* exec) const
throwOutOfMemoryError(exec);
}
// This function construsts a substring out of a rope without flattening by reusing the existing fibers.
// This can reduce memory usage substantially. Since traversing ropes is slow the function will revert
// back to flattening if the rope turns out to be long.
JSString* JSString::substringFromRope(ExecState* exec, unsigned substringStart, unsigned substringLength)
{
ASSERT(isRope());
ASSERT(substringLength);
JSGlobalData* globalData = &exec->globalData();
UString substringFibers[3];
unsigned fiberCount = 0;
unsigned substringFiberCount = 0;
unsigned substringEnd = substringStart + substringLength;
unsigned fiberEnd = 0;
RopeIterator end;
for (RopeIterator it(m_fibers.data(), m_fiberCount); it != end; ++it) {
++fiberCount;
StringImpl* fiberString = *it;
unsigned fiberStart = fiberEnd;
fiberEnd = fiberStart + fiberString->length();
if (fiberEnd <= substringStart)
continue;
unsigned copyStart = std::max(substringStart, fiberStart);
unsigned copyEnd = std::min(substringEnd, fiberEnd);
if (copyStart == fiberStart && copyEnd == fiberEnd)
substringFibers[substringFiberCount++] = UString(fiberString);
else
substringFibers[substringFiberCount++] = UString(StringImpl::create(fiberString, copyStart - fiberStart, copyEnd - copyStart));
if (fiberEnd >= substringEnd)
break;
if (fiberCount > substringFromRopeCutoff || substringFiberCount >= 3) {
// This turned out to be a really inefficient rope. Just flatten it.
resolveRope(exec);
return jsSubstring(&exec->globalData(), m_value, substringStart, substringLength);
}
}
ASSERT(substringFiberCount && substringFiberCount <= 3);
if (substringLength == 1) {
ASSERT(substringFiberCount == 1);
UChar c = substringFibers[0][0];
if (c <= maxSingleCharacterString)
return globalData->smallStrings.singleCharacterString(globalData, c);
}
if (substringFiberCount == 1)
return JSString::create(*globalData, substringFibers[0]);
if (substringFiberCount == 2)
return JSString::create(*globalData, substringFibers[0], substringFibers[1]);
return JSString::create(*globalData, substringFibers[0], substringFibers[1], substringFibers[2]);
}
JSValue JSString::replaceCharacter(ExecState* exec, UChar character, const UString& replacement)
{
if (!isRope()) {
size_t matchPosition = m_value.find(character);
if (matchPosition == notFound)
return JSValue(this);
return jsString(exec, m_value.substringSharingImpl(0, matchPosition), replacement, m_value.substringSharingImpl(matchPosition + 1));
}
RopeIterator end;
// Count total fibers and find matching string.
size_t fiberCount = 0;
StringImpl* matchString = 0;
size_t matchPosition = notFound;
for (RopeIterator it(m_fibers.data(), m_fiberCount); it != end; ++it) {
++fiberCount;
if (matchString)
continue;
StringImpl* string = *it;
matchPosition = string->find(character);
if (matchPosition == notFound)
continue;
matchString = string;
}
if (!matchString)
return this;
RopeBuilder builder(replacement.length() ? fiberCount + 2 : fiberCount + 1);
if (UNLIKELY(builder.isOutOfMemory()))
return throwOutOfMemoryError(exec);
for (RopeIterator it(m_fibers.data(), m_fiberCount); it != end; ++it) {
StringImpl* string = *it;
if (string != matchString) {
builder.append(UString(string));
continue;
}
builder.append(UString(string).substringSharingImpl(0, matchPosition));
if (replacement.length())
builder.append(replacement);
builder.append(UString(string).substringSharingImpl(matchPosition + 1));
matchString = 0;
}
JSGlobalData* globalData = &exec->globalData();
return JSValue(JSString::create(*globalData, builder.release()));
size_t matchPosition = value(exec).find(character);
if (matchPosition == notFound)
return JSValue(this);
return jsString(exec, m_value.substringSharingImpl(0, matchPosition), replacement, value(exec).substringSharingImpl(matchPosition + 1));
}
JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i)
......
This diff is collapsed.
......@@ -204,4 +204,28 @@ bool JSValue::isValidCallee()
return asObject(asCell())->globalObject();
}
JSString* JSValue::toPrimitiveString(ExecState* exec) const
{
if (isString())
return static_cast<JSString*>(asCell());
if (isInt32())
return jsString(&exec->globalData(), exec->globalData().numericStrings.add(asInt32()));
if (isDouble())
return jsString(&exec->globalData(), exec->globalData().numericStrings.add(asDouble()));
if (isTrue())
return jsNontrivialString(exec, exec->propertyNames().trueKeyword.ustring());
if (isFalse())
return jsNontrivialString(exec, exec->propertyNames().falseKeyword.ustring());
if (isNull())
return jsNontrivialString(exec, exec->propertyNames().nullKeyword.ustring());
if (isUndefined())
return jsNontrivialString(exec, exec->propertyNames().undefined.ustring());
ASSERT(isCell());
JSValue v = asCell()->toPrimitive(exec, NoPreference);
if (v.isString())
return static_cast<JSString*>(v.asCell());
return jsString(&exec->globalData(), v.toString(exec));
}
} // namespace JSC
......@@ -191,7 +191,7 @@ namespace JSC {
// been set in the ExecState already.
double toNumber(ExecState*) const;
UString toString(ExecState*) const;
UString toPrimitiveString(ExecState*) const;
JSString* toPrimitiveString(ExecState*) const;
JSObject* toObject(ExecState*) const;
JSObject* toObject(ExecState*, JSGlobalObject*) const;
......
......@@ -50,10 +50,10 @@ NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2)
if (p1.isString()) {
return p2.isString()
? jsString(callFrame, asString(p1), asString(p2))
: jsString(callFrame, asString(p1), p2.toString(callFrame));
: jsString(callFrame, asString(p1), jsString(callFrame, p2.toString(callFrame)));
}
if (p2.isString())
return jsString(callFrame, p1.toString(callFrame), asString(p2));
return jsString(callFrame, jsString(callFrame, p1.toString(callFrame)), asString(p2));
return jsNumber(p1.toNumber(callFrame) + p2.toNumber(callFrame));
}
......
......@@ -36,203 +36,87 @@ namespace JSC {
ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
{
unsigned length1 = s1->length();
if (!length1)
return s2;
unsigned length2 = s2->length();
if (!length2)
return s1;
if ((length1 + length2) < length1)
return throwOutOfMemoryError(exec);
JSGlobalData& globalData = exec->globalData();
unsigned fiberCount = s1->fiberCount() + s2->fiberCount();
JSGlobalData* globalData = &exec->globalData();
if (fiberCount <= JSString::s_maxInternalRopeLength)
return JSString::create(*globalData, fiberCount, s1, s2);
JSString::RopeBuilder ropeBuilder(fiberCount);
if (UNLIKELY(ropeBuilder.isOutOfMemory()))
return throwOutOfMemoryError(exec);
ropeBuilder.append(s1);
ropeBuilder.append(s2);
return JSString::create(*globalData, ropeBuilder.release());
}
ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
{
unsigned length1 = u1.length();
unsigned length1 = s1->length();
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 (fiberCount <= JSString::s_maxInternalRopeLength)
return JSString::create(*globalData, fiberCount, u1, s2);
JSString::RopeBuilder ropeBuilder(fiberCount);
if (UNLIKELY(ropeBuilder.isOutOfMemory()))
return throwOutOfMemoryError(exec);
ropeBuilder.append(u1);
ropeBuilder.append(s2);
return JSString::create(*globalData, ropeBuilder.release());
}
ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
{
unsigned length1 = s1->length();
if (!length1)
return jsString(exec, u2);
unsigned length2 = u2.length();
if (!length2)
return s1;
if ((length1 + length2) < length1)
return throwOutOfMemoryError(exec);
unsigned fiberCount = s1->fiberCount() + 1;
JSGlobalData* globalData = &exec->globalData();
if (fiberCount <= JSString::s_maxInternalRopeLength)
return JSString::create(*globalData, fiberCount, s1, u2);
JSString::RopeBuilder ropeBuilder(fiberCount);
if (UNLIKELY(ropeBuilder.isOutOfMemory()))
return throwOutOfMemoryError(exec);
ropeBuilder.append(s1);
ropeBuilder.append(u2);
return JSString::create(*globalData, ropeBuilder.release());
return fixupVPtr(&globalData, JSString::create(globalData, s1, s2));
}
ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2)
ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3)
{
unsigned length1 = u1.length();
if (!length1)
return jsString(exec, u2);
unsigned length2 = u2.length();
if (!length2)
return jsString(exec, u1);
if ((length1 + length2) < length1)
return throwOutOfMemoryError(exec);
JSGlobalData* globalData = &exec->globalData();
return JSString::create(*globalData, u1, u2);
}
ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3)
{
unsigned length1 = u1.length();
unsigned length2 = u2.length();
unsigned length3 = u3.length();
if (!length1)
return jsString(exec, u2, u3);
return jsString(exec, jsString(globalData, u2), jsString(globalData, u3));
if (!length2)
return jsString(exec, u1, u3);
return jsString(exec, jsString(globalData, u1), jsString(globalData, u3));
if (!length3)
return jsString(exec, u1, u2);
return jsString(exec, jsString(globalData, u1), jsString(globalData, u2));
if ((length1 + length2) < length1)
return throwOutOfMemoryError(exec);
if ((length1 + length2 + length3) < length3)
return throwOutOfMemoryError(exec);
JSGlobalData* globalData = &exec->globalData();
return JSString::create(*globalData, u1, u2, u3);
return fixupVPtr(globalData, JSString::create(exec->globalData(), jsString(globalData, u1), jsString(globalData, u2), jsString(globalData, u3)));
}
ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
{
ASSERT(count >= 3);
unsigned fiberCount = 0;
for (unsigned i = 0; i < count; ++i) {
JSValue v = strings[i].jsValue();
if (LIKELY(v.isString()))
fiberCount += asString(v)->fiberCount();
else
++fiberCount;
}
JSGlobalData* globalData = &exec->globalData();
if (fiberCount == 3)
return JSString::create(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
JSString::RopeBuilder ropeBuilder(*globalData);
JSString::RopeBuilder ropeBuilder(fiberCount);
if (UNLIKELY(ropeBuilder.isOutOfMemory()))
return throwOutOfMemoryError(exec);
unsigned length = 0;
bool overflow = false;
unsigned oldLength = 0;
for (unsigned i = 0; i < count; ++i) {
JSValue v = strings[i].jsValue();
if (LIKELY(v.isString()))
if (v.isString())
ropeBuilder.append(asString(v));
else
ropeBuilder.append(v.toString(exec));
ropeBuilder.append(jsString(globalData, v.toString(exec)));
unsigned newLength = ropeBuilder.length();
if (newLength < length)
overflow = true;
length = newLength;
if (ropeBuilder.length() < oldLength) // True for overflow
return throwOutOfMemoryError(exec);
}
if (overflow)
return throwOutOfMemoryError(exec);
return JSString::create(*globalData, ropeBuilder.release());
return ropeBuilder.release();
}
ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue)
{
unsigned fiberCount = 0;
if (LIKELY(thisValue.isString()))
fiberCount += asString(thisValue)->fiberCount();
else
++fiberCount;
for (unsigned i = 0; i < exec->argumentCount(); ++i) {
JSValue v = exec->argument(i);
if (LIKELY(v.isString()))
fiberCount += asString(v)->fiberCount();
else
++fiberCount;
}
JSString::RopeBuilder ropeBuilder(fiberCount);
if (UNLIKELY(ropeBuilder.isOutOfMemory()))
return throwOutOfMemoryError(exec);
JSGlobalData* globalData = &exec->globalData();
JSString::RopeBuilder ropeBuilder(*globalData);
if (LIKELY(thisValue.isString()))
if (thisValue.isString())
ropeBuilder.append(asString(thisValue));
else
ropeBuilder.append(thisValue.toString(exec));
ropeBuilder.append(jsString(globalData, thisValue.toString(exec)));
unsigned length = 0;
bool overflow = false;
unsigned oldLength = 0;
for (unsigned i = 0; i < exec->argumentCount(); ++i) {
JSValue v = exec->argument(i);
if (LIKELY(v.isString()))
if (v.isString())
ropeBuilder.append(asString(v));
else
ropeBuilder.append(v.toString(exec));
ropeBuilder.append(jsString(globalData, v.toString(exec)));
unsigned newLength = ropeBuilder.length();
if (newLength < length)
overflow = true;
length = newLength;
if (ropeBuilder.length() < oldLength) // True for overflow
return throwOutOfMemoryError(exec);
}
if (overflow)
return throwOutOfMemoryError(exec);
JSGlobalData* globalData = &exec->globalData();
return JSString::create(*globalData, ropeBuilder.release());
return ropeBuilder.release();
}
// ECMA 11.9.3
......
......@@ -106,7 +106,7 @@ unsigned SmallStrings::count() const
void SmallStrings::createEmptyString(JSGlobalData* globalData)
{
ASSERT(!m_emptyString);
m_emptyString = JSString::createHasOtherOwner(*globalData, "");
m_emptyString = JSString::createHasOtherOwner(*globalData, UString("").impl());
}
void SmallStrings::createSingleCharacterString(JSGlobalData* globalData, unsigned char character)
......
......@@ -98,7 +98,11 @@ static EncodedJSValue JSC_HOST_CALL constructWithStringConstructor(ExecState* ex
JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject();
if (!exec->argumentCount())