Title: [275271] trunk
Revision
275271
Author
shvaikal...@gmail.com
Date
2021-03-31 00:21:37 -0700 (Wed, 31 Mar 2021)

Log Message

Optimize constructors of ES6 collections
https://bugs.webkit.org/show_bug.cgi?id=223953

Reviewed by Yusuke Suzuki.

JSTests:

* microbenchmarks/map-constructor.js:
* microbenchmarks/set-constructor.js: Added.
* microbenchmarks/weak-map-constructor.js: Added.
* microbenchmarks/weak-set-constructor.js: Added.
* stress/map-constructor-adder.js:
* stress/set-constructor-adder.js:
* stress/weak-map-constructor-adder-error-cross-realm.js: Added.
* stress/weak-map-constructor-adder.js:
* stress/weak-set-constructor-adder-error-cross-realm.js: Added.
* stress/weak-set-constructor-adder.js:
* stress/weak-set-constructor.js:

Source/_javascript_Core:

This patch speeds up the constructors by avoiding call() for non-observable
"set" / "add" methods and using getIndex() for Map / WeakMap collections.

For Map / Set, this change leverages existing cloning helpers, which rely on
watchpoints, to avoid even a method lookup. However, slower path is used for
subclasses. Results in 1.9x speed-up for common case.

For WeakMap / WeakSet, adder function is checked by C++ pointer, which enables
fast path even for cross-realm subclasses. Results in 2.3x progression.

Both approaches require special handling of a cross-realm NewTarget to ensure
that raised exceptions (OOM / TypeError) belong to realm of the adder function,
and not to constructor's or NewTarget's.

Also, adds descriptve error messages for non-callable "set" / "add" properties.

* runtime/JSMap.cpp:
(JSC::JSMap::isSetFastAndNonObservable):
(JSC::JSMap::canCloneFastAndNonObservable): Deleted.
* runtime/JSMap.h:
* runtime/JSSet.cpp:
(JSC::JSSet::isAddFastAndNonObservable):
(JSC::JSSet::canCloneFastAndNonObservable): Deleted.
* runtime/JSSet.h:
* runtime/MapConstructor.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/SetConstructor.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/WeakMapConstructor.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/WeakMapPrototype.cpp:
(JSC::WeakMapPrototype::finishCreation):
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/WeakMapPrototype.h:
* runtime/WeakSetConstructor.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/WeakSetPrototype.cpp:
(JSC::WeakSetPrototype::finishCreation):
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/WeakSetPrototype.h:

LayoutTests:

* js/dom/basic-weakset-expected.txt:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (275270 => 275271)


--- trunk/JSTests/ChangeLog	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/JSTests/ChangeLog	2021-03-31 07:21:37 UTC (rev 275271)
@@ -1,3 +1,22 @@
+2021-03-31  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        Optimize constructors of ES6 collections
+        https://bugs.webkit.org/show_bug.cgi?id=223953
+
+        Reviewed by Yusuke Suzuki.
+
+        * microbenchmarks/map-constructor.js:
+        * microbenchmarks/set-constructor.js: Added.
+        * microbenchmarks/weak-map-constructor.js: Added.
+        * microbenchmarks/weak-set-constructor.js: Added.
+        * stress/map-constructor-adder.js:
+        * stress/set-constructor-adder.js:
+        * stress/weak-map-constructor-adder-error-cross-realm.js: Added.
+        * stress/weak-map-constructor-adder.js:
+        * stress/weak-set-constructor-adder-error-cross-realm.js: Added.
+        * stress/weak-set-constructor-adder.js:
+        * stress/weak-set-constructor.js:
+
 2021-03-29  Ryan Haddad  <ryanhad...@apple.com>
 
         Unreviewed test gardening.

Modified: trunk/JSTests/microbenchmarks/map-constructor.js (275270 => 275271)


--- trunk/JSTests/microbenchmarks/map-constructor.js	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/JSTests/microbenchmarks/map-constructor.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -4,28 +4,10 @@
 }
 noInline(test);
 
-var array = [
-    [0, 0],
-    [1, 0],
-    [2, 0],
-    [3, 0],
-    [4, 0],
-    [5, 0],
-    [6, 0],
-    [7, 0],
-    [8, 0],
-    [9, 0],
-    [10, 0],
-    [11, 0],
-    [12, 0],
-    [13, 0],
-    [14, 0],
-    [15, 0],
-    [16, 0],
-    [17, 0],
-    [18, 0],
-    [19, 0],
-];
+var array = [];
 
+for (var j = 0; j < 50; ++j)
+    array[j] = [j, 0];
+
 for (var i = 0; i < 1e4; ++i)
     test(array);

Added: trunk/JSTests/microbenchmarks/set-constructor.js (0 => 275271)


--- trunk/JSTests/microbenchmarks/set-constructor.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/set-constructor.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -0,0 +1,13 @@
+function test(array)
+{
+    return new Set(array);
+}
+noInline(test);
+
+var array = [];
+
+for (var j = 0; j < 50; ++j)
+    array[j] = j;
+
+for (var i = 0; i < 1e4; ++i)
+    test(array);

Added: trunk/JSTests/microbenchmarks/weak-map-constructor.js (0 => 275271)


--- trunk/JSTests/microbenchmarks/weak-map-constructor.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/weak-map-constructor.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -0,0 +1,13 @@
+function test(array)
+{
+    return new WeakMap(array);
+}
+noInline(test);
+
+var array = [];
+
+for (var j = 0; j < 50; ++j)
+    array[j] = [{}, 0];
+
+for (var i = 0; i < 1e4; ++i)
+    test(array);

Added: trunk/JSTests/microbenchmarks/weak-set-constructor.js (0 => 275271)


--- trunk/JSTests/microbenchmarks/weak-set-constructor.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/weak-set-constructor.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -0,0 +1,13 @@
+function test(array)
+{
+    return new WeakSet(array);
+}
+noInline(test);
+
+var array = [];
+
+for (var j = 0; j < 50; ++j)
+    array[j] = {};
+
+for (var i = 0; i < 1e4; ++i)
+    test(array);

Modified: trunk/JSTests/stress/map-constructor-adder.js (275270 => 275271)


--- trunk/JSTests/stress/map-constructor-adder.js	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/JSTests/stress/map-constructor-adder.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -1,5 +1,18 @@
 // Map constructor with adder change.
 
+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("Didn't throw!");
+}
+
 var originalAdder = Map.prototype.set;
 var counter = 0;
 
@@ -33,14 +46,12 @@
 
 var map = new Map();
 var map = new Map([]);
-var error = null;
-try {
-    var map = new Map([ [0, 0] ]);
-} catch (e) {
-    error = e;
-}
-if (!error)
-    throw "Error: error not thrown";
-if (String(error) !== "Error: adder called")
-    throw "Error: bad error " + String(error);
 
+shouldThrow(() => {
+    new Map([ [0, 0] ]);
+}, "Error: adder called");
+
+Map.prototype.set = undefined;
+shouldThrow(() => {
+    new Map([ [0, 0] ]);
+}, "TypeError: 'set' property of a Map should be callable.");

Modified: trunk/JSTests/stress/set-constructor-adder.js (275270 => 275271)


--- trunk/JSTests/stress/set-constructor-adder.js	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/JSTests/stress/set-constructor-adder.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -1,5 +1,18 @@
 // Set constructor with adder change.
 
+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("Didn't throw!");
+}
+
 var originalAdder = Set.prototype.add;
 var counter = 0;
 
@@ -21,14 +34,12 @@
 
 var set = new Set();
 var set = new Set([]);
-var error = null;
-try {
-    var set = new Set([0]);
-} catch (e) {
-    error = e;
-}
-if (!error)
-    throw "Error: error not thrown";
-if (String(error) !== "Error: adder called")
-    throw "Error: bad error " + String(error);
 
+shouldThrow(() => {
+    new Set([0]);
+}, "Error: adder called");
+
+Set.prototype.add = Symbol();
+shouldThrow(() => {
+    new Set([0]);
+}, "TypeError: 'add' property of a Set should be callable.");

Added: trunk/JSTests/stress/weak-map-constructor-adder-error-cross-realm.js (0 => 275271)


--- trunk/JSTests/stress/weak-map-constructor-adder-error-cross-realm.js	                        (rev 0)
+++ trunk/JSTests/stress/weak-map-constructor-adder-error-cross-realm.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -0,0 +1,14 @@
+const realmA = $vm.createGlobalObject();
+const realmB = $vm.createGlobalObject();
+const realmC = $vm.createGlobalObject();
+
+realmB.WeakMap.prototype.set =
+realmC.WeakMap.prototype.set;
+
+try {
+    Reflect.construct(realmA.WeakMap, [[[0, 0]]], realmB.WeakMap);
+    throw "Didn't throw!";
+} catch (err) {
+    if (!(err instanceof realmC.TypeError))
+        throw "Bad realm of an error!";
+}

Modified: trunk/JSTests/stress/weak-map-constructor-adder.js (275270 => 275271)


--- trunk/JSTests/stress/weak-map-constructor-adder.js	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/JSTests/stress/weak-map-constructor-adder.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -1,5 +1,18 @@
 // WeakMap constructor with adder change.
 
+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("Didn't throw!");
+}
+
 var originalAdder = WeakMap.prototype.set;
 var counter = 0;
 
@@ -38,14 +51,12 @@
 
 var map = new WeakMap();
 var map = new WeakMap([]);
-var error = null;
-try {
-    var map = new WeakMap([ [0, 0] ]);
-} catch (e) {
-    error = e;
-}
-if (!error)
-    throw "Error: error not thrown";
-if (String(error) !== "Error: adder called")
-    throw "Error: bad error " + String(error);
 
+shouldThrow(() => {
+    new WeakMap([ [0, 0] ]);
+}, "Error: adder called");
+
+WeakMap.prototype.set = null;
+shouldThrow(() => {
+    new WeakMap([ [0, 0] ]);
+}, "TypeError: 'set' property of a WeakMap should be callable.");

Added: trunk/JSTests/stress/weak-set-constructor-adder-error-cross-realm.js (0 => 275271)


--- trunk/JSTests/stress/weak-set-constructor-adder-error-cross-realm.js	                        (rev 0)
+++ trunk/JSTests/stress/weak-set-constructor-adder-error-cross-realm.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -0,0 +1,14 @@
+const realmA = $vm.createGlobalObject();
+const realmB = $vm.createGlobalObject();
+const realmC = $vm.createGlobalObject();
+
+realmB.WeakSet.prototype.add =
+realmC.WeakSet.prototype.add;
+
+try {
+    Reflect.construct(realmA.WeakSet, [[0]], realmB.WeakSet);
+    throw "Didn't throw!";
+} catch (err) {
+    if (!(err instanceof realmC.TypeError))
+        throw "Bad realm of an error!";
+}

Modified: trunk/JSTests/stress/weak-set-constructor-adder.js (275270 => 275271)


--- trunk/JSTests/stress/weak-set-constructor-adder.js	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/JSTests/stress/weak-set-constructor-adder.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -1,5 +1,18 @@
 // WeakSet constructor with adder change.
 
+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("Didn't throw!");
+}
+
 var originalAdder = WeakSet.prototype.add;
 var counter = 0;
 
@@ -38,14 +51,12 @@
 
 var set = new WeakSet();
 var set = new WeakSet([]);
-var error = null;
-try {
-    var set = new WeakSet([ 0 ]);
-} catch (e) {
-    error = e;
-}
-if (!error)
-    throw new Error("error not thrown");
-if (String(error) !== "Error: adder called")
-    throw new Error("bad error " + String(error));
 
+shouldThrow(() => {
+    new WeakSet([ 0 ]);
+}, "Error: adder called");
+
+WeakSet.prototype.add = "foo";
+shouldThrow(() => {
+    new WeakSet([ 0 ]);
+}, "TypeError: 'add' property of a WeakSet should be callable.");

Modified: trunk/JSTests/stress/weak-set-constructor.js (275270 => 275271)


--- trunk/JSTests/stress/weak-set-constructor.js	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/JSTests/stress/weak-set-constructor.js	2021-03-31 07:21:37 UTC (rev 275271)
@@ -141,6 +141,6 @@
 ];
 
 for (var item of nonObjectKeys) {
-    testTypeError(item, 'TypeError: Attempted to add a non-object key to a WeakSet');
+    testTypeError(item, 'TypeError: Attempted to add a non-object value to a WeakSet');
     testCallTypeError(item);
 }

Modified: trunk/LayoutTests/ChangeLog (275270 => 275271)


--- trunk/LayoutTests/ChangeLog	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/LayoutTests/ChangeLog	2021-03-31 07:21:37 UTC (rev 275271)
@@ -1,3 +1,12 @@
+2021-03-31  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        Optimize constructors of ES6 collections
+        https://bugs.webkit.org/show_bug.cgi?id=223953
+
+        Reviewed by Yusuke Suzuki.
+
+        * js/dom/basic-weakset-expected.txt:
+
 2021-03-30  Antoine Quint  <grao...@webkit.org>
 
         Computed style for background-position should not use "left" and "top" keywords

Modified: trunk/LayoutTests/js/dom/basic-weakset-expected.txt (275270 => 275271)


--- trunk/LayoutTests/js/dom/basic-weakset-expected.txt	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/LayoutTests/js/dom/basic-weakset-expected.txt	2021-03-31 07:21:37 UTC (rev 275271)
@@ -7,14 +7,14 @@
 PASS WeakSet.prototype instanceof WeakSet is false
 PASS new WeakSet() instanceof WeakSet is true
 PASS WeakSet() threw exception TypeError: calling WeakSet constructor without new is invalid.
-PASS set.add(0) threw exception TypeError: Attempted to add a non-object key to a WeakSet.
-PASS set.add(0.5) threw exception TypeError: Attempted to add a non-object key to a WeakSet.
-PASS set.add('foo') threw exception TypeError: Attempted to add a non-object key to a WeakSet.
-PASS set.add(true) threw exception TypeError: Attempted to add a non-object key to a WeakSet.
-PASS set.add(false) threw exception TypeError: Attempted to add a non-object key to a WeakSet.
-PASS set.add(null) threw exception TypeError: Attempted to add a non-object key to a WeakSet.
-PASS set.add(undefined) threw exception TypeError: Attempted to add a non-object key to a WeakSet.
-PASS set.add(Symbol.iterator) threw exception TypeError: Attempted to add a non-object key to a WeakSet.
+PASS set.add(0) threw exception TypeError: Attempted to add a non-object value to a WeakSet.
+PASS set.add(0.5) threw exception TypeError: Attempted to add a non-object value to a WeakSet.
+PASS set.add('foo') threw exception TypeError: Attempted to add a non-object value to a WeakSet.
+PASS set.add(true) threw exception TypeError: Attempted to add a non-object value to a WeakSet.
+PASS set.add(false) threw exception TypeError: Attempted to add a non-object value to a WeakSet.
+PASS set.add(null) threw exception TypeError: Attempted to add a non-object value to a WeakSet.
+PASS set.add(undefined) threw exception TypeError: Attempted to add a non-object value to a WeakSet.
+PASS set.add(Symbol.iterator) threw exception TypeError: Attempted to add a non-object value to a WeakSet.
 PASS set.has(0) is false
 PASS set.has(0.5) is false
 PASS set.has('foo') is false

Modified: trunk/Source/_javascript_Core/ChangeLog (275270 => 275271)


--- trunk/Source/_javascript_Core/ChangeLog	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-03-31 07:21:37 UTC (rev 275271)
@@ -1,3 +1,51 @@
+2021-03-31  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        Optimize constructors of ES6 collections
+        https://bugs.webkit.org/show_bug.cgi?id=223953
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch speeds up the constructors by avoiding call() for non-observable
+        "set" / "add" methods and using getIndex() for Map / WeakMap collections.
+
+        For Map / Set, this change leverages existing cloning helpers, which rely on
+        watchpoints, to avoid even a method lookup. However, slower path is used for
+        subclasses. Results in 1.9x speed-up for common case.
+
+        For WeakMap / WeakSet, adder function is checked by C++ pointer, which enables
+        fast path even for cross-realm subclasses. Results in 2.3x progression.
+
+        Both approaches require special handling of a cross-realm NewTarget to ensure
+        that raised exceptions (OOM / TypeError) belong to realm of the adder function,
+        and not to constructor's or NewTarget's.
+
+        Also, adds descriptve error messages for non-callable "set" / "add" properties.
+
+        * runtime/JSMap.cpp:
+        (JSC::JSMap::isSetFastAndNonObservable):
+        (JSC::JSMap::canCloneFastAndNonObservable): Deleted.
+        * runtime/JSMap.h:
+        * runtime/JSSet.cpp:
+        (JSC::JSSet::isAddFastAndNonObservable):
+        (JSC::JSSet::canCloneFastAndNonObservable): Deleted.
+        * runtime/JSSet.h:
+        * runtime/MapConstructor.cpp:
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        * runtime/SetConstructor.cpp:
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        * runtime/WeakMapConstructor.cpp:
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        * runtime/WeakMapPrototype.cpp:
+        (JSC::WeakMapPrototype::finishCreation):
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        * runtime/WeakMapPrototype.h:
+        * runtime/WeakSetConstructor.cpp:
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        * runtime/WeakSetPrototype.cpp:
+        (JSC::WeakSetPrototype::finishCreation):
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        * runtime/WeakSetPrototype.h:
+
 2021-03-30  Devin Rousso  <drou...@apple.com>
 
         REGRESSION(r274607): media controls script is visible in Web Inspector even without the engineering "Show WebKit-internal scripts" enabled

Modified: trunk/Source/_javascript_Core/runtime/JSMap.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/JSMap.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/JSMap.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -39,6 +39,21 @@
     return instance;
 }
 
+bool JSMap::isSetFastAndNonObservable(Structure* structure)
+{
+    JSGlobalObject* globalObject = structure->globalObject();
+    if (!globalObject->isMapPrototypeSetFastAndNonObservable())
+        return false;
+
+    if (structure->hasPolyProto())
+        return false;
+
+    if (structure->storedPrototype() != globalObject->mapPrototype())
+        return false;
+
+    return true;
+}
+
 bool JSMap::isIteratorProtocolFastAndNonObservable()
 {
     JSGlobalObject* globalObject = this->globalObject();
@@ -60,23 +75,4 @@
     return true;
 }
 
-bool JSMap::canCloneFastAndNonObservable(Structure* structure)
-{
-    auto setFastAndNonObservable = [&] (Structure* structure) {
-        JSGlobalObject* globalObject = structure->globalObject();
-        if (!globalObject->isMapPrototypeSetFastAndNonObservable())
-            return false;
-
-        if (structure->hasPolyProto())
-            return false;
-
-        if (structure->storedPrototype() != globalObject->mapPrototype())
-            return false;
-
-        return true;
-    };
-
-    return isIteratorProtocolFastAndNonObservable() && setFastAndNonObservable(structure);
 }
-
-}

Modified: trunk/Source/_javascript_Core/runtime/JSMap.h (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/JSMap.h	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/JSMap.h	2021-03-31 07:21:37 UTC (rev 275271)
@@ -59,8 +59,8 @@
         add(globalObject, key, value);
     }
 
+    static bool isSetFastAndNonObservable(Structure*);
     bool isIteratorProtocolFastAndNonObservable();
-    bool canCloneFastAndNonObservable(Structure*);
     JSMap* clone(JSGlobalObject*, VM&, Structure*);
 
 private:

Modified: trunk/Source/_javascript_Core/runtime/JSSet.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/JSSet.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/JSSet.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -39,6 +39,21 @@
     return instance;
 }
 
+bool JSSet::isAddFastAndNonObservable(Structure* structure)
+{
+    JSGlobalObject* globalObject = structure->globalObject();
+    if (!globalObject->isSetPrototypeAddFastAndNonObservable())
+        return false;
+
+    if (structure->hasPolyProto())
+        return false;
+
+    if (structure->storedPrototype() != globalObject->jsSetPrototype())
+        return false;
+
+    return true;
+}
+
 bool JSSet::isIteratorProtocolFastAndNonObservable()
 {
     JSGlobalObject* globalObject = this->globalObject();
@@ -60,23 +75,4 @@
     return true;
 }
 
-bool JSSet::canCloneFastAndNonObservable(Structure* structure)
-{
-    auto addFastAndNonObservable = [&] (Structure* structure) {
-        JSGlobalObject* globalObject = structure->globalObject();
-        if (!globalObject->isSetPrototypeAddFastAndNonObservable())
-            return false;
-
-        if (structure->hasPolyProto())
-            return false;
-
-        if (structure->storedPrototype() != globalObject->jsSetPrototype())
-            return false;
-
-        return true;
-    };
-
-    return isIteratorProtocolFastAndNonObservable() && addFastAndNonObservable(structure);
 }
-
-}

Modified: trunk/Source/_javascript_Core/runtime/JSSet.h (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/JSSet.h	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/JSSet.h	2021-03-31 07:21:37 UTC (rev 275271)
@@ -59,8 +59,8 @@
         return instance;
     }
 
+    static bool isAddFastAndNonObservable(Structure*);
     bool isIteratorProtocolFastAndNonObservable();
-    bool canCloneFastAndNonObservable(Structure*);
     JSSet* clone(JSGlobalObject*, VM&, Structure*);
 
 private:

Modified: trunk/Source/_javascript_Core/runtime/MapConstructor.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/MapConstructor.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/MapConstructor.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -70,8 +70,9 @@
     if (iterable.isUndefinedOrNull())
         RELEASE_AND_RETURN(scope, JSValue::encode(JSMap::create(globalObject, vm, mapStructure)));
 
+    bool canPerformFastSet = JSMap::isSetFastAndNonObservable(mapStructure);
     if (auto* iterableMap = jsDynamicCast<JSMap*>(vm, iterable)) {
-        if (iterableMap->canCloneFastAndNonObservable(mapStructure))
+        if (canPerformFastSet && iterableMap->isIteratorProtocolFastAndNonObservable())
             RELEASE_AND_RETURN(scope, JSValue::encode(iterableMap->clone(globalObject, vm, mapStructure)));
     }
 
@@ -78,12 +79,16 @@
     JSMap* map = JSMap::create(globalObject, vm, mapStructure);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    JSValue adderFunction = map->JSObject::get(globalObject, vm.propertyNames->set);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    JSValue adderFunction;
+    CallData adderFunctionCallData;
+    if (!canPerformFastSet) {
+        adderFunction = map->JSObject::get(globalObject, vm.propertyNames->set);
+        RETURN_IF_EXCEPTION(scope, { });
 
-    auto adderFunctionCallData = getCallData(vm, adderFunction);
-    if (adderFunctionCallData.type == CallData::Type::None)
-        return JSValue::encode(throwTypeError(globalObject, scope));
+        adderFunctionCallData = getCallData(vm, adderFunction);
+        if (UNLIKELY(adderFunctionCallData.type == CallData::Type::None))
+            return throwVMTypeError(globalObject, scope, "'set' property of a Map should be callable."_s);
+    }
 
     scope.release();
     forEachInIterable(globalObject, iterable, [&](VM& vm, JSGlobalObject* globalObject, JSValue nextItem) {
@@ -93,17 +98,24 @@
             return;
         }
 
-        JSValue key = nextItem.get(globalObject, static_cast<unsigned>(0));
+        JSObject* nextObject = asObject(nextItem);
+
+        JSValue key = nextObject->getIndex(globalObject, static_cast<unsigned>(0));
         RETURN_IF_EXCEPTION(scope, void());
 
-        JSValue value = nextItem.get(globalObject, static_cast<unsigned>(1));
+        JSValue value = nextObject->getIndex(globalObject, static_cast<unsigned>(1));
         RETURN_IF_EXCEPTION(scope, void());
 
+        scope.release();
+        if (canPerformFastSet) {
+            map->set(mapStructure->globalObject(), key, value);
+            return;
+        }
+
         MarkedArgumentBuffer arguments;
         arguments.append(key);
         arguments.append(value);
         ASSERT(!arguments.hasOverflowed());
-        scope.release();
         call(globalObject, adderFunction, adderFunctionCallData, map, arguments);
     });
 

Modified: trunk/Source/_javascript_Core/runtime/SetConstructor.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/SetConstructor.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/SetConstructor.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -70,8 +70,9 @@
     if (iterable.isUndefinedOrNull()) 
         RELEASE_AND_RETURN(scope, JSValue::encode(JSSet::create(globalObject, vm, setStructure)));
 
+    bool canPerformFastAdd = JSSet::isAddFastAndNonObservable(setStructure);
     if (auto* iterableSet = jsDynamicCast<JSSet*>(vm, iterable)) {
-        if (iterableSet->canCloneFastAndNonObservable(setStructure)) 
+        if (canPerformFastAdd && iterableSet->isIteratorProtocolFastAndNonObservable()) 
             RELEASE_AND_RETURN(scope, JSValue::encode(iterableSet->clone(globalObject, vm, setStructure)));
     }
 
@@ -78,15 +79,24 @@
     JSSet* set = JSSet::create(globalObject, vm, setStructure);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    JSValue adderFunction = set->JSObject::get(globalObject, vm.propertyNames->add);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    JSValue adderFunction;
+    CallData adderFunctionCallData;
+    if (!canPerformFastAdd) {
+        adderFunction = set->JSObject::get(globalObject, vm.propertyNames->add);
+        RETURN_IF_EXCEPTION(scope, { });
 
-    auto adderFunctionCallData = getCallData(vm, adderFunction);
-    if (UNLIKELY(adderFunctionCallData.type == CallData::Type::None))
-        return JSValue::encode(throwTypeError(globalObject, scope));
+        adderFunctionCallData = getCallData(vm, adderFunction);
+        if (UNLIKELY(adderFunctionCallData.type == CallData::Type::None))
+            return throwVMTypeError(globalObject, scope, "'add' property of a Set should be callable."_s);
+    }
 
     scope.release();
     forEachInIterable(globalObject, iterable, [&](VM&, JSGlobalObject* globalObject, JSValue nextValue) {
+        if (canPerformFastAdd) {
+            set->add(setStructure->globalObject(), nextValue);
+            return;
+        }
+
         MarkedArgumentBuffer arguments;
         arguments.append(nextValue);
         ASSERT(!arguments.hasOverflowed());

Modified: trunk/Source/_javascript_Core/runtime/WeakMapConstructor.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/WeakMapConstructor.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/WeakMapConstructor.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -75,8 +75,10 @@
 
     auto adderFunctionCallData = getCallData(vm, adderFunction);
     if (adderFunctionCallData.type == CallData::Type::None)
-        return JSValue::encode(throwTypeError(globalObject, scope));
+        return throwVMTypeError(globalObject, scope, "'set' property of a WeakMap should be callable."_s);
 
+    bool canPerformFastSet = adderFunctionCallData.type == CallData::Type::Native && adderFunctionCallData.native.function == protoFuncWeakMapSet;
+
     scope.release();
     forEachInIterable(globalObject, iterable, [&](VM& vm, JSGlobalObject* globalObject, JSValue nextItem) {
         auto scope = DECLARE_THROW_SCOPE(vm);
@@ -85,12 +87,22 @@
             return;
         }
 
-        JSValue key = nextItem.get(globalObject, static_cast<unsigned>(0));
+        JSObject* nextObject = asObject(nextItem);
+
+        JSValue key = nextObject->getIndex(globalObject, static_cast<unsigned>(0));
         RETURN_IF_EXCEPTION(scope, void());
 
-        JSValue value = nextItem.get(globalObject, static_cast<unsigned>(1));
+        JSValue value = nextObject->getIndex(globalObject, static_cast<unsigned>(1));
         RETURN_IF_EXCEPTION(scope, void());
 
+        if (canPerformFastSet) {
+            if (key.isObject())
+                weakMap->set(vm, asObject(key), value);
+            else
+                throwTypeError(asObject(adderFunction)->globalObject(vm), scope, WeakMapNonObjectKeyError);
+            return;
+        }
+
         MarkedArgumentBuffer arguments;
         arguments.append(key);
         arguments.append(value);

Modified: trunk/Source/_javascript_Core/runtime/WeakMapPrototype.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/WeakMapPrototype.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/WeakMapPrototype.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -31,12 +31,13 @@
 
 namespace JSC {
 
+const ASCIILiteral WeakMapNonObjectKeyError { "Attempted to set a non-object key in a WeakMap"_s };
+
 const ClassInfo WeakMapPrototype::s_info = { "WeakMap", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WeakMapPrototype) };
 
 static JSC_DECLARE_HOST_FUNCTION(protoFuncWeakMapDelete);
 static JSC_DECLARE_HOST_FUNCTION(protoFuncWeakMapGet);
 static JSC_DECLARE_HOST_FUNCTION(protoFuncWeakMapHas);
-static JSC_DECLARE_HOST_FUNCTION(protoFuncWeakMapSet);
 
 void WeakMapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
@@ -109,7 +110,7 @@
         return JSValue::encode(jsUndefined());
     JSValue key = callFrame->argument(0);
     if (!key.isObject())
-        return JSValue::encode(throwTypeError(globalObject, scope, "Attempted to set a non-object key in a WeakMap"_s));
+        return JSValue::encode(throwTypeError(globalObject, scope, WeakMapNonObjectKeyError));
     map->set(vm, asObject(key), callFrame->argument(1));
     return JSValue::encode(callFrame->thisValue());
 }

Modified: trunk/Source/_javascript_Core/runtime/WeakMapPrototype.h (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/WeakMapPrototype.h	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/WeakMapPrototype.h	2021-03-31 07:21:37 UTC (rev 275271)
@@ -29,6 +29,10 @@
 
 namespace JSC {
 
+extern const ASCIILiteral WeakMapNonObjectKeyError;
+
+JSC_DECLARE_HOST_FUNCTION(protoFuncWeakMapSet);
+
 class WeakMapPrototype final : public JSNonFinalObject {
 public:
     using Base = JSNonFinalObject;

Modified: trunk/Source/_javascript_Core/runtime/WeakSetConstructor.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/WeakSetConstructor.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/WeakSetConstructor.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -75,10 +75,20 @@
 
     auto adderFunctionCallData = getCallData(vm, adderFunction);
     if (adderFunctionCallData.type == CallData::Type::None)
-        return JSValue::encode(throwTypeError(globalObject, scope));
+        return throwVMTypeError(globalObject, scope, "'add' property of a WeakSet should be callable."_s);
 
+    bool canPerformFastAdd = adderFunctionCallData.type == CallData::Type::Native && adderFunctionCallData.native.function == protoFuncWeakSetAdd;
+
     scope.release();
     forEachInIterable(globalObject, iterable, [&](VM&, JSGlobalObject* globalObject, JSValue nextValue) {
+        if (canPerformFastAdd) {
+            if (nextValue.isObject())
+                weakSet->add(vm, asObject(nextValue));
+            else
+                throwTypeError(asObject(adderFunction)->globalObject(vm), scope, WeakSetNonObjectValueError);
+            return;
+        }
+
         MarkedArgumentBuffer arguments;
         arguments.append(nextValue);
         ASSERT(!arguments.hasOverflowed());

Modified: trunk/Source/_javascript_Core/runtime/WeakSetPrototype.cpp (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/WeakSetPrototype.cpp	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/WeakSetPrototype.cpp	2021-03-31 07:21:37 UTC (rev 275271)
@@ -31,11 +31,12 @@
 
 namespace JSC {
 
+const ASCIILiteral WeakSetNonObjectValueError { "Attempted to add a non-object value to a WeakSet"_s };
+
 const ClassInfo WeakSetPrototype::s_info = { "WeakSet", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WeakSetPrototype) };
 
 static JSC_DECLARE_HOST_FUNCTION(protoFuncWeakSetDelete);
 static JSC_DECLARE_HOST_FUNCTION(protoFuncWeakSetHas);
-static JSC_DECLARE_HOST_FUNCTION(protoFuncWeakSetAdd);
 
 void WeakSetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
@@ -96,7 +97,7 @@
         return JSValue::encode(jsUndefined());
     JSValue key = callFrame->argument(0);
     if (!key.isObject())
-        return JSValue::encode(throwTypeError(globalObject, scope, "Attempted to add a non-object key to a WeakSet"_s));
+        return JSValue::encode(throwTypeError(globalObject, scope, WeakSetNonObjectValueError));
     set->add(vm, asObject(key));
     return JSValue::encode(callFrame->thisValue());
 }

Modified: trunk/Source/_javascript_Core/runtime/WeakSetPrototype.h (275270 => 275271)


--- trunk/Source/_javascript_Core/runtime/WeakSetPrototype.h	2021-03-31 05:52:41 UTC (rev 275270)
+++ trunk/Source/_javascript_Core/runtime/WeakSetPrototype.h	2021-03-31 07:21:37 UTC (rev 275271)
@@ -29,6 +29,10 @@
 
 namespace JSC {
 
+extern const ASCIILiteral WeakSetNonObjectValueError;
+
+JSC_DECLARE_HOST_FUNCTION(protoFuncWeakSetAdd);
+
 class WeakSetPrototype final : public JSNonFinalObject {
 public:
     using Base = JSNonFinalObject;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to