Title: [185899] trunk/Source
Revision
185899
Author
da...@apple.com
Date
2015-06-23 19:33:18 -0700 (Tue, 23 Jun 2015)

Log Message

Make Array.join work directly on substrings without reifying them
https://bugs.webkit.org/show_bug.cgi?id=146191

Reviewed by Andreas Kling.

Source/_javascript_Core:

Besides the Array.join change, this has other optimizations based on
profiling the Peacekeeper array benchmark.

I measured a 14% speed improvement in the Peacekeeper array benchmark.

Still a lot of low hanging fruit in that test because so many of functions
on the array prototype are not optimizing for simple cases. For example,
the reverse function does individual get and put calls even when the array
is entirely made up of integers in contiguous storage.

* runtime/ArrayPrototype.cpp:
(JSC::getProperty): Use tryGetIndexQuickly first before getPropertySlot.
(JSC::argumentClampedIndexFromStartOrEnd): Marked inline.
(JSC::shift): Use the getProperty helper in this file instead of using
getPropertySlot. Use putByIndexInline instead of calling putByIndex directly.
In both cases this can yield a faster code path.
(JSC::unshift): Ditto.
(JSC::arrayProtoFuncToString): Updated to use the new JSStringJoiner
interface. Changed local variable name to thisArray since it's not a
JSObject*. Changed loop index to i instead of k.
(JSC::arrayProtoFuncToLocaleString): Updated to use the new JSStringJoiner
interface. Renamed thisObj to thisObject. Added a missing exception check
after the toLocaleString function is called, but before toString is called
the result of that function.
(JSC::arrayProtoFuncJoin): Updated to use the new JSStringJointer interface.
Added a missing exception check after calling toString on the separator
but before calling get to get the first element in the array-like object
being joined. Changed loop index to i instead of k. Added missing exception
check after calling toString on each string from the array before calling
get for the next element.
(JSC::arrayProtoFuncConcat): Use JSArray::length instead of using the
getLength function.
(JSC::arrayProtoFuncReverse): Ditto. Also use putByIndexInline.
(JSC::arrayProtoFuncShift): Ditto.
(JSC::arrayProtoFuncSplice): Use getIndex instead of get, which includes some
additional optimizations.
(JSC::getOrHole): Deleted. Unused function.
(JSC::arrayProtoFuncUnShift): Use putByIndexInline.

* runtime/ExceptionHelpers.cpp:
(JSC::errorDescriptionForValue): Removed the duplicate copy of the the logic
from JSValue::toString.

* runtime/JSCJSValue.cpp:
(JSC::JSValue::toStringSlowCase): Improved the performance when converting a
small integer to a single character string.
(JSC::JSValue::toWTFStringSlowCase): Moved the contents of the
inlineJSValueNotStringtoString function here.
* runtime/JSCJSValue.h: Removed no longer used toWTFStringInline and fixed
a comment with a typo.

* runtime/JSObject.h:
(JSC::JSObject::putByIndexInline): Marked ALWAYS_INLINE because this was not
getting inlined at some call sites.
(JSC::JSObject::indexingData): Deleted. Unused function.
(JSC::JSObject::currentIndexingData): Deleted. Unused function.
(JSC::JSObject::getHolyIndexQuickly): Deleted. Unused function.
(JSC::JSObject::relevantLength): Deleted. Unused function.
(JSC::JSObject::currentRelevantLength): Deleted. Unused function.

* runtime/JSString.h: Added the StringViewWithUnderlyingString struct and
the viewWithUnderlyingString function. Removed the inlineJSValueNotStringtoString
and toWTFStringInline functions.

* runtime/JSStringJoiner.cpp:
(JSC::appendStringToData): Changed this to be a template instead of writing
it out, since StringView::getCharactersWithUpconvert does almsot exactly what
this function was trying to do.
(JSC::joinStrings): Rewrote this to use StringView.
(JSC::JSStringJoiner::joinedLength): Added. Factored out from the join function.
(JSC::JSStringJoiner::join): Rewrote to make it a bit simpler. Added an assertion
that we entirely filled capacity, since we are now reserving capacity and using
uncheckedAppend. Use String instead of RefPtr<StringImpl> because there was no
particular value to using the impl directly.

* runtime/JSStringJoiner.h: Changed the interface to the class to use StringView.
Also changed this class so it now has the responsibility to convert each JSValue
into a string. This let us share more code between toString and join, and also
lets us use the new viewWithUnderlyingString function, which could be confusing at
all the call sites, but is easier to understand here.

Source/WTF:

* wtf/Vector.h: Added an overload of uncheckedAppend like the one we added
a while back, a non-template function that forwards through to the function
template. This lets us call uncheckedAppend on an argument list and have it
properly convert it to the Vector's element type.

* wtf/text/StringView.h:
(WTF::StringView::getCharactersWithUpconvert): Changed to not use memcpy;
saw some indication the hand-written loop was faster when profiling. Also
use m_length directly when we know we are dealing with an 8-bit string,
since the masking that the index function does is not needed in that case.
(WTF::StringView::UpconvertedCharacters::UpconvertedCharacters): Ditto.
(WTF::StringView::toString): Ditto.
(WTF::StringView::toFloat): Ditto.
(WTF::StringView::toInt): Ditto.
(WTF::StringView::toStringWithoutCopying): Ditto.
(WTF::StringView::find): Ditto.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (185898 => 185899)


--- trunk/Source/_javascript_Core/ChangeLog	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,3 +1,91 @@
+2015-06-22  Darin Adler  <da...@apple.com>
+
+        Make Array.join work directly on substrings without reifying them
+        https://bugs.webkit.org/show_bug.cgi?id=146191
+
+        Reviewed by Andreas Kling.
+
+        Besides the Array.join change, this has other optimizations based on
+        profiling the Peacekeeper array benchmark.
+
+        I measured a 14% speed improvement in the Peacekeeper array benchmark.
+
+        Still a lot of low hanging fruit in that test because so many of functions
+        on the array prototype are not optimizing for simple cases. For example,
+        the reverse function does individual get and put calls even when the array
+        is entirely made up of integers in contiguous storage.
+
+        * runtime/ArrayPrototype.cpp:
+        (JSC::getProperty): Use tryGetIndexQuickly first before getPropertySlot.
+        (JSC::argumentClampedIndexFromStartOrEnd): Marked inline.
+        (JSC::shift): Use the getProperty helper in this file instead of using
+        getPropertySlot. Use putByIndexInline instead of calling putByIndex directly.
+        In both cases this can yield a faster code path.
+        (JSC::unshift): Ditto.
+        (JSC::arrayProtoFuncToString): Updated to use the new JSStringJoiner
+        interface. Changed local variable name to thisArray since it's not a
+        JSObject*. Changed loop index to i instead of k.
+        (JSC::arrayProtoFuncToLocaleString): Updated to use the new JSStringJoiner
+        interface. Renamed thisObj to thisObject. Added a missing exception check
+        after the toLocaleString function is called, but before toString is called
+        the result of that function.
+        (JSC::arrayProtoFuncJoin): Updated to use the new JSStringJointer interface.
+        Added a missing exception check after calling toString on the separator
+        but before calling get to get the first element in the array-like object
+        being joined. Changed loop index to i instead of k. Added missing exception
+        check after calling toString on each string from the array before calling
+        get for the next element.
+        (JSC::arrayProtoFuncConcat): Use JSArray::length instead of using the
+        getLength function.
+        (JSC::arrayProtoFuncReverse): Ditto. Also use putByIndexInline.
+        (JSC::arrayProtoFuncShift): Ditto.
+        (JSC::arrayProtoFuncSplice): Use getIndex instead of get, which includes some
+        additional optimizations.
+        (JSC::getOrHole): Deleted. Unused function.
+        (JSC::arrayProtoFuncUnShift): Use putByIndexInline.
+
+        * runtime/ExceptionHelpers.cpp:
+        (JSC::errorDescriptionForValue): Removed the duplicate copy of the the logic
+        from JSValue::toString.
+
+        * runtime/JSCJSValue.cpp:
+        (JSC::JSValue::toStringSlowCase): Improved the performance when converting a
+        small integer to a single character string.
+        (JSC::JSValue::toWTFStringSlowCase): Moved the contents of the
+        inlineJSValueNotStringtoString function here.
+        * runtime/JSCJSValue.h: Removed no longer used toWTFStringInline and fixed
+        a comment with a typo.
+
+        * runtime/JSObject.h:
+        (JSC::JSObject::putByIndexInline): Marked ALWAYS_INLINE because this was not
+        getting inlined at some call sites.
+        (JSC::JSObject::indexingData): Deleted. Unused function.
+        (JSC::JSObject::currentIndexingData): Deleted. Unused function.
+        (JSC::JSObject::getHolyIndexQuickly): Deleted. Unused function.
+        (JSC::JSObject::relevantLength): Deleted. Unused function.
+        (JSC::JSObject::currentRelevantLength): Deleted. Unused function.
+
+        * runtime/JSString.h: Added the StringViewWithUnderlyingString struct and
+        the viewWithUnderlyingString function. Removed the inlineJSValueNotStringtoString
+        and toWTFStringInline functions.
+
+        * runtime/JSStringJoiner.cpp:
+        (JSC::appendStringToData): Changed this to be a template instead of writing
+        it out, since StringView::getCharactersWithUpconvert does almsot exactly what
+        this function was trying to do.
+        (JSC::joinStrings): Rewrote this to use StringView.
+        (JSC::JSStringJoiner::joinedLength): Added. Factored out from the join function.
+        (JSC::JSStringJoiner::join): Rewrote to make it a bit simpler. Added an assertion
+        that we entirely filled capacity, since we are now reserving capacity and using
+        uncheckedAppend. Use String instead of RefPtr<StringImpl> because there was no
+        particular value to using the impl directly.
+
+        * runtime/JSStringJoiner.h: Changed the interface to the class to use StringView.
+        Also changed this class so it now has the responsibility to convert each JSValue
+        into a string. This let us share more code between toString and join, and also
+        lets us use the new viewWithUnderlyingString function, which could be confusing at
+        all the call sites, but is easier to understand here.
+
 2015-06-23  Matthew Mirman  <mmir...@apple.com>
 
         Completes native binding descriptors with native getters and potentially setters.

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2015-06-24 02:33:18 UTC (rev 185899)
@@ -138,11 +138,12 @@
 
 // ------------------------------ Array Functions ----------------------------
 
-// Helper function
-static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index)
+static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
 {
-    PropertySlot slot(obj);
-    if (!obj->getPropertySlot(exec, index, slot))
+    if (JSValue result = object->tryGetIndexQuickly(index))
+        return result;
+    PropertySlot slot(object);
+    if (!object->getPropertySlot(exec, index, slot))
         return JSValue();
     return slot.getValue(exec, index);
 }
@@ -160,7 +161,7 @@
     obj->methodTable()->put(obj, exec, exec->propertyNames().length, value, slot);
 }
 
-static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
+static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
 {
     JSValue value = exec->argument(argument);
     if (value.isUndefined())
@@ -174,7 +175,6 @@
     return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
 }
 
-
 // The shift/unshift function implement the shift/unshift behaviour required
 // by the corresponding array prototype methods, and by splice. In both cases,
 // the methods are operating an an array or array like object.
@@ -189,6 +189,7 @@
 // currentCount) will be shifted to the left or right as appropriate; in the
 // case of shift this must be removing values, in the case of unshift this
 // must be introducing new values.
+
 template<JSArray::ShiftCountMode shiftCountMode>
 void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
 {
@@ -207,12 +208,10 @@
     for (unsigned k = header; k < length - currentCount; ++k) {
         unsigned from = k + currentCount;
         unsigned to = k + resultCount;
-        PropertySlot slot(thisObj);
-        if (thisObj->getPropertySlot(exec, from, slot)) {
-            JSValue value = slot.getValue(exec, from);
+        if (JSValue value = getProperty(exec, thisObj, from)) {
             if (exec->hadException())
                 return;
-            thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, to, value, true);
+            thisObj->putByIndexInline(exec, to, value, true);
             if (exec->hadException())
                 return;
         } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
@@ -227,6 +226,7 @@
         }
     }
 }
+
 template<JSArray::ShiftCountMode shiftCountMode>
 void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
 {
@@ -247,16 +247,14 @@
         if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count))
             return;
     }
-    
+
     for (unsigned k = length - currentCount; k > header; --k) {
         unsigned from = k + currentCount - 1;
         unsigned to = k + resultCount - 1;
-        PropertySlot slot(thisObj);
-        if (thisObj->getPropertySlot(exec, from, slot)) {
-            JSValue value = slot.getValue(exec, from);
+        if (JSValue value = getProperty(exec, thisObj, from)) {
             if (exec->hadException())
                 return;
-            thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, to, value, true);
+            thisObj->putByIndexInline(exec, to, value, true);
         } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
             throwTypeError(exec, ASCIILiteral("Unable to delete property."));
             return;
@@ -291,80 +289,75 @@
         return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList()));
 
     ASSERT(isJSArray(thisValue));
-    JSArray* thisObj = asArray(thisValue);
+    JSArray* thisArray = asArray(thisValue);
     
-    unsigned length = getLength(exec, thisObj);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    unsigned length = thisArray->length();
 
-    StringRecursionChecker checker(exec, thisObj);
+    StringRecursionChecker checker(exec, thisArray);
     if (JSValue earlyReturnValue = checker.earlyReturnValue())
         return JSValue::encode(earlyReturnValue);
 
-    String separator(",", String::ConstructFromLiteral);
-    JSStringJoiner stringJoiner(separator, length);
-    for (unsigned k = 0; k < length; k++) {
-        JSValue element;
-        if (thisObj->canGetIndexQuickly(k))
-            element = thisObj->getIndexQuickly(k);
-        else {
-            element = thisObj->get(exec, k);
+    JSStringJoiner joiner(*exec, ',', length);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    for (unsigned i = 0; i < length; ++i) {
+        JSValue element = thisArray->tryGetIndexQuickly(i);
+        if (!element) {
+            element = thisArray->get(exec, i);
             if (exec->hadException())
                 return JSValue::encode(jsUndefined());
         }
-
-        if (element.isUndefinedOrNull())
-            stringJoiner.append(String());
-        else
-            stringJoiner.append(element.toWTFString(exec));
-
+        joiner.append(*exec, element);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
     }
-    return JSValue::encode(stringJoiner.join(exec));
+
+    return JSValue::encode(joiner.join(*exec));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
 
-    JSObject* thisObj = thisValue.toObject(exec);
+    JSObject* thisObject = thisValue.toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
 
-    unsigned length = getLength(exec, thisObj);
+    unsigned length = getLength(exec, thisObject);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
 
-    StringRecursionChecker checker(exec, thisObj);
+    StringRecursionChecker checker(exec, thisObject);
     if (JSValue earlyReturnValue = checker.earlyReturnValue())
         return JSValue::encode(earlyReturnValue);
 
-    String separator(",", String::ConstructFromLiteral);
-    JSStringJoiner stringJoiner(separator, length);
-    for (unsigned k = 0; k < length; k++) {
-        JSValue element = thisObj->get(exec, k);
+    JSStringJoiner stringJoiner(*exec, ',', length);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    for (unsigned i = 0; i < length; ++i) {
+        JSValue element = thisObject->getIndex(exec, i);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
-        if (!element.isUndefinedOrNull()) {
-            JSObject* o = element.toObject(exec);
-            JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
+        if (element.isUndefinedOrNull())
+            continue;
+        JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
+        if (exec->hadException())
+            return JSValue::encode(jsUndefined());
+        CallData callData;
+        CallType callType = getCallData(conversionFunction, callData);
+        if (callType != CallTypeNone) {
+            element = call(exec, conversionFunction, callType, callData, element, exec->emptyList());
             if (exec->hadException())
                 return JSValue::encode(jsUndefined());
-            String str;
-            CallData callData;
-            CallType callType = getCallData(conversionFunction, callData);
-            if (callType != CallTypeNone)
-                str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toWTFString(exec);
-            else
-                str = element.toWTFString(exec);
-            if (exec->hadException())
-                return JSValue::encode(jsUndefined());
-            stringJoiner.append(str);
         }
+        stringJoiner.append(*exec, element);
+        if (exec->hadException())
+            return JSValue::encode(jsUndefined());
     }
 
-    return JSValue::encode(stringJoiner.join(exec));
+    return JSValue::encode(stringJoiner.join(*exec));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
@@ -378,61 +371,56 @@
     if (JSValue earlyReturnValue = checker.earlyReturnValue())
         return JSValue::encode(earlyReturnValue);
 
-    String separator;
-    if (!exec->argument(0).isUndefined())
-        separator = exec->argument(0).toWTFString(exec);
-    if (separator.isNull())
-        separator = String(",", String::ConstructFromLiteral);
+    JSString* separator = nullptr;
+    if (!exec->argument(0).isUndefined()) {
+        separator = exec->argument(0).toString(exec);
+        if (exec->hadException())
+            return JSValue::encode(jsUndefined());
+    }
 
-    JSStringJoiner stringJoiner(separator, length);
+    const LChar comma = ',';
+    JSStringJoiner stringJoiner(*exec, separator ? separator->view(exec) : StringView{ &comma, 1 }, length);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
 
-    unsigned k = 0;
+    unsigned i = 0;
+
     if (isJSArray(thisObj)) {
         JSArray* array = asArray(thisObj);
-
-        for (; k < length; k++) {
-            if (!array->canGetIndexQuickly(k))
-                break;
-
-            JSValue element = array->getIndexQuickly(k);
-            if (!element.isUndefinedOrNull())
-                stringJoiner.append(element.toWTFStringInline(exec));
-            else
-                stringJoiner.append(String());
+        for (; i < length && array->canGetIndexQuickly(i); ++i) {
+            stringJoiner.append(*exec, array->getIndexQuickly(i));
+            if (exec->hadException())
+                return JSValue::encode(jsUndefined());
         }
     }
 
-    for (; k < length; k++) {
-        JSValue element = thisObj->get(exec, k);
+    for (; i < length; ++i) {
+        JSValue element = thisObj->get(exec, i);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
-        if (!element.isUndefinedOrNull())
-            stringJoiner.append(element.toWTFStringInline(exec));
-        else
-            stringJoiner.append(String());
+        stringJoiner.append(*exec, element);
+        if (exec->hadException())
+            return JSValue::encode(jsUndefined());
     }
 
-    return JSValue::encode(stringJoiner.join(exec));
+    return JSValue::encode(stringJoiner.join(*exec));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
-    size_t argCount = exec->argumentCount();
+    unsigned argCount = exec->argumentCount();
     JSValue curArg = thisValue.toObject(exec);
     Checked<unsigned, RecordOverflow> finalArraySize = 0;
 
-    for (size_t i = 0;;) {
-        if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
-            finalArraySize += getLength(exec, currentArray);
-            if (exec->hadException())
-                return JSValue::encode(jsUndefined());
-        } else
-            finalArraySize++;
+    for (unsigned i = 0; ; ++i) {
+        if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg))
+            finalArraySize += currentArray->length();
+        else
+            ++finalArraySize;
         if (i == argCount)
             break;
         curArg = exec->uncheckedArgument(i);
-        ++i;
     }
 
     if (finalArraySize.hasOverflowed())
@@ -444,9 +432,9 @@
 
     curArg = thisValue.toObject(exec);
     unsigned n = 0;
-    for (size_t i = 0;;) {
+    for (unsigned i = 0; ; ++i) {
         if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
-            unsigned length = getLength(exec, currentArray);
+            unsigned length = currentArray->length();
             if (exec->hadException())
                 return JSValue::encode(jsUndefined());
             for (unsigned k = 0; k < length; ++k) {
@@ -464,7 +452,6 @@
         if (i == argCount)
             break;
         curArg = exec->uncheckedArgument(i);
-        ++i;
     }
     arr->setLength(exec, n);
     return JSValue::encode(arr);
@@ -550,7 +537,7 @@
             return JSValue::encode(jsUndefined());
 
         if (obj2) {
-            thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, k, obj2, true);
+            thisObj->putByIndexInline(exec, k, obj2, true);
             if (exec->hadException())
                 return JSValue::encode(jsUndefined());
         } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, k)) {
@@ -559,7 +546,7 @@
         }
 
         if (obj) {
-            thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, lk1, obj, true);
+            thisObj->putByIndexInline(exec, lk1, obj, true);
             if (exec->hadException())
                 return JSValue::encode(jsUndefined());
         } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, lk1)) {
@@ -582,7 +569,7 @@
         putLength(exec, thisObj, jsNumber(length));
         result = jsUndefined();
     } else {
-        result = thisObj->get(exec, 0);
+        result = thisObj->getIndex(exec, 0);
         shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
@@ -621,15 +608,6 @@
     return JSValue::encode(result);
 }
 
-inline JSValue getOrHole(JSObject* obj, ExecState* exec, unsigned propertyName)
-{
-    PropertySlot slot(obj);
-    if (obj->getPropertySlot(exec, propertyName, slot))
-        return slot.getValue(exec, propertyName);
-
-    return JSValue();
-}
-
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
 {
     // 15.4.4.12
@@ -686,7 +664,7 @@
             return JSValue::encode(jsUndefined());
     }
     for (unsigned k = 0; k < additionalArgs; ++k) {
-        thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, k + begin, exec->uncheckedArgument(k + 2), true);
+        thisObj->putByIndexInline(exec, k + begin, exec->uncheckedArgument(k + 2), true);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
     }
@@ -711,7 +689,7 @@
             return JSValue::encode(jsUndefined());
     }
     for (unsigned k = 0; k < nrArgs; ++k) {
-        thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, k, exec->uncheckedArgument(k), true);
+        thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
     }

Modified: trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp	2015-06-24 02:33:18 UTC (rev 185899)
@@ -82,32 +82,16 @@
     
 JSString* errorDescriptionForValue(ExecState* exec, JSValue v)
 {
-    VM& vm = exec->vm();
-    if (v.isNull())
-        return vm.smallStrings.nullString();
-    if (v.isUndefined())
-        return vm.smallStrings.undefinedString();
-    if (v.isInt32())
-        return jsString(&vm, vm.numericStrings.add(v.asInt32()));
-    if (v.isDouble())
-        return jsString(&vm, vm.numericStrings.add(v.asDouble()));
-    if (v.isTrue())
-        return vm.smallStrings.trueString();
-    if (v.isFalse())
-        return vm.smallStrings.falseString();
     if (v.isString())
-        return jsNontrivialString(&vm, makeString('"',  asString(v)->value(exec), '"'));
+        return jsNontrivialString(exec, makeString('"',  asString(v)->value(exec), '"'));
     if (v.isObject()) {
         CallData callData;
         JSObject* object = asObject(v);
         if (object->methodTable()->getCallData(object, callData) != CallTypeNone)
-            return vm.smallStrings.functionString();
+            return exec->vm().smallStrings.functionString();
         return jsString(exec, JSObject::calculatedClassName(object));
     }
-    
-    // The JSValue should never be empty, so this point in the code should never be reached.
-    ASSERT_NOT_REACHED();
-    return vm.smallStrings.emptyString();
+    return v.toString(exec);
 }
     
 static String defaultApproximateSourceError(const String& originalMessage, const String& sourceText)

Modified: trunk/Source/_javascript_Core/runtime/JSCJSValue.cpp (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/JSCJSValue.cpp	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSCJSValue.cpp	2015-06-24 02:33:18 UTC (rev 185899)
@@ -353,8 +353,12 @@
 {
     VM& vm = exec->vm();
     ASSERT(!isString());
-    if (isInt32())
-        return jsString(&vm, vm.numericStrings.add(asInt32()));
+    if (isInt32()) {
+        auto integer = asInt32();
+        if (static_cast<unsigned>(integer) <= 9)
+            return vm.smallStrings.singleCharacterString(integer + '0');
+        return jsNontrivialString(&vm, vm.numericStrings.add(integer));
+    }
     if (isDouble())
         return jsString(&vm, vm.numericStrings.add(asDouble()));
     if (isTrue())
@@ -380,7 +384,20 @@
 
 String JSValue::toWTFStringSlowCase(ExecState* exec) const
 {
-    return inlineJSValueNotStringtoString(*this, exec);
+    VM& vm = exec->vm();
+    if (isInt32())
+        return vm.numericStrings.add(asInt32());
+    if (isDouble())
+        return vm.numericStrings.add(asDouble());
+    if (isTrue())
+        return vm.propertyNames->trueKeyword.string();
+    if (isFalse())
+        return vm.propertyNames->falseKeyword.string();
+    if (isNull())
+        return vm.propertyNames->nullKeyword.string();
+    if (isUndefined())
+        return vm.propertyNames->undefinedKeyword.string();
+    return toString(exec)->value(exec);
 }
 
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/JSCJSValue.h (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/JSCJSValue.h	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSCJSValue.h	2015-06-24 02:33:18 UTC (rev 185899)
@@ -252,7 +252,6 @@
     JSString* toString(ExecState*) const;
     Identifier toPropertyKey(ExecState*) const;
     WTF::String toWTFString(ExecState*) const;
-    WTF::String toWTFStringInline(ExecState*) const;
     JSObject* toObject(ExecState*) const;
     JSObject* toObject(ExecState*, JSGlobalObject*) const;
 
@@ -262,8 +261,8 @@
     int32_t toInt32(ExecState*) const;
     uint32_t toUInt32(ExecState*) const;
 
-    // Floating point conversions (this is a convenience method for webcore;
-    // signle precision float is not a representation used in JS or JSC).
+    // Floating point conversions (this is a convenience function for WebCore;
+    // single precision float is not a representation used in JS or JSC).
     float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); }
 
     // Object operations, with the toObject operation included.

Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/JSObject.h	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h	2015-06-24 02:33:18 UTC (rev 185899)
@@ -145,7 +145,7 @@
     JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
     JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
         
-    void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
+    ALWAYS_INLINE void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
     {
         if (canSetIndexQuickly(propertyName)) {
             setIndexQuickly(exec->vm(), propertyName, value);
@@ -837,103 +837,6 @@
     // This is relevant to undecided, int32, double, and contiguous.
     unsigned countElements();
         
-    // This strange method returns a pointer to the start of the indexed data
-    // as if it contained JSValues. But it won't always contain JSValues.
-    // Make sure you cast this to the appropriate type before using.
-    template<IndexingType indexingType>
-    ContiguousJSValues indexingData()
-    {
-        switch (indexingType) {
-        case ALL_INT32_INDEXING_TYPES:
-        case ALL_DOUBLE_INDEXING_TYPES:
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
-            return m_butterfly->contiguous();
-                
-        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
-            return m_butterfly->arrayStorage()->vector();
-
-        default:
-            CRASH();
-            return ContiguousJSValues();
-        }
-    }
-
-    ContiguousJSValues currentIndexingData()
-    {
-        switch (indexingType()) {
-        case ALL_INT32_INDEXING_TYPES:
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
-            return m_butterfly->contiguous();
-
-        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
-            return m_butterfly->arrayStorage()->vector();
-
-        default:
-            CRASH();
-            return ContiguousJSValues();
-        }
-    }
-        
-    JSValue getHolyIndexQuickly(unsigned i)
-    {
-        ASSERT(i < m_butterfly->vectorLength());
-        switch (indexingType()) {
-        case ALL_INT32_INDEXING_TYPES:
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
-            return m_butterfly->contiguous()[i].get();
-        case ALL_DOUBLE_INDEXING_TYPES: {
-            double value = m_butterfly->contiguousDouble()[i];
-            if (value == value)
-                return JSValue(JSValue::EncodeAsDouble, value);
-            return JSValue();
-        }
-        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
-            return m_butterfly->arrayStorage()->m_vector[i].get();
-        default:
-            CRASH();
-            return JSValue();
-        }
-    }
-        
-    template<IndexingType indexingType>
-    unsigned relevantLength()
-    {
-        switch (indexingType) {
-        case ALL_INT32_INDEXING_TYPES:
-        case ALL_DOUBLE_INDEXING_TYPES:
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
-            return m_butterfly->publicLength();
-                
-        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
-            return std::min(
-                m_butterfly->arrayStorage()->length(),
-                m_butterfly->arrayStorage()->vectorLength());
-                
-        default:
-            CRASH();
-            return 0;
-        }
-    }
-
-    unsigned currentRelevantLength()
-    {
-        switch (indexingType()) {
-        case ALL_INT32_INDEXING_TYPES:
-        case ALL_DOUBLE_INDEXING_TYPES:
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
-            return m_butterfly->publicLength();
-
-        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
-            return std::min(
-                m_butterfly->arrayStorage()->length(),
-                m_butterfly->arrayStorage()->vectorLength());
-
-        default:
-            CRASH();
-            return 0;
-        }
-    }
-
 private:
     friend class LLIntOffsetsExtractor;
         

Modified: trunk/Source/_javascript_Core/runtime/JSString.h (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/JSString.h	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSString.h	2015-06-24 02:33:18 UTC (rev 185899)
@@ -62,6 +62,11 @@
 
 JSRopeString* jsStringBuilder(VM*);
 
+struct StringViewWithUnderlyingString {
+    StringView view;
+    String underlyingString;
+};
+
 class JSString : public JSCell {
 public:
     friend class JIT;
@@ -144,6 +149,7 @@
     AtomicString toAtomicString(ExecState*) const;
     RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const;
     StringView view(ExecState*) const;
+    StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
     const String& value(ExecState*) const;
     const String& tryGetValue() const;
     const StringImpl* tryGetValueImpl() const;
@@ -384,6 +390,7 @@
     void resolveRopeInternal16NoSubstring(UChar*) const;
     void clearFibers() const;
     StringView view(ExecState*) const;
+    StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
 
     WriteBarrierBase<JSString>& fiber(unsigned i) const
     {
@@ -673,32 +680,6 @@
     return toWTFStringSlowCase(exec);
 }
 
-ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
-{
-    VM& vm = exec->vm();
-    if (value.isInt32())
-        return vm.numericStrings.add(value.asInt32());
-    if (value.isDouble())
-        return vm.numericStrings.add(value.asDouble());
-    if (value.isTrue())
-        return vm.propertyNames->trueKeyword.string();
-    if (value.isFalse())
-        return vm.propertyNames->falseKeyword.string();
-    if (value.isNull())
-        return vm.propertyNames->nullKeyword.string();
-    if (value.isUndefined())
-        return vm.propertyNames->undefinedKeyword.string();
-    return value.toString(exec)->value(exec);
-}
-
-ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
-{
-    if (isString())
-        return static_cast<JSString*>(asCell())->value(exec);
-
-    return inlineJSValueNotStringtoString(*this, exec);
-}
-
 ALWAYS_INLINE StringView JSRopeString::view(ExecState* exec) const
 {
     if (isSubstring()) {
@@ -710,6 +691,18 @@
     return StringView(m_value);
 }
 
+ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState& state) const
+{
+    if (isSubstring()) {
+        auto& base = substringBase()->m_value;
+        if (is8Bit())
+            return { { base.characters8() + substringOffset(), m_length }, base };
+        return { { base.characters16() + substringOffset(), m_length }, base };
+    }
+    resolveRope(&state);
+    return { m_value, m_value };
+}
+
 ALWAYS_INLINE StringView JSString::view(ExecState* exec) const
 {
     if (isRope())
@@ -717,6 +710,13 @@
     return StringView(m_value);
 }
 
+ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState& state) const
+{
+    if (isRope())
+        return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(state);
+    return { m_value, m_value };
+}
+
 inline bool JSString::isSubstring() const
 {
     return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();

Modified: trunk/Source/_javascript_Core/runtime/JSStringJoiner.cpp (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/JSStringJoiner.cpp	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSStringJoiner.cpp	2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,105 +26,94 @@
 #include "config.h"
 #include "JSStringJoiner.h"
 
-#include "ExceptionHelpers.h"
-#include "JSScope.h"
-#include "JSString.h"
 #include "JSCInlines.h"
-#include <wtf/text/StringImpl.h>
 
 namespace JSC {
 
-// The destination is 16bits, at least one string is 16 bits.
-static inline void appendStringToData(UChar*& data, const String& string)
+template<typename CharacterType>
+static inline void appendStringToData(CharacterType*& data, StringView string)
 {
-    if (string.isNull())
-        return;
-
-    unsigned length = string.length();
-    const StringImpl* stringImpl = string.impl();
-
-    if (stringImpl->is8Bit()) {
-        for (unsigned i = 0; i < length; ++i) {
-            *data = ""
-            ++data;
-        }
-    } else {
-        for (unsigned i = 0; i < length; ++i) {
-            *data = ""
-            ++data;
-        }
-    }
+    string.getCharactersWithUpconvert(data);
+    data += string.length();
 }
 
-// If the destination is 8bits, we know every string has to be 8bit.
-static inline void appendStringToData(LChar*& data, const String& string)
+template<typename CharacterType>
+static inline String joinStrings(const Vector<StringViewWithUnderlyingString>& strings, StringView separator, unsigned joinedLength)
 {
-    if (string.isNull())
-        return;
-    ASSERT(string.is8Bit());
+    ASSERT(joinedLength);
 
-    unsigned length = string.length();
-    const StringImpl* stringImpl = string.impl();
+    CharacterType* data;
+    String result = StringImpl::tryCreateUninitialized(joinedLength, data);
+    if (result.isNull())
+        return result;
 
-    for (unsigned i = 0; i < length; ++i) {
-        *data = ""
-        ++data;
+    appendStringToData(data, strings[0].view);
+
+    unsigned size = strings.size();
+
+    switch (separator.length()) {
+    case 0:
+        for (unsigned i = 1; i < size; ++i)
+            appendStringToData(data, strings[i].view);
+        break;
+    case 1: {
+        CharacterType separatorCharacter = separator[0];
+        for (unsigned i = 1; i < size; ++i) {
+            *data++ = separatorCharacter;
+            appendStringToData(data, strings[i].view);
+        }
+        break;
     }
+    default:
+        for (unsigned i = 1; i < size; ++i) {
+            appendStringToData(data, separator);
+            appendStringToData(data, strings[i].view);
+        }
+    }
+    ASSERT(data == result.characters<CharacterType>() + joinedLength);
+
+    return result;
 }
 
-template<typename CharacterType>
-static inline PassRefPtr<StringImpl> joinStrings(const Vector<String>& strings, const String& separator, unsigned outputLength)
+inline unsigned JSStringJoiner::joinedLength(ExecState& state) const
 {
-    ASSERT(outputLength);
+    unsigned numberOfStrings = m_strings.size();
+    if (!numberOfStrings)
+        return 0;
 
-    CharacterType* data;
-    RefPtr<StringImpl> outputStringImpl = StringImpl::tryCreateUninitialized(outputLength, data);
-    if (!outputStringImpl)
-        return nullptr;
+    Checked<unsigned, RecordOverflow> separatorLength = m_separator.length();
+    Checked<unsigned, RecordOverflow> totalSeparatorsLength = separatorLength * (numberOfStrings - 1);
+    Checked<unsigned, RecordOverflow> totalLength = totalSeparatorsLength + m_accumulatedStringsLength;
 
-    const String firstString = strings.first();
-    appendStringToData(data, firstString);
-
-    for (size_t i = 1; i < strings.size(); ++i) {
-        appendStringToData(data, separator);
-        appendStringToData(data, strings[i]);
+    unsigned result;
+    if (totalLength.safeGet(result) == CheckedState::DidOverflow) {
+        throwOutOfMemoryError(&state);
+        return 0;
     }
-
-    ASSERT(data == (outputStringImpl->characters<CharacterType>() + outputStringImpl->length()));
-    return outputStringImpl.release();
+    return result;
 }
 
-JSValue JSStringJoiner::join(ExecState* exec)
+JSValue JSStringJoiner::join(ExecState& state)
 {
-    if (!m_isValid)
-        return throwOutOfMemoryError(exec);
+    ASSERT(m_strings.size() == m_strings.capacity());
 
-    if (!m_strings.size())
-        return jsEmptyString(exec);
+    unsigned length = joinedLength(state);
+    if (state.hadException())
+        return jsUndefined();
 
-    Checked<unsigned, RecordOverflow> separatorLength = m_separator.length();
-    // FIXME: add special cases of joinStrings() for (separatorLength == 0) and (separatorLength == 1).
-    ASSERT(m_strings.size() > 0);
-    Checked<unsigned, RecordOverflow> totalSeparactorsLength = separatorLength * (m_strings.size() - 1);
-    Checked<unsigned, RecordOverflow> outputStringSize = totalSeparactorsLength + m_accumulatedStringsLength;
+    if (!length)
+        return jsEmptyString(&state);
 
-    unsigned finalSize;
-    if (outputStringSize.safeGet(finalSize) == CheckedState::DidOverflow)
-        return throwOutOfMemoryError(exec);
-        
-    if (!outputStringSize)
-        return jsEmptyString(exec);
-
-    RefPtr<StringImpl> outputStringImpl;
-    if (m_is8Bits)
-        outputStringImpl = joinStrings<LChar>(m_strings, m_separator, finalSize);
+    String result;
+    if (m_isAll8Bit)
+        result = joinStrings<LChar>(m_strings, m_separator, length);
     else
-        outputStringImpl = joinStrings<UChar>(m_strings, m_separator, finalSize);
+        result = joinStrings<UChar>(m_strings, m_separator, length);
 
-    if (!outputStringImpl)
-        return throwOutOfMemoryError(exec);
+    if (result.isNull())
+        return throwOutOfMemoryError(&state);
 
-    return JSString::create(exec->vm(), outputStringImpl.release());
+    return jsString(&state, WTF::move(result));
 }
 
 }

Modified: trunk/Source/_javascript_Core/runtime/JSStringJoiner.h (185898 => 185899)


--- trunk/Source/_javascript_Core/runtime/JSStringJoiner.h	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSStringJoiner.h	2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,50 +26,112 @@
 #ifndef JSStringJoiner_h
 #define JSStringJoiner_h
 
+#include "ExceptionHelpers.h"
 #include "JSCJSValue.h"
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
 
 namespace JSC {
 
-class ExecState;
-
-
 class JSStringJoiner {
 public:
-    JSStringJoiner(const String& separator, size_t stringCount);
+    JSStringJoiner(ExecState&, LChar separator, unsigned stringCount);
+    JSStringJoiner(ExecState&, StringView separator, unsigned stringCount);
 
-    void append(const String&);
-    JSValue join(ExecState*);
+    void append(ExecState&, JSValue);
 
+    JSValue join(ExecState&);
+
 private:
-    String m_separator;
-    Vector<String> m_strings;
+    void append(StringViewWithUnderlyingString&&);
+    void append8Bit(const String&);
+    void appendLiteral(const Identifier&);
+    void appendEmptyString();
+    unsigned joinedLength(ExecState&) const;
 
+    LChar m_singleCharacterSeparator;
+    StringView m_separator;
+    Vector<StringViewWithUnderlyingString> m_strings;
     Checked<unsigned, RecordOverflow> m_accumulatedStringsLength;
-    bool m_isValid;
-    bool m_is8Bits;
+    bool m_isAll8Bit { true };
 };
 
-inline JSStringJoiner::JSStringJoiner(const String& separator, size_t stringCount)
+inline JSStringJoiner::JSStringJoiner(ExecState& state, StringView separator, unsigned stringCount)
     : m_separator(separator)
-    , m_isValid(true)
-    , m_is8Bits(m_separator.is8Bit())
+    , m_isAll8Bit(m_separator.is8Bit())
 {
-    ASSERT(!m_separator.isNull());
-    m_isValid = m_strings.tryReserveCapacity(stringCount);
+    if (!m_strings.tryReserveCapacity(stringCount))
+        throwOutOfMemoryError(&state);
 }
 
-inline void JSStringJoiner::append(const String& str)
+inline JSStringJoiner::JSStringJoiner(ExecState& state, LChar separator, unsigned stringCount)
+    : m_singleCharacterSeparator(separator)
+    , m_separator { &m_singleCharacterSeparator, 1 }
 {
-    if (!m_isValid)
+    if (!m_strings.tryReserveCapacity(stringCount))
+        throwOutOfMemoryError(&state);
+}
+
+ALWAYS_INLINE void JSStringJoiner::append(StringViewWithUnderlyingString&& string)
+{
+    m_accumulatedStringsLength += string.view.length();
+    m_isAll8Bit = m_isAll8Bit && string.view.is8Bit();
+    m_strings.uncheckedAppend(WTF::move(string));
+}
+
+ALWAYS_INLINE void JSStringJoiner::append8Bit(const String& string)
+{
+    ASSERT(string.is8Bit());
+    m_accumulatedStringsLength += string.length();
+    m_strings.uncheckedAppend({ string, string });
+}
+
+ALWAYS_INLINE void JSStringJoiner::appendLiteral(const Identifier& literal)
+{
+    m_accumulatedStringsLength += literal.length();
+    ASSERT(literal.string().is8Bit());
+    m_strings.uncheckedAppend({ literal.string(), { } });
+}
+
+ALWAYS_INLINE void JSStringJoiner::appendEmptyString()
+{
+    m_strings.uncheckedAppend({ { }, { } });
+}
+
+ALWAYS_INLINE void JSStringJoiner::append(ExecState& state, JSValue value)
+{
+    // The following code differs from using the result of JSValue::toString in the following ways:
+    // 1) It's inlined more than JSValue::toString is.
+    // 2) It includes conversion to WTF::String in a way that avoids allocating copies of substrings.
+    // 3) It doesn't create a JSString for numbers, true, or false.
+    // 4) It turns undefined and null into the empty string instead of "undefined" and "null".
+    // 5) It uses optimized code paths for all the cases known to be 8-bit and for the empty string.
+
+    if (value.isCell()) {
+        if (value.asCell()->isString()) {
+            append(asString(value)->viewWithUnderlyingString(state));
+            return;
+        }
+        append(value.toString(&state)->viewWithUnderlyingString(state));
         return;
+    }
 
-    m_strings.append(str);
-    if (!str.isNull()) {
-        m_accumulatedStringsLength += str.length();
-        m_is8Bits = m_is8Bits && str.is8Bit();
+    if (value.isInt32()) {
+        append8Bit(state.vm().numericStrings.add(value.asInt32()));
+        return;
     }
+    if (value.isDouble()) {
+        append8Bit(state.vm().numericStrings.add(value.asDouble()));
+        return;
+    }
+    if (value.isTrue()) {
+        append8Bit(state.vm().propertyNames->trueKeyword.string());
+        return;
+    }
+    if (value.isFalse()) {
+        append8Bit(state.vm().propertyNames->falseKeyword.string());
+        return;
+    }
+    ASSERT(value.isUndefinedOrNull());
+    appendEmptyString();
 }
 
 }

Modified: trunk/Source/WTF/ChangeLog (185898 => 185899)


--- trunk/Source/WTF/ChangeLog	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/WTF/ChangeLog	2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,3 +1,27 @@
+2015-06-22  Darin Adler  <da...@apple.com>
+
+        Make Array.join work directly on substrings without reifying them
+        https://bugs.webkit.org/show_bug.cgi?id=146191
+
+        Reviewed by Andreas Kling.
+
+        * wtf/Vector.h: Added an overload of uncheckedAppend like the one we added
+        a while back, a non-template function that forwards through to the function
+        template. This lets us call uncheckedAppend on an argument list and have it
+        properly convert it to the Vector's element type.
+
+        * wtf/text/StringView.h:
+        (WTF::StringView::getCharactersWithUpconvert): Changed to not use memcpy;
+        saw some indication the hand-written loop was faster when profiling. Also
+        use m_length directly when we know we are dealing with an 8-bit string,
+        since the masking that the index function does is not needed in that case.
+        (WTF::StringView::UpconvertedCharacters::UpconvertedCharacters): Ditto.
+        (WTF::StringView::toString): Ditto.
+        (WTF::StringView::toFloat): Ditto.
+        (WTF::StringView::toInt): Ditto.
+        (WTF::StringView::toStringWithoutCopying): Ditto.
+        (WTF::StringView::find): Ditto.
+
 2015-06-22  YunQiang Su  <wzss...@gmail.com>
 
         [WTF] Platform.h: use _ABI64 instead of _MIPS_SIM_ABI64 to determine MIPS N64

Modified: trunk/Source/WTF/wtf/Vector.h (185898 => 185899)


--- trunk/Source/WTF/wtf/Vector.h	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/WTF/wtf/Vector.h	2015-06-24 02:33:18 UTC (rev 185899)
@@ -724,9 +724,12 @@
     void clear() { shrinkCapacity(0); }
 
     void append(ValueType&& value) { append<ValueType>(std::forward<ValueType>(value)); }
+    template<typename U> void append(U&&);
+
+    void uncheckedAppend(ValueType&& value) { uncheckedAppend<ValueType>(std::forward<ValueType>(value)); }
+    template<typename U> void uncheckedAppend(U&&);
+
     template<typename U> void append(const U*, size_t);
-    template<typename U> void append(U&&);
-    template<typename U> void uncheckedAppend(U&& val);
     template<typename U, size_t otherCapacity> void appendVector(const Vector<U, otherCapacity>&);
     template<typename U> bool tryAppend(const U*, size_t);
 

Modified: trunk/Source/WTF/wtf/text/StringView.h (185898 => 185899)


--- trunk/Source/WTF/wtf/text/StringView.h	2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/WTF/wtf/text/StringView.h	2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -409,19 +409,23 @@
 inline void StringView::getCharactersWithUpconvert(LChar* destination) const
 {
     ASSERT(is8Bit());
-    memcpy(destination, characters8(), length());
+    auto characters8 = this->characters8();
+    for (unsigned i = 0; i < m_length; ++i)
+        destination[i] = characters8[i];
 }
 
 inline void StringView::getCharactersWithUpconvert(UChar* destination) const
 {
     if (is8Bit()) {
-        const LChar* characters8 = this->characters8();
-        unsigned length = this->length();
-        for (unsigned i = 0; i < length; ++i)
+        auto characters8 = this->characters8();
+        for (unsigned i = 0; i < m_length; ++i)
             destination[i] = characters8[i];
         return;
     }
-    memcpy(destination, characters16(), length() * sizeof(UChar));
+    auto characters16 = this->characters16();
+    unsigned length = this->length();
+    for (unsigned i = 0; i < length; ++i)
+        destination[i] = characters16[i];
 }
 
 inline StringView::UpconvertedCharacters::UpconvertedCharacters(const StringView& string)
@@ -431,7 +435,7 @@
         return;
     }
     const LChar* characters8 = string.characters8();
-    unsigned length = string.length();
+    unsigned length = string.m_length;
     m_upconvertedCharacters.reserveInitialCapacity(length);
     for (unsigned i = 0; i < length; ++i)
         m_upconvertedCharacters.uncheckedAppend(characters8[i]);
@@ -441,35 +445,35 @@
 inline String StringView::toString() const
 {
     if (is8Bit())
-        return String(characters8(), length());
+        return String(characters8(), m_length);
     return String(characters16(), length());
 }
 
 inline float StringView::toFloat(bool& isValid) const
 {
     if (is8Bit())
-        return charactersToFloat(characters8(), length(), &isValid);
+        return charactersToFloat(characters8(), m_length, &isValid);
     return charactersToFloat(characters16(), length(), &isValid);
 }
 
 inline int StringView::toInt(bool& isValid) const
 {
     if (is8Bit())
-        return charactersToInt(characters8(), length(), &isValid);
+        return charactersToInt(characters8(), m_length, &isValid);
     return charactersToInt(characters16(), length(), &isValid);
 }
 
 inline String StringView::toStringWithoutCopying() const
 {
     if (is8Bit())
-        return StringImpl::createWithoutCopying(characters8(), length());
+        return StringImpl::createWithoutCopying(characters8(), m_length);
     return StringImpl::createWithoutCopying(characters16(), length());
 }
 
 inline size_t StringView::find(UChar character, unsigned start) const
 {
     if (is8Bit())
-        return WTF::find(characters8(), length(), character, start);
+        return WTF::find(characters8(), m_length, character, start);
     return WTF::find(characters16(), length(), character, start);
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to