Title: [266215] trunk
Revision
266215
Author
[email protected]
Date
2020-08-26 18:55:08 -0700 (Wed, 26 Aug 2020)

Log Message

Merge putLength() into setLength()
https://bugs.webkit.org/show_bug.cgi?id=211279

Reviewed by Darin Adler and Saam Barati.

JSTests:

* microbenchmarks/array-shift-unshift-empty.js: Added.
* stress/array-setLength-on-proxy-error.js: Added.

Source/_javascript_Core:

This patch:

1. Replaces all putLength() call sites with setLength(), saving two JSValue
   instantiations in arrayProtoFuncPop() and two in arrayProtoFuncShift().

2. Merges putLength() into setLength(), removing superfluous put() call for
   JSArray. Also, performs put() in strict mode to preserve the original
   error messages, like ones in ProxyObject::performPut().

3. Inlines performPop(), which avoided an extra index check and Identifier
   creation, as it was on the slow path anyway (note JSArray::pop() call).

This change advances provided setLength()-heavy microbenchmark by ~40%,
while existing Array tests are neutral.

* runtime/ArrayPrototype.cpp:
(JSC::setLength):
(JSC::arrayProtoFuncPop):
(JSC::arrayProtoFuncPush):
(JSC::arrayProtoFuncShift):
(JSC::arrayProtoFuncUnShift):
(JSC::putLength): Deleted.

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (266214 => 266215)


--- trunk/JSTests/ChangeLog	2020-08-27 01:52:31 UTC (rev 266214)
+++ trunk/JSTests/ChangeLog	2020-08-27 01:55:08 UTC (rev 266215)
@@ -1,3 +1,13 @@
+2020-08-26  Alexey Shvayka  <[email protected]>
+
+        Merge putLength() into setLength()
+        https://bugs.webkit.org/show_bug.cgi?id=211279
+
+        Reviewed by Darin Adler and Saam Barati.
+
+        * microbenchmarks/array-shift-unshift-empty.js: Added.
+        * stress/array-setLength-on-proxy-error.js: Added.
+
 2020-08-26  Yusuke Suzuki  <[email protected]>
 
         [JSC] Enable Intl.Segmenter

Added: trunk/JSTests/microbenchmarks/array-shift-unshift-empty.js (0 => 266215)


--- trunk/JSTests/microbenchmarks/array-shift-unshift-empty.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/array-shift-unshift-empty.js	2020-08-27 01:55:08 UTC (rev 266215)
@@ -0,0 +1,8 @@
+noInline(Array.prototype.shift);
+noInline(Array.prototype.unshift);
+
+for (var i = 0; i < 1e6; ++i) {
+    var arr = [];
+    arr.shift();
+    arr.unshift();
+}

Added: trunk/JSTests/stress/array-setLength-on-proxy-error.js (0 => 266215)


--- trunk/JSTests/stress/array-setLength-on-proxy-error.js	                        (rev 0)
+++ trunk/JSTests/stress/array-setLength-on-proxy-error.js	2020-08-27 01:55:08 UTC (rev 266215)
@@ -0,0 +1,47 @@
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    try {
+        func();
+    } catch (error) {
+        errorThrown = true;
+        if (String(error) !== errorMessage)
+            throw new Error(`bad error: ${error}`);
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+}
+
+var proxy1 = new Proxy({}, {
+    set: (_, key) => key !== "length",
+});
+
+shouldThrow(() => {
+    Array.prototype.unshift.call(proxy1);
+}, "TypeError: Proxy object's 'set' trap returned falsy value for property 'length'");
+
+var obj2 = Object.defineProperty({}, "length", {
+    value: 22,
+    writable: false,
+    configurable: false,
+});
+
+var proxy2 = new Proxy(obj2, {
+    set: () => true,
+});
+
+shouldThrow(() => {
+    Array.prototype.shift.call(proxy2);
+}, "TypeError: Proxy handler's 'set' on a non-configurable and non-writable property on 'target' should either return false or be the same value already on the 'target'");
+
+var obj3 = Object.defineProperty({}, "length", {
+    get: () => 33,
+    configurable: false,
+});
+
+var proxy3 = new Proxy(obj3, {
+    set: () => true,
+});
+
+shouldThrow(() => {
+    Array.prototype.pop.call(proxy3);
+}, "TypeError: Proxy handler's 'set' method on a non-configurable accessor property without a setter should return false");

Modified: trunk/Source/_javascript_Core/ChangeLog (266214 => 266215)


--- trunk/Source/_javascript_Core/ChangeLog	2020-08-27 01:52:31 UTC (rev 266214)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-08-27 01:55:08 UTC (rev 266215)
@@ -1,3 +1,33 @@
+2020-08-26  Alexey Shvayka  <[email protected]>
+
+        Merge putLength() into setLength()
+        https://bugs.webkit.org/show_bug.cgi?id=211279
+
+        Reviewed by Darin Adler and Saam Barati.
+
+        This patch:
+
+        1. Replaces all putLength() call sites with setLength(), saving two JSValue
+           instantiations in arrayProtoFuncPop() and two in arrayProtoFuncShift().
+
+        2. Merges putLength() into setLength(), removing superfluous put() call for
+           JSArray. Also, performs put() in strict mode to preserve the original
+           error messages, like ones in ProxyObject::performPut().
+
+        3. Inlines performPop(), which avoided an extra index check and Identifier
+           creation, as it was on the slow path anyway (note JSArray::pop() call).
+
+        This change advances provided setLength()-heavy microbenchmark by ~40%,
+        while existing Array tests are neutral.
+
+        * runtime/ArrayPrototype.cpp:
+        (JSC::setLength):
+        (JSC::arrayProtoFuncPop):
+        (JSC::arrayProtoFuncPush):
+        (JSC::arrayProtoFuncShift):
+        (JSC::arrayProtoFuncUnShift):
+        (JSC::putLength): Deleted.
+
 2020-08-26  Saam Barati  <[email protected]>
 
         Make isIndex use MAX_ARRAY_INDEX

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (266214 => 266215)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2020-08-27 01:52:31 UTC (rev 266214)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2020-08-27 01:55:08 UTC (rev 266215)
@@ -160,27 +160,16 @@
     RELEASE_AND_RETURN(scope, slot.getValue(globalObject, index));
 }
 
-static ALWAYS_INLINE void putLength(JSGlobalObject* globalObject, VM& vm, JSObject* obj, JSValue value)
-{
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    PutPropertySlot slot(obj);
-    bool success = obj->methodTable(vm)->put(obj, globalObject, vm.propertyNames->length, value, slot);
-    RETURN_IF_EXCEPTION(scope, void());
-    if (UNLIKELY(!success))
-        throwTypeError(globalObject, scope, ReadonlyPropertyWriteError);
-}
-
 static ALWAYS_INLINE void setLength(JSGlobalObject* globalObject, VM& vm, JSObject* obj, uint64_t value)
 {
-    auto scope = DECLARE_THROW_SCOPE(vm);
     static constexpr bool throwException = true;
-    if (isJSArray(obj)) {
+    if (LIKELY(isJSArray(obj))) {
         ASSERT(static_cast<uint32_t>(value) == value);
         jsCast<JSArray*>(obj)->setLength(globalObject, static_cast<uint32_t>(value), throwException);
-        RETURN_IF_EXCEPTION(scope, void());
+    } else {
+        PutPropertySlot slot(obj, throwException);
+        obj->methodTable(vm)->put(obj, globalObject, vm.propertyNames->length, jsNumber(value), slot);
     }
-    scope.release();
-    putLength(globalObject, vm, obj, jsNumber(value));
 }
 
 namespace ArrayPrototypeInternal {
@@ -876,28 +865,24 @@
 
     if (length == 0) {
         scope.release();
-        putLength(globalObject, vm, thisObj, jsNumber(length));
+        setLength(globalObject, vm, thisObj, length);
         return JSValue::encode(jsUndefined());
     }
 
-    auto performPop = [&] (auto index, JSValue indexValue) {
-        JSValue result = thisObj->get(globalObject, index);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
-        bool success = thisObj->deleteProperty(globalObject, index);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
-        if (UNLIKELY(!success)) {
-            throwTypeError(globalObject, scope, UnableToDeletePropertyError);
-            return encodedJSValue();
-        }
-        scope.release();
-        putLength(globalObject, vm, thisObj, indexValue);
-        return JSValue::encode(result);
-    };
+    static_assert(MAX_ARRAY_INDEX + 1 > MAX_ARRAY_INDEX);
+    uint64_t index = length - 1;
+    JSValue result = thisObj->get(globalObject, index);
+    RETURN_IF_EXCEPTION(scope, { });
+    bool success = thisObj->deleteProperty(globalObject, index);
+    RETURN_IF_EXCEPTION(scope, { });
+    if (UNLIKELY(!success)) {
+        throwTypeError(globalObject, scope, UnableToDeletePropertyError);
+        return { };
+    }
 
-    static_assert(MAX_ARRAY_INDEX + 1 > MAX_ARRAY_INDEX);
-    if (LIKELY(length <= MAX_ARRAY_INDEX + 1))
-        return performPop(static_cast<uint32_t>(length - 1), jsNumber(static_cast<uint32_t>(length - 1)));
-    return performPop(Identifier::from(vm, length - 1), jsNumber(length - 1));
+    scope.release();
+    setLength(globalObject, vm, thisObj, index);
+    return JSValue::encode(result);
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(JSGlobalObject* globalObject, CallFrame* callFrame)
@@ -935,10 +920,10 @@
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
     
-    JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(callFrame->argumentCount()));
+    uint64_t newLength = length + argCount;
     scope.release();
-    putLength(globalObject, vm, thisObj, newLength);
-    return JSValue::encode(newLength);
+    setLength(globalObject, vm, thisObj, newLength);
+    return JSValue::encode(jsNumber(newLength));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(JSGlobalObject* globalObject, CallFrame* callFrame)
@@ -1057,7 +1042,7 @@
 
     if (length == 0) {
         scope.release();
-        putLength(globalObject, vm, thisObj, jsNumber(length));
+        setLength(globalObject, vm, thisObj, length);
         return JSValue::encode(jsUndefined());
     }
 
@@ -1066,7 +1051,7 @@
     shift<JSArray::ShiftCountForShift>(globalObject, thisObj, 0, 1, 0, length);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
     scope.release();
-    putLength(globalObject, vm, thisObj, jsNumber(length - 1));
+    setLength(globalObject, vm, thisObj, length - 1);
     return JSValue::encode(result);
 }
 
@@ -1263,10 +1248,10 @@
         thisObj->putByIndexInline(globalObject, k, callFrame->uncheckedArgument(k), true);
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
-    JSValue result = jsNumber(length + nrArgs);
+    uint64_t newLength = length + nrArgs;
     scope.release();
-    putLength(globalObject, vm, thisObj, result);
-    return JSValue::encode(result);
+    setLength(globalObject, vm, thisObj, newLength);
+    return JSValue::encode(jsNumber(newLength));
 }
 
 enum class IndexOfDirection { Forward, Backward };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to