[JSC] Iterating over a Set/Map is too slow
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Jan 2016 05:54:03 +0000 (05:54 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Jan 2016 05:54:03 +0000 (05:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=152691

Reviewed by Saam Barati.

Source/JavaScriptCore:

Set#forEach and Set & for-of are very slow. There are 2 reasons.

1. forEach is implemented in C++. And typically, taking JS callback and calling it from C++.

C++ to JS transition seems costly. perf result in Linux machine shows this.

    Samples: 23K of event 'cycles', Event count (approx.): 21446074385
    34.04%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::Interpreter::execute(JSC::CallFrameClosure&)
    20.48%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] vmEntryToJavaScript
     9.80%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*)
     7.95%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::setProtoFuncForEach(JSC::ExecState*)
     5.65%  jsc  perf-22854.map                      [.] 0x00007f5d2c204a6f

Writing forEach in JS eliminates this.

    Samples: 23K of event 'cycles', Event count (approx.): 21255691651
    62.91%  jsc  perf-22890.map                      [.] 0x00007fd117c0a3b9
    24.89%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::privateFuncSetIteratorNext(JSC::ExecState*)
     0.29%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::CodeBlock::updateAllPredictionsAndCountLiveness(unsigned int&, unsigned int&)
     0.24%  jsc  [vdso]                              [.] 0x00000000000008e8
     0.22%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::CodeBlock::predictedMachineCodeSize()
     0.16%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] WTF::MetaAllocator::currentStatistics()
     0.15%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::Lexer<unsigned char>::lex(JSC::JSToken*, unsigned int, bool)

2. Iterator result object allocation is costly.

Iterator result object allocation is costly. Even if the (1) is solved, when executing Set & for-of, perf result shows very slow performance due to (2).

    Samples: 108K of event 'cycles', Event count (approx.): 95529273748
    18.02%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::createIteratorResultObject(JSC::ExecState*, JSC::JSValue, bool)
    15.68%  jsc  jsc                                 [.] JSC::JSObject::putDirect(JSC::VM&, JSC::PropertyName, JSC::JSValue, unsigned int)
    14.18%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::PrototypeMap::emptyObjectStructureForPrototype(JSC::JSObject*, unsigned int)
    13.40%  jsc  perf-25420.map                      [.] 0x00007fce158006a1
     6.79%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::StructureTransitionTable::get(WTF::UniquedStringImpl*, unsigned int) const

In the long term, we should implement SetIterator#next in JS and make the iterator result object allocation written in JS to encourage object allocation elimination in FTL.
But seeing the perf result, we can find the easy to fix bottleneck in the current implementation.
Every time createIteratorResultObject creates the empty object and use putDirect to store properties.
The pre-baked Structure* with `done` and `value` properties makes this implementation fast.

After these improvements, the micro benchmark[1] shows the following.

old:
    Linked List x 212,776 ops/sec ±0.21% (162 runs sampled)
    Array x 376,156 ops/sec ±0.20% (162 runs sampled)
    Array forEach x 17,345 ops/sec ±0.99% (137 runs sampled)
    Array for-of x 16,518 ops/sec ±0.58% (160 runs sampled)
    Set forEach x 13,263 ops/sec ±0.20% (162 runs sampled)
    Set for-of x 4,732 ops/sec ±0.34% (123 runs sampled)

new:
    Linked List x 210,833 ops/sec ±0.28% (161 runs sampled)
    Array x 371,347 ops/sec ±0.36% (162 runs sampled)
    Array forEach x 17,460 ops/sec ±0.84% (136 runs sampled)
    Array for-of x 16,188 ops/sec ±1.27% (158 runs sampled)
    Set forEach x 23,684 ops/sec ±2.46% (139 runs sampled)
    Set for-of x 12,176 ops/sec ±0.54% (157 runs sampled)

Set#forEach becomes comparable to Array#forEach. And Set#forEach and Set & for-of are improved (1.79x, and 2.57x).
After this optimizations, they are still much slower than linked list and array.
This should be optimized in the long term.

[1]: https://gist.github.com/Constellation/8db5f5b8f12fe7e283d0

* CMakeLists.txt:
* DerivedSources.make:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/MapPrototype.js: Copied from Source/JavaScriptCore/runtime/IteratorOperations.h.
(forEach):
* builtins/SetPrototype.js: Copied from Source/JavaScriptCore/runtime/IteratorOperations.h.
(forEach):
* runtime/CommonIdentifiers.h:
* runtime/IteratorOperations.cpp:
(JSC::createIteratorResultObjectStructure):
(JSC::createIteratorResultObject):
* runtime/IteratorOperations.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::iteratorResultObjectStructure):
(JSC::JSGlobalObject::iteratorResultStructure): Deleted.
(JSC::JSGlobalObject::iteratorResultStructureOffset): Deleted.
* runtime/MapPrototype.cpp:
(JSC::MapPrototype::getOwnPropertySlot):
(JSC::privateFuncIsMap):
(JSC::privateFuncMapIterator):
(JSC::privateFuncMapIteratorNext):
(JSC::MapPrototype::finishCreation): Deleted.
(JSC::mapProtoFuncForEach): Deleted.
* runtime/MapPrototype.h:
* runtime/SetPrototype.cpp:
(JSC::SetPrototype::getOwnPropertySlot):
(JSC::privateFuncIsSet):
(JSC::privateFuncSetIterator):
(JSC::privateFuncSetIteratorNext):
(JSC::SetPrototype::finishCreation): Deleted.
(JSC::setProtoFuncForEach): Deleted.
* runtime/SetPrototype.h:

LayoutTests:

Add regress tests.

* js/regress/map-for-each-expected.txt: Added.
* js/regress/map-for-each.html: Added.
* js/regress/map-for-of-expected.txt: Added.
* js/regress/map-for-of.html: Added.
* js/regress/script-tests/map-for-each.js: Added.
(createMap):
(i.map.forEach):
* js/regress/script-tests/map-for-of.js: Added.
(createMap):
* js/regress/script-tests/set-for-each.js: Added.
(set forEach):
(set createSet):
* js/regress/script-tests/set-for-of.js: Added.
* js/regress/set-for-each-expected.txt: Added.
* js/regress/set-for-each.html: Added.
* js/regress/set-for-of-expected.txt: Added.
* js/regress/set-for-of.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@194838 268f45cc-cd09-0410-ab3c-d52691b4dbfc

30 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/map-for-each-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/map-for-each.html [new file with mode: 0644]
LayoutTests/js/regress/map-for-of-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/map-for-of.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/map-for-each.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/map-for-of.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/set-for-each.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/set-for-of.js [new file with mode: 0644]
LayoutTests/js/regress/set-for-each-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/set-for-each.html [new file with mode: 0644]
LayoutTests/js/regress/set-for-of-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/set-for-of.html [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/DerivedSources.make
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/builtins/MapPrototype.js [new file with mode: 0644]
Source/JavaScriptCore/builtins/SetPrototype.js [new file with mode: 0644]
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/IteratorOperations.cpp
Source/JavaScriptCore/runtime/IteratorOperations.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/MapPrototype.cpp
Source/JavaScriptCore/runtime/MapPrototype.h
Source/JavaScriptCore/runtime/SetPrototype.cpp
Source/JavaScriptCore/runtime/SetPrototype.h

index a434a3d..6074558 100644 (file)
@@ -1,3 +1,30 @@
+2016-01-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Iterating over a Set/Map is too slow
+        https://bugs.webkit.org/show_bug.cgi?id=152691
+
+        Reviewed by Saam Barati.
+
+        Add regress tests.
+
+        * js/regress/map-for-each-expected.txt: Added.
+        * js/regress/map-for-each.html: Added.
+        * js/regress/map-for-of-expected.txt: Added.
+        * js/regress/map-for-of.html: Added.
+        * js/regress/script-tests/map-for-each.js: Added.
+        (createMap):
+        (i.map.forEach):
+        * js/regress/script-tests/map-for-of.js: Added.
+        (createMap):
+        * js/regress/script-tests/set-for-each.js: Added.
+        (set forEach):
+        (set createSet):
+        * js/regress/script-tests/set-for-of.js: Added.
+        * js/regress/set-for-each-expected.txt: Added.
+        * js/regress/set-for-each.html: Added.
+        * js/regress/set-for-of-expected.txt: Added.
+        * js/regress/set-for-of.html: Added.
+
 2016-01-09  Zalan Bujtas  <zalan@apple.com>
 
         REGRESSION (r194426): First email field is not autofilled on amazon.com
diff --git a/LayoutTests/js/regress/map-for-each-expected.txt b/LayoutTests/js/regress/map-for-each-expected.txt
new file mode 100644 (file)
index 0000000..1397dc6
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/map-for-each
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/map-for-each.html b/LayoutTests/js/regress/map-for-each.html
new file mode 100644 (file)
index 0000000..4ff1751
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/map-for-each.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/map-for-of-expected.txt b/LayoutTests/js/regress/map-for-of-expected.txt
new file mode 100644 (file)
index 0000000..4e4b384
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/map-for-of
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/map-for-of.html b/LayoutTests/js/regress/map-for-of.html
new file mode 100644 (file)
index 0000000..7ac9e5b
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/map-for-of.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/map-for-each.js b/LayoutTests/js/regress/script-tests/map-for-each.js
new file mode 100644 (file)
index 0000000..b459852
--- /dev/null
@@ -0,0 +1,23 @@
+var globalSum = 0;
+(function () {
+    function createMap(count) {
+        var map = new Map;
+        for (var i = 0; i < count; i++) {
+            map.set(i, i);
+        }
+        return map;
+    }
+
+    var COUNT = 2000;
+    var map = createMap(COUNT);
+    var sum = 0;
+
+    for (var i = 0; i < 1e2; ++i) {
+        sum = 0;
+        map.forEach(function (value, key) {
+            sum += key;
+        });
+        globalSum = sum;
+    }
+    return sum;
+}());
diff --git a/LayoutTests/js/regress/script-tests/map-for-of.js b/LayoutTests/js/regress/script-tests/map-for-of.js
new file mode 100644 (file)
index 0000000..69e11b7
--- /dev/null
@@ -0,0 +1,23 @@
+var globalSum = 0;
+(function () {
+    function createMap(count) {
+        var map = new Map;
+        for (var i = 0; i < count; i++) {
+            map.set(i, i);
+        }
+        return map;
+    }
+
+    var COUNT = 2000;
+    var map = createMap(COUNT);
+    var sum = 0;
+
+    for (var i = 0; i < 1e2; ++i) {
+        sum = 0;
+        for (var item of map) {
+            sum += item[0];
+        }
+        globalSum = sum;
+    }
+    return sum;
+}());
diff --git a/LayoutTests/js/regress/script-tests/set-for-each.js b/LayoutTests/js/regress/script-tests/set-for-each.js
new file mode 100644 (file)
index 0000000..00d6653
--- /dev/null
@@ -0,0 +1,23 @@
+var globalSum = 0;
+(function () {
+    function createSet(count) {
+        var set = new Set;
+        for (var i = 0; i < count; i++) {
+            set.add(i);
+        }
+        return set;
+    }
+
+    var COUNT = 2000;
+    var set = createSet(COUNT);
+    var sum = 0;
+
+    for (var i = 0; i < 1e2; ++i) {
+        sum = 0;
+        set.forEach(function (item) {
+            sum += item;
+        });
+        globalSum = sum;
+    }
+    return sum;
+}());
diff --git a/LayoutTests/js/regress/script-tests/set-for-of.js b/LayoutTests/js/regress/script-tests/set-for-of.js
new file mode 100644 (file)
index 0000000..a52b389
--- /dev/null
@@ -0,0 +1,23 @@
+var globalSum = 0;
+(function () {
+    function createSet(count) {
+        var set = new Set;
+        for (var i = 0; i < count; i++) {
+            set.add(i);
+        }
+        return set;
+    }
+
+    var COUNT = 2000;
+    var set = createSet(COUNT);
+    var sum = 0;
+
+    for (var i = 0; i < 1e2; ++i) {
+        sum = 0;
+        for (var item of set) {
+            sum += item;
+        }
+        globalSum = sum;
+    }
+    return sum;
+}());
diff --git a/LayoutTests/js/regress/set-for-each-expected.txt b/LayoutTests/js/regress/set-for-each-expected.txt
new file mode 100644 (file)
index 0000000..a0366c8
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/set-for-each
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/set-for-each.html b/LayoutTests/js/regress/set-for-each.html
new file mode 100644 (file)
index 0000000..472cfc9
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/set-for-each.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/set-for-of-expected.txt b/LayoutTests/js/regress/set-for-of-expected.txt
new file mode 100644 (file)
index 0000000..b18ddc7
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/set-for-of
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/set-for-of.html b/LayoutTests/js/regress/set-for-of.html
new file mode 100644 (file)
index 0000000..882f64c
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/set-for-of.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 7c35773..4987d43 100644 (file)
@@ -794,6 +794,7 @@ set(JavaScriptCore_OBJECT_LUT_SOURCES
     runtime/JSONObject.cpp
     runtime/JSPromiseConstructor.cpp
     runtime/JSPromisePrototype.cpp
+    runtime/MapPrototype.cpp
     runtime/ModuleLoaderObject.cpp
     runtime/NumberConstructor.cpp
     runtime/NumberPrototype.cpp
@@ -801,6 +802,7 @@ set(JavaScriptCore_OBJECT_LUT_SOURCES
     runtime/ReflectObject.cpp
     runtime/RegExpConstructor.cpp
     runtime/RegExpPrototype.cpp
+    runtime/SetPrototype.cpp
     runtime/StringConstructor.cpp
     runtime/StringIteratorPrototype.cpp
     runtime/SymbolConstructor.cpp
@@ -1237,6 +1239,7 @@ set(JavaScriptCore_BUILTINS_SOURCES
     ${JAVASCRIPTCORE_DIR}/builtins/InspectorInstrumentationObject.js
     ${JAVASCRIPTCORE_DIR}/builtins/InternalPromiseConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/IteratorPrototype.js
+    ${JAVASCRIPTCORE_DIR}/builtins/MapPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ModuleLoaderObject.js
     ${JAVASCRIPTCORE_DIR}/builtins/NumberPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ObjectConstructor.js
@@ -1244,6 +1247,7 @@ set(JavaScriptCore_BUILTINS_SOURCES
     ${JAVASCRIPTCORE_DIR}/builtins/PromiseOperations.js
     ${JAVASCRIPTCORE_DIR}/builtins/PromisePrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ReflectObject.js
+    ${JAVASCRIPTCORE_DIR}/builtins/SetPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/StringConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/StringIteratorPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/StringPrototype.js
index ef6750b..5aa4d9b 100644 (file)
@@ -1,3 +1,112 @@
+2016-01-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Iterating over a Set/Map is too slow
+        https://bugs.webkit.org/show_bug.cgi?id=152691
+
+        Reviewed by Saam Barati.
+
+        Set#forEach and Set & for-of are very slow. There are 2 reasons.
+
+        1. forEach is implemented in C++. And typically, taking JS callback and calling it from C++.
+
+        C++ to JS transition seems costly. perf result in Linux machine shows this.
+
+            Samples: 23K of event 'cycles', Event count (approx.): 21446074385
+            34.04%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::Interpreter::execute(JSC::CallFrameClosure&)
+            20.48%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] vmEntryToJavaScript
+             9.80%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*)
+             7.95%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::setProtoFuncForEach(JSC::ExecState*)
+             5.65%  jsc  perf-22854.map                      [.] 0x00007f5d2c204a6f
+
+        Writing forEach in JS eliminates this.
+
+            Samples: 23K of event 'cycles', Event count (approx.): 21255691651
+            62.91%  jsc  perf-22890.map                      [.] 0x00007fd117c0a3b9
+            24.89%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::privateFuncSetIteratorNext(JSC::ExecState*)
+             0.29%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::CodeBlock::updateAllPredictionsAndCountLiveness(unsigned int&, unsigned int&)
+             0.24%  jsc  [vdso]                              [.] 0x00000000000008e8
+             0.22%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::CodeBlock::predictedMachineCodeSize()
+             0.16%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] WTF::MetaAllocator::currentStatistics()
+             0.15%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::Lexer<unsigned char>::lex(JSC::JSToken*, unsigned int, bool)
+
+        2. Iterator result object allocation is costly.
+
+        Iterator result object allocation is costly. Even if the (1) is solved, when executing Set & for-of, perf result shows very slow performance due to (2).
+
+            Samples: 108K of event 'cycles', Event count (approx.): 95529273748
+            18.02%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::createIteratorResultObject(JSC::ExecState*, JSC::JSValue, bool)
+            15.68%  jsc  jsc                                 [.] JSC::JSObject::putDirect(JSC::VM&, JSC::PropertyName, JSC::JSValue, unsigned int)
+            14.18%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::PrototypeMap::emptyObjectStructureForPrototype(JSC::JSObject*, unsigned int)
+            13.40%  jsc  perf-25420.map                      [.] 0x00007fce158006a1
+             6.79%  jsc  libjavascriptcoregtk-4.0.so.18.3.1  [.] JSC::StructureTransitionTable::get(WTF::UniquedStringImpl*, unsigned int) const
+
+        In the long term, we should implement SetIterator#next in JS and make the iterator result object allocation written in JS to encourage object allocation elimination in FTL.
+        But seeing the perf result, we can find the easy to fix bottleneck in the current implementation.
+        Every time createIteratorResultObject creates the empty object and use putDirect to store properties.
+        The pre-baked Structure* with `done` and `value` properties makes this implementation fast.
+
+        After these improvements, the micro benchmark[1] shows the following.
+
+        old:
+            Linked List x 212,776 ops/sec ±0.21% (162 runs sampled)
+            Array x 376,156 ops/sec ±0.20% (162 runs sampled)
+            Array forEach x 17,345 ops/sec ±0.99% (137 runs sampled)
+            Array for-of x 16,518 ops/sec ±0.58% (160 runs sampled)
+            Set forEach x 13,263 ops/sec ±0.20% (162 runs sampled)
+            Set for-of x 4,732 ops/sec ±0.34% (123 runs sampled)
+
+        new:
+            Linked List x 210,833 ops/sec ±0.28% (161 runs sampled)
+            Array x 371,347 ops/sec ±0.36% (162 runs sampled)
+            Array forEach x 17,460 ops/sec ±0.84% (136 runs sampled)
+            Array for-of x 16,188 ops/sec ±1.27% (158 runs sampled)
+            Set forEach x 23,684 ops/sec ±2.46% (139 runs sampled)
+            Set for-of x 12,176 ops/sec ±0.54% (157 runs sampled)
+
+        Set#forEach becomes comparable to Array#forEach. And Set#forEach and Set & for-of are improved (1.79x, and 2.57x).
+        After this optimizations, they are still much slower than linked list and array.
+        This should be optimized in the long term.
+
+        [1]: https://gist.github.com/Constellation/8db5f5b8f12fe7e283d0
+
+        * CMakeLists.txt:
+        * DerivedSources.make:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * builtins/MapPrototype.js: Copied from Source/JavaScriptCore/runtime/IteratorOperations.h.
+        (forEach):
+        * builtins/SetPrototype.js: Copied from Source/JavaScriptCore/runtime/IteratorOperations.h.
+        (forEach):
+        * runtime/CommonIdentifiers.h:
+        * runtime/IteratorOperations.cpp:
+        (JSC::createIteratorResultObjectStructure):
+        (JSC::createIteratorResultObject):
+        * runtime/IteratorOperations.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::iteratorResultObjectStructure):
+        (JSC::JSGlobalObject::iteratorResultStructure): Deleted.
+        (JSC::JSGlobalObject::iteratorResultStructureOffset): Deleted.
+        * runtime/MapPrototype.cpp:
+        (JSC::MapPrototype::getOwnPropertySlot):
+        (JSC::privateFuncIsMap):
+        (JSC::privateFuncMapIterator):
+        (JSC::privateFuncMapIteratorNext):
+        (JSC::MapPrototype::finishCreation): Deleted.
+        (JSC::mapProtoFuncForEach): Deleted.
+        * runtime/MapPrototype.h:
+        * runtime/SetPrototype.cpp:
+        (JSC::SetPrototype::getOwnPropertySlot):
+        (JSC::privateFuncIsSet):
+        (JSC::privateFuncSetIterator):
+        (JSC::privateFuncSetIteratorNext):
+        (JSC::SetPrototype::finishCreation): Deleted.
+        (JSC::setProtoFuncForEach): Deleted.
+        * runtime/SetPrototype.h:
+
 2016-01-10  Filip Pizlo  <fpizlo@apple.com>
 
         Unreviewed, fix ARM64 build.
index 1ee394f..55016fb 100644 (file)
@@ -89,6 +89,7 @@ JavaScriptCore_BUILTINS_SOURCES = \
     $(JavaScriptCore)/builtins/InspectorInstrumentationObject.js \
     $(JavaScriptCore)/builtins/InternalPromiseConstructor.js \
     $(JavaScriptCore)/builtins/IteratorPrototype.js \
+    $(JavaScriptCore)/builtins/MapPrototype.js \
     $(JavaScriptCore)/builtins/ModuleLoaderObject.js \
     $(JavaScriptCore)/builtins/NumberPrototype.js \
     $(JavaScriptCore)/builtins/ObjectConstructor.js \
@@ -96,6 +97,7 @@ JavaScriptCore_BUILTINS_SOURCES = \
     $(JavaScriptCore)/builtins/PromiseOperations.js \
     $(JavaScriptCore)/builtins/PromisePrototype.js \
     $(JavaScriptCore)/builtins/ReflectObject.js \
+    $(JavaScriptCore)/builtins/SetPrototype.js \
     $(JavaScriptCore)/builtins/StringConstructor.js \
     $(JavaScriptCore)/builtins/StringIteratorPrototype.js \
     $(JavaScriptCore)/builtins/StringPrototype.js \
@@ -136,6 +138,7 @@ OBJECT_LUT_HEADERS = \
     JSONObject.lut.h \
     JSPromisePrototype.lut.h \
     JSPromiseConstructor.lut.h \
+    MapPrototype.lut.h \
     ModuleLoaderObject.lut.h \
     NumberConstructor.lut.h \
     NumberPrototype.lut.h \
@@ -143,6 +146,7 @@ OBJECT_LUT_HEADERS = \
     ReflectObject.lut.h \
     RegExpConstructor.lut.h \
     RegExpPrototype.lut.h \
+    SetPrototype.lut.h \
     StringConstructor.lut.h \
     StringIteratorPrototype.lut.h \
     SymbolConstructor.lut.h \
index 635d34a..1ae8404 100644 (file)
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSPromisePrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\KeywordLookup.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\Lexer.lut.h" />
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\MapPrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\NumberConstructor.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\NumberPrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\ObjectConstructor.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\RegExpConstructor.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\RegExpJitTables.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\RegExpPrototype.lut.h" />
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\SetPrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\StringConstructor.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\SymbolPrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSCBuiltins.h" />
index daeaad1..57ac190 100644 (file)
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\RegExpConstructor.lut.h">
       <Filter>Derived Sources</Filter>
     </ClInclude>
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\MapPrototype.lut.h">
+      <Filter>Derived Sources</Filter>
+    </ClInclude>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\NumberPrototype.lut.h">
       <Filter>Derived Sources</Filter>
     </ClInclude>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\RegExpPrototype.lut.h">
       <Filter>Derived Sources</Filter>
     </ClInclude>
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\SetPrototype.lut.h">
+      <Filter>Derived Sources</Filter>
+    </ClInclude>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\StringConstructor.lut.h">
       <Filter>Derived Sources</Filter>
     </ClInclude>
index 9109dd9..d8b5871 100644 (file)
                70113D4A1A8DB093003848C4 /* IteratorOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IteratorOperations.h; sourceTree = "<group>"; };
                7013CA891B491A9400CAE613 /* JSJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSJob.cpp; sourceTree = "<group>"; };
                7013CA8A1B491A9400CAE613 /* JSJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSJob.h; sourceTree = "<group>"; };
+               7035587C1C418419004BD7BF /* MapPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = MapPrototype.js; sourceTree = "<group>"; };
+               7035587D1C418419004BD7BF /* SetPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SetPrototype.js; sourceTree = "<group>"; };
+               7035587E1C418458004BD7BF /* MapPrototype.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MapPrototype.lut.h; path = MapPrototype.lut.h; sourceTree = "<group>"; };
+               7035587F1C418458004BD7BF /* SetPrototype.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetPrototype.lut.h; path = SetPrototype.lut.h; sourceTree = "<group>"; };
                704FD35305697E6D003DBED9 /* BooleanObject.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = BooleanObject.h; sourceTree = "<group>"; tabWidth = 8; };
                705B41A31A6E501E00716757 /* Symbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Symbol.cpp; sourceTree = "<group>"; };
                705B41A41A6E501E00716757 /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = "<group>"; };
                                A7C225CD1399849C00FF1662 /* KeywordLookup.h */,
                                BC18C52D0E16FCE100B34460 /* Lexer.lut.h */,
                                70DE9A081BE7D670005D89D9 /* LLIntAssembly.h */,
+                               7035587E1C418458004BD7BF /* MapPrototype.lut.h */,
                                996B730C1BD9FA2C00331B84 /* ModuleLoaderObject.lut.h */,
                                BC2680E60E16D52300A06E92 /* NumberConstructor.lut.h */,
                                996B730D1BD9FA2C00331B84 /* NumberPrototype.lut.h */,
                                BCD202D50E170708002C7E82 /* RegExpConstructor.lut.h */,
                                A718F61A11754A21002465A7 /* RegExpJitTables.h */,
                                996B73101BD9FA2C00331B84 /* RegExpPrototype.lut.h */,
+                               7035587F1C418458004BD7BF /* SetPrototype.lut.h */,
                                996B73111BD9FA2C00331B84 /* StringConstructor.lut.h */,
                                996B73121BD9FA2C00331B84 /* StringIteratorPrototype.lut.h */,
                                996B73131BD9FA2C00331B84 /* SymbolConstructor.lut.h */,
                                E35E03611B7AB4850073AD2A /* InspectorInstrumentationObject.js */,
                                E33F50881B844A1A00413856 /* InternalPromiseConstructor.js */,
                                7CF9BC5B1B65D9A3009DB1EF /* IteratorPrototype.js */,
+                               7035587C1C418419004BD7BF /* MapPrototype.js */,
                                E30677971B8BC6F5003F87F0 /* ModuleLoaderObject.js */,
                                A15DE5C51C0FBF8D0089133D /* NumberPrototype.js */,
                                7CF9BC5C1B65D9B1009DB1EF /* ObjectConstructor.js */,
                                7CFBAC1C18B535E500D00750 /* PromisePrototype.js */,
                                7CF9BC5E1B65D9B1009DB1EF /* PromiseConstructor.js */,
                                7CF9BC5F1B65D9B1009DB1EF /* ReflectObject.js */,
+                               7035587D1C418419004BD7BF /* SetPrototype.js */,
                                7CF9BC601B65D9B1009DB1EF /* StringConstructor.js */,
                                7CF9BC611B65D9B1009DB1EF /* StringIteratorPrototype.js */,
                                A1E0451B1C25B4B100BB663C /* StringPrototype.js */,
diff --git a/Source/JavaScriptCore/builtins/MapPrototype.js b/Source/JavaScriptCore/builtins/MapPrototype.js
new file mode 100644 (file)
index 0000000..63646a8
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function forEach(callback /*, thisArg */)
+{
+    "use strict";
+
+    if (!@isMap(this))
+        throw new @TypeError("Map operation called on non-Map object");
+
+    if (typeof callback !== 'function')
+        throw new @TypeError("Map.prototype.forEach callback must be a function");
+
+    var thisArg = arguments.length > 1 ? arguments[1] : undefined;
+    var iterator = @MapIterator(this);
+
+    // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results.
+    var value = [ undefined, undefined ];
+    for (;;) {
+        if (@mapIteratorNext.@call(iterator, value))
+            break;
+        callback.@call(thisArg, value[1], value[0], this);
+    }
+}
diff --git a/Source/JavaScriptCore/builtins/SetPrototype.js b/Source/JavaScriptCore/builtins/SetPrototype.js
new file mode 100644 (file)
index 0000000..ab89c6e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function forEach(callback /*, thisArg */)
+{
+    "use strict";
+
+    if (!@isSet(this))
+        throw new @TypeError("Set operation called on non-Set object");
+
+    if (typeof callback !== 'function')
+        throw new @TypeError("Set.prototype.forEach callback must be a function");
+
+    var thisArg = arguments.length > 1 ? arguments[1] : undefined;
+    var iterator = @SetIterator(this);
+
+    // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results.
+    var value = [ undefined ];
+    for (;;) {
+        if (@setIteratorNext.@call(iterator, value))
+            break;
+        callback.@call(thisArg, value[0], value[0], this);
+    }
+}
index b51028f..0c5a8a0 100644 (file)
     macro(isBoundFunction) \
     macro(hasInstanceBoundFunction) \
     macro(instanceOf) \
+    macro(isSet) \
+    macro(isMap) \
+    macro(SetIterator) \
+    macro(setIteratorNext) \
+    macro(MapIterator) \
+    macro(mapIteratorNext) \
 
 
 namespace JSC {
index 2e886bf..7dcd07b 100644 (file)
@@ -131,11 +131,25 @@ void iteratorClose(ExecState* exec, JSValue iterator)
     }
 }
 
+static const PropertyOffset donePropertyOffset = 0;
+static const PropertyOffset valuePropertyOffset = 1;
+
+Structure* createIteratorResultObjectStructure(VM& vm, JSGlobalObject& globalObject)
+{
+    Structure* iteratorResultStructure = vm.prototypeMap.emptyObjectStructureForPrototype(globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity());
+    PropertyOffset offset;
+    iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->done, 0, offset);
+    RELEASE_ASSERT(offset == donePropertyOffset);
+    iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->value, 0, offset);
+    RELEASE_ASSERT(offset == valuePropertyOffset);
+    return iteratorResultStructure;
+}
+
 JSObject* createIteratorResultObject(ExecState* exec, JSValue value, bool done)
 {
-    JSObject* resultObject = constructEmptyObject(exec);
-    resultObject->putDirect(exec->vm(), exec->propertyNames().done, jsBoolean(done));
-    resultObject->putDirect(exec->vm(), exec->propertyNames().value, value);
+    JSObject* resultObject = constructEmptyObject(exec, exec->lexicalGlobalObject()->iteratorResultObjectStructure());
+    resultObject->putDirect(exec->vm(), donePropertyOffset, jsBoolean(done));
+    resultObject->putDirect(exec->vm(), valuePropertyOffset, value);
     return resultObject;
 }
 
index a9a300c..faee46a 100644 (file)
@@ -39,6 +39,8 @@ JSValue iteratorStep(ExecState*, JSValue iterator);
 void iteratorClose(ExecState*, JSValue iterator);
 JS_EXPORT_PRIVATE JSObject* createIteratorResultObject(ExecState*, JSValue, bool done);
 
+Structure* createIteratorResultObjectStructure(VM&, JSGlobalObject&);
+
 }
 
 #endif // !defined(IteratorOperations_h)
index 8bc9190..769a3ef 100644 (file)
@@ -457,12 +457,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     FOR_EACH_SIMPLE_BUILTIN_TYPE_WITH_CONSTRUCTOR(PUT_CONSTRUCTOR_FOR_SIMPLE_TYPE)
 
 #undef PUT_CONSTRUCTOR_FOR_SIMPLE_TYPE
-    PrototypeMap& prototypeMap = vm.prototypeMap;
-    Structure* iteratorResultStructure = prototypeMap.emptyObjectStructureForPrototype(m_objectPrototype.get(), JSFinalObject::defaultInlineCapacity());
-    PropertyOffset offset;
-    iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->done, 0, offset);
-    iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->value, 0, offset);
-    m_iteratorResultStructure.set(vm, this, iteratorResultStructure);
+    m_iteratorResultObjectStructure.set(vm, this, createIteratorResultObjectStructure(vm, *this));
     
     m_evalFunction.set(vm, this, JSFunction::create(vm, this, 1, vm.propertyNames->eval.string(), globalFuncEval));
     putDirectWithoutTransition(vm, vm.propertyNames->eval, m_evalFunction.get(), DontEnum);
@@ -556,6 +551,14 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->PromisePrivateName, promiseConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->InternalPromisePrivateName, internalPromiseConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->promisePendingPrivateName, jsNumber(static_cast<unsigned>(JSPromise::Status::Pending)), DontEnum | DontDelete | ReadOnly),
+
+        GlobalPropertyInfo(vm.propertyNames->isSetPrivateName, JSFunction::create(vm, this, 1, String(), privateFuncIsSet), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->SetIteratorPrivateName, JSFunction::create(vm, this, 1, String(), privateFuncSetIterator), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->setIteratorNextPrivateName, JSFunction::create(vm, this, 0, String(), privateFuncSetIteratorNext), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->isMapPrivateName, JSFunction::create(vm, this, 1, String(), privateFuncIsMap), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->MapIteratorPrivateName, JSFunction::create(vm, this, 1, String(), privateFuncMapIterator), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->mapIteratorNextPrivateName, JSFunction::create(vm, this, 0, String(), privateFuncMapIteratorNext), DontEnum | DontDelete | ReadOnly),
+
         GlobalPropertyInfo(vm.propertyNames->builtinNames().promiseFulfilledPrivateName(), jsNumber(static_cast<unsigned>(JSPromise::Status::Fulfilled)), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().promiseRejectedPrivateName(), jsNumber(static_cast<unsigned>(JSPromise::Status::Rejected)), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().toLengthPrivateName(), privateFuncToLength, DontEnum | DontDelete | ReadOnly),
@@ -889,6 +892,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(&thisObject->m_symbolObjectStructure);
     visitor.append(&thisObject->m_regExpStructure);
     visitor.append(&thisObject->m_generatorFunctionStructure);
+    visitor.append(&thisObject->m_iteratorResultObjectStructure);
     visitor.append(&thisObject->m_regExpMatchesArrayStructure);
     visitor.append(&thisObject->m_moduleRecordStructure);
     visitor.append(&thisObject->m_moduleNamespaceObjectStructure);
index 57e3a78..387221b 100644 (file)
@@ -271,7 +271,7 @@ protected:
     WriteBarrier<Structure> m_consoleStructure;
     WriteBarrier<Structure> m_dollarVMStructure;
     WriteBarrier<Structure> m_internalFunctionStructure;
-    WriteBarrier<Structure> m_iteratorResultStructure;
+    WriteBarrier<Structure> m_iteratorResultObjectStructure;
     WriteBarrier<Structure> m_regExpMatchesArrayStructure;
     WriteBarrier<Structure> m_moduleRecordStructure;
     WriteBarrier<Structure> m_moduleNamespaceObjectStructure;
@@ -522,8 +522,7 @@ public:
     Structure* setStructure() const { return m_setStructure.get(); }
     Structure* stringObjectStructure() const { return m_stringObjectStructure.get(); }
     Structure* symbolObjectStructure() const { return m_symbolObjectStructure.get(); }
-    Structure* iteratorResultStructure() const { return m_iteratorResultStructure.get(); }
-    static ptrdiff_t iteratorResultStructureOffset() { return OBJECT_OFFSETOF(JSGlobalObject, m_iteratorResultStructure); }
+    Structure* iteratorResultObjectStructure() const { return m_iteratorResultObjectStructure.get(); }
     Structure* regExpMatchesArrayStructure() const { return m_regExpMatchesArrayStructure.get(); }
     Structure* moduleRecordStructure() const { return m_moduleRecordStructure.get(); }
     Structure* moduleNamespaceObjectStructure() const { return m_moduleNamespaceObjectStructure.get(); }
index be790dd..c901285 100644 (file)
 #include "Error.h"
 #include "ExceptionHelpers.h"
 #include "GetterSetter.h"
+#include "IteratorOperations.h"
 #include "JSCJSValueInlines.h"
 #include "JSFunctionInlines.h"
 #include "JSMap.h"
 #include "JSMapIterator.h"
+#include "Lookup.h"
 #include "MapDataInlines.h"
 #include "StructureInlines.h"
 
+#include "MapPrototype.lut.h"
+
 namespace JSC {
 
 const ClassInfo MapPrototype::s_info = { "Map", &Base::s_info, 0, CREATE_METHOD_TABLE(MapPrototype) };
 
+/* Source for MapPrototype.lut.h
+@begin mapPrototypeTable
+  forEach   JSBuiltin  DontEnum|Function 0
+@end
+*/
+
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncClear(ExecState*);
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncDelete(ExecState*);
-static EncodedJSValue JSC_HOST_CALL mapProtoFuncForEach(ExecState*);
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncGet(ExecState*);
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncHas(ExecState*);
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncSet(ExecState*);
@@ -61,7 +70,6 @@ void MapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 
     JSC_NATIVE_FUNCTION(vm.propertyNames->clear, mapProtoFuncClear, DontEnum, 0);
     JSC_NATIVE_FUNCTION(vm.propertyNames->deleteKeyword, mapProtoFuncDelete, DontEnum, 1);
-    JSC_NATIVE_FUNCTION(vm.propertyNames->forEach, mapProtoFuncForEach, DontEnum, 1);
     JSC_NATIVE_FUNCTION(vm.propertyNames->get, mapProtoFuncGet, DontEnum, 1);
     JSC_NATIVE_FUNCTION(vm.propertyNames->has, mapProtoFuncHas, DontEnum, 1);
     JSC_NATIVE_FUNCTION(vm.propertyNames->set, mapProtoFuncSet, DontEnum, 2);
@@ -83,6 +91,11 @@ void MapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     putDirectNonIndexAccessor(vm, vm.propertyNames->size, accessor, DontEnum | Accessor);
 }
 
+bool MapPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+    return getStaticFunctionSlot<Base>(exec, mapPrototypeTable, jsCast<MapPrototype*>(object), propertyName, slot);
+}
+
 ALWAYS_INLINE static JSMap* getMap(CallFrame* callFrame, JSValue thisValue)
 {
     if (!thisValue.isObject()) {
@@ -114,44 +127,6 @@ EncodedJSValue JSC_HOST_CALL mapProtoFuncDelete(CallFrame* callFrame)
     return JSValue::encode(jsBoolean(map->remove(callFrame, callFrame->argument(0))));
 }
 
-EncodedJSValue JSC_HOST_CALL mapProtoFuncForEach(CallFrame* callFrame)
-{
-    JSMap* map = getMap(callFrame, callFrame->thisValue());
-    if (!map)
-        return JSValue::encode(jsUndefined());
-    JSValue callBack = callFrame->argument(0);
-    CallData callData;
-    CallType callType = getCallData(callBack, callData);
-    if (callType == CallTypeNone)
-        return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral("Map.prototype.forEach called without callback")));
-    JSValue thisValue = callFrame->argument(1);
-    VM* vm = &callFrame->vm();
-    JSMapIterator* iterator = JSMapIterator::create(*vm, callFrame->callee()->globalObject()->mapIteratorStructure(), map, MapIterateKeyValue);
-    JSValue key, value;
-    if (callType == CallTypeJS) {
-        JSFunction* function = jsCast<JSFunction*>(callBack);
-        CachedCall cachedCall(callFrame, function, 3);
-        while (iterator->nextKeyValue(key, value) && !vm->exception()) {
-            cachedCall.setThis(thisValue);
-            cachedCall.setArgument(0, value);
-            cachedCall.setArgument(1, key);
-            cachedCall.setArgument(2, map);
-            cachedCall.call();
-        }
-        iterator->finish();
-    } else {
-        while (iterator->nextKeyValue(key, value) && !vm->exception()) {
-            MarkedArgumentBuffer args;
-            args.append(value);
-            args.append(key);
-            args.append(map);
-            JSC::call(callFrame, callBack, callType, callData, thisValue, args);
-        }
-        iterator->finish();
-    }
-    return JSValue::encode(jsUndefined());
-}
-
 EncodedJSValue JSC_HOST_CALL mapProtoFuncGet(CallFrame* callFrame)
 {
     JSMap* map = getMap(callFrame, callFrame->thisValue());
@@ -210,4 +185,31 @@ EncodedJSValue JSC_HOST_CALL mapProtoFuncKeys(CallFrame* callFrame)
     return JSValue::encode(JSMapIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->mapIteratorStructure(), thisObj, MapIterateKey));
 }
 
+EncodedJSValue JSC_HOST_CALL privateFuncIsMap(ExecState* exec)
+{
+    return JSValue::encode(jsBoolean(jsDynamicCast<JSMap*>(exec->uncheckedArgument(0))));
+}
+
+EncodedJSValue JSC_HOST_CALL privateFuncMapIterator(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSMap*>(exec->uncheckedArgument(0)));
+    JSMap* map = jsCast<JSMap*>(exec->uncheckedArgument(0));
+    return JSValue::encode(JSMapIterator::create(exec->vm(), exec->callee()->globalObject()->mapIteratorStructure(), map, MapIterateKeyValue));
+}
+
+EncodedJSValue JSC_HOST_CALL privateFuncMapIteratorNext(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSMapIterator*>(exec->thisValue()));
+    JSMapIterator* iterator = jsCast<JSMapIterator*>(exec->thisValue());
+    JSValue key, value;
+    if (iterator->nextKeyValue(key, value)) {
+        JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
+        resultArray->putDirectIndex(exec, 0, key);
+        resultArray->putDirectIndex(exec, 1, value);
+        return JSValue::encode(jsBoolean(false));
+    }
+    iterator->finish();
+    return JSValue::encode(jsBoolean(true));
+}
+
 }
index eaa5825..3e72567 100644 (file)
@@ -34,6 +34,8 @@ class MapPrototype : public JSNonFinalObject {
 public:
     typedef JSNonFinalObject Base;
 
+    static const unsigned StructureFlags = OverridesGetOwnPropertySlot | Base::StructureFlags;
+
     static MapPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
     {
         MapPrototype* prototype = new (NotNull, allocateCell<MapPrototype>(vm.heap)) MapPrototype(vm, structure);
@@ -54,8 +56,13 @@ private:
     {
     }
     void finishCreation(VM&, JSGlobalObject*);
+    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
 };
 
+EncodedJSValue JSC_HOST_CALL privateFuncIsMap(ExecState*);
+EncodedJSValue JSC_HOST_CALL privateFuncMapIterator(ExecState*);
+EncodedJSValue JSC_HOST_CALL privateFuncMapIteratorNext(ExecState*);
+
 }
 
 #endif // !defined(MapPrototype_h)
index ddeee37..12014f3 100644 (file)
 #include "Error.h"
 #include "ExceptionHelpers.h"
 #include "GetterSetter.h"
+#include "IteratorOperations.h"
 #include "JSCJSValueInlines.h"
 #include "JSFunctionInlines.h"
 #include "JSSet.h"
 #include "JSSetIterator.h"
+#include "Lookup.h"
 #include "MapDataInlines.h"
 #include "StructureInlines.h"
 
+#include "SetPrototype.lut.h"
+
 namespace JSC {
 
 const ClassInfo SetPrototype::s_info = { "Set", &Base::s_info, 0, CREATE_METHOD_TABLE(SetPrototype) };
 
+/* Source for SetIteratorPrototype.lut.h
+@begin setPrototypeTable
+  forEach   JSBuiltin  DontEnum|Function 0
+@end
+*/
+
 static EncodedJSValue JSC_HOST_CALL setProtoFuncAdd(ExecState*);
 static EncodedJSValue JSC_HOST_CALL setProtoFuncClear(ExecState*);
 static EncodedJSValue JSC_HOST_CALL setProtoFuncDelete(ExecState*);
-static EncodedJSValue JSC_HOST_CALL setProtoFuncForEach(ExecState*);
 static EncodedJSValue JSC_HOST_CALL setProtoFuncHas(ExecState*);
 static EncodedJSValue JSC_HOST_CALL setProtoFuncValues(ExecState*);
 static EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(ExecState*);
@@ -61,7 +70,6 @@ void SetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     JSC_NATIVE_FUNCTION(vm.propertyNames->add, setProtoFuncAdd, DontEnum, 1);
     JSC_NATIVE_FUNCTION(vm.propertyNames->clear, setProtoFuncClear, DontEnum, 0);
     JSC_NATIVE_FUNCTION(vm.propertyNames->deleteKeyword, setProtoFuncDelete, DontEnum, 1);
-    JSC_NATIVE_FUNCTION(vm.propertyNames->forEach, setProtoFuncForEach, DontEnum, 1);
     JSC_NATIVE_FUNCTION(vm.propertyNames->has, setProtoFuncHas, DontEnum, 1);
     JSC_NATIVE_FUNCTION(vm.propertyNames->entries, setProtoFuncEntries, DontEnum, 0);
 
@@ -77,6 +85,12 @@ void SetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     putDirectNonIndexAccessor(vm, vm.propertyNames->size, accessor, DontEnum | Accessor);
 }
 
+bool SetPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+    return getStaticFunctionSlot<Base>(exec, setPrototypeTable, jsCast<SetPrototype*>(object), propertyName, slot);
+}
+
+
 ALWAYS_INLINE static JSSet* getSet(CallFrame* callFrame, JSValue thisValue)
 {
     if (!thisValue.isObject()) {
@@ -118,44 +132,6 @@ EncodedJSValue JSC_HOST_CALL setProtoFuncDelete(CallFrame* callFrame)
     return JSValue::encode(jsBoolean(set->remove(callFrame, callFrame->argument(0))));
 }
 
-EncodedJSValue JSC_HOST_CALL setProtoFuncForEach(CallFrame* callFrame)
-{
-    JSSet* set = getSet(callFrame, callFrame->thisValue());
-    if (!set)
-        return JSValue::encode(jsUndefined());
-    JSValue callBack = callFrame->argument(0);
-    CallData callData;
-    CallType callType = getCallData(callBack, callData);
-    if (callType == CallTypeNone)
-        return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral("Set.prototype.forEach called without callback")));
-    JSValue thisValue = callFrame->argument(1);
-    VM* vm = &callFrame->vm();
-    JSSetIterator* iterator = JSSetIterator::create(*vm, callFrame->callee()->globalObject()->setIteratorStructure(), set, SetIterateKey);
-    JSValue key;
-    if (callType == CallTypeJS) {
-        JSFunction* function = jsCast<JSFunction*>(callBack);
-        CachedCall cachedCall(callFrame, function, 3);
-        while (iterator->next(callFrame, key) && !vm->exception()) {
-            cachedCall.setThis(thisValue);
-            cachedCall.setArgument(0, key);
-            cachedCall.setArgument(1, key);
-            cachedCall.setArgument(2, set);
-            cachedCall.call();
-        }
-        iterator->finish();
-    } else {
-        while (iterator->next(callFrame, key) && !vm->exception()) {
-            MarkedArgumentBuffer args;
-            args.append(key);
-            args.append(key);
-            args.append(set);
-            JSC::call(callFrame, callBack, callType, callData, thisValue, args);
-        }
-        iterator->finish();
-    }
-    return JSValue::encode(jsUndefined());
-}
-
 EncodedJSValue JSC_HOST_CALL setProtoFuncHas(CallFrame* callFrame)
 {
     JSSet* set = getSet(callFrame, callFrame->thisValue());
@@ -188,4 +164,30 @@ EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(CallFrame* callFrame)
     return JSValue::encode(JSSetIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->setIteratorStructure(), thisObj, SetIterateKeyValue));
 }
 
+EncodedJSValue JSC_HOST_CALL privateFuncIsSet(ExecState* exec)
+{
+    return JSValue::encode(jsBoolean(jsDynamicCast<JSSet*>(exec->uncheckedArgument(0))));
+}
+
+EncodedJSValue JSC_HOST_CALL privateFuncSetIterator(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSSet*>(exec->uncheckedArgument(0)));
+    JSSet* set = jsCast<JSSet*>(exec->uncheckedArgument(0));
+    return JSValue::encode(JSSetIterator::create(exec->vm(), exec->callee()->globalObject()->setIteratorStructure(), set, SetIterateKey));
+}
+
+EncodedJSValue JSC_HOST_CALL privateFuncSetIteratorNext(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSSetIterator*>(exec->thisValue()));
+    JSSetIterator* iterator = jsCast<JSSetIterator*>(exec->thisValue());
+    JSValue result;
+    if (iterator->next(exec, result)) {
+        JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
+        resultArray->putDirectIndex(exec, 0, result);
+        return JSValue::encode(jsBoolean(false));
+    }
+    iterator->finish();
+    return JSValue::encode(jsBoolean(true));
+}
+
 }
index bdf90f4..e2719a6 100644 (file)
@@ -34,6 +34,8 @@ class SetPrototype : public JSNonFinalObject {
 public:
     typedef JSNonFinalObject Base;
 
+    static const unsigned StructureFlags = OverridesGetOwnPropertySlot | Base::StructureFlags;
+
     static SetPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
     {
         SetPrototype* prototype = new (NotNull, allocateCell<SetPrototype>(vm.heap)) SetPrototype(vm, structure);
@@ -54,8 +56,13 @@ private:
     {
     }
     void finishCreation(VM&, JSGlobalObject*);
+    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
 };
 
+EncodedJSValue JSC_HOST_CALL privateFuncIsSet(ExecState*);
+EncodedJSValue JSC_HOST_CALL privateFuncSetIterator(ExecState*);
+EncodedJSValue JSC_HOST_CALL privateFuncSetIteratorNext(ExecState*);
+
 }
 
 #endif // !defined(SetPrototype_h)