[JSC] Optimize Map iteration with intrinsic
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Aug 2017 22:19:13 +0000 (22:19 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Aug 2017 22:19:13 +0000 (22:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174355

Reviewed by Saam Barati.

JSTests:

* stress/map-iterator-result-should-have-expected-shape.js: Added.
(shouldBe):
(throw.new.Error):
* stress/set-iterator-result-should-have-expected-shape.js: Added.
(shouldBe):
(throw.new.Error.let.iterator.set Symbol):
(throw.new.Error.set add):
(let.iterator.set Symbol):

Source/JavaScriptCore:

This patch optimizes Map/Set iteration by taking the approach similar to Array iteration.
We create a simple iterator object instead of JSMapIterator and JSSetIterator. And we
directly handles Map/Set buckets in JS builtins. We carefully create mapIteratorNext and
setIteratorNext functions which should be inlined. This leads significant performance boost
when they are inlined in for-of iteration.

This patch changes how DFG and FTL handles MapBucket if the bucket is not found.
Previously, we use nullptr for that, and DFG and FTL specially handle this nullptr as bucket.
Instead, this patch introduces sentinel buckets. They are marked as deleted, and not linked
to any hash maps. And its key and value fields are filled with Undefined. By returning this
sentinel bucket instead of returning nullptr, we simplify DFG and FTL's LoadXXXFromMapBucket
code.

We still keep JSMapIterator and JSSetIterator because they are useful to serialize Map and Set
in WebCore. So they are not used in user observable JS. We change them from JS objects to JS cells.

Existing microbenchmarks shows performance improvements.

large-map-iteration                           164.1622+-4.1618     ^     56.6284+-1.5355        ^ definitely 2.8989x faster
set-for-of                                     15.4369+-1.0631     ^      9.2955+-0.5979        ^ definitely 1.6607x faster
map-for-each                                    7.5889+-0.5792     ^      6.3011+-0.4816        ^ definitely 1.2044x faster
map-for-of                                     32.3904+-1.3003     ^     12.6907+-0.6118        ^ definitely 2.5523x faster
map-rehash                                     13.9275+-0.9187     ^     11.5367+-0.6430        ^ definitely 1.2072x faster

* CMakeLists.txt:
* DerivedSources.make:
* builtins/ArrayPrototype.js:
(globalPrivate.createArrayIterator):
* builtins/BuiltinNames.h:
* builtins/MapIteratorPrototype.js: Copied from Source/JavaScriptCore/builtins/MapPrototype.js.
(globalPrivate.mapIteratorNext):
(next):
* builtins/MapPrototype.js:
(globalPrivate.createMapIterator):
(values):
(keys):
(entries):
(forEach):
* builtins/SetIteratorPrototype.js: Copied from Source/JavaScriptCore/builtins/MapPrototype.js.
(globalPrivate.setIteratorNext):
(next):
* builtins/SetPrototype.js:
(globalPrivate.createSetIterator):
(values):
(entries):
(forEach):
* bytecode/BytecodeIntrinsicRegistry.cpp:
(JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/SpeculatedType.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasBucketOwnerType):
(JSC::DFG::Node::bucketOwnerType):
(JSC::DFG::Node::OpInfoWrapper::as const):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetMapBucketHead):
(JSC::DFG::SpeculativeJIT::compileGetMapBucketNext):
(JSC::DFG::SpeculativeJIT::compileLoadKeyFromMapBucket):
(JSC::DFG::SpeculativeJIT::compileLoadValueFromMapBucket):
(JSC::DFG::SpeculativeJIT::compileCompareEqPtr): Deleted.
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compileCompareEqPtr):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compileCompareEqPtr):
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileGetMapBucket):
(JSC::FTL::DFG::LowerDFGToB3::compileGetMapBucketHead):
(JSC::FTL::DFG::LowerDFGToB3::compileGetMapBucketNext):
(JSC::FTL::DFG::LowerDFGToB3::compileLoadValueFromMapBucket):
(JSC::FTL::DFG::LowerDFGToB3::compileLoadKeyFromMapBucket):
(JSC::FTL::DFG::LowerDFGToB3::setStorage):
(JSC::FTL::DFG::LowerDFGToB3::compileLoadFromJSMapBucket): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::compileIsNonEmptyMapBucket): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::lowMapBucket): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::setMapBucket): Deleted.
* inspector/JSInjectedScriptHost.cpp:
(Inspector::JSInjectedScriptHost::subtype):
(Inspector::JSInjectedScriptHost::getInternalProperties):
(Inspector::cloneMapIteratorObject):
(Inspector::cloneSetIteratorObject):
(Inspector::JSInjectedScriptHost::iteratorEntries):
* runtime/HashMapImpl.h:
(JSC::HashMapBucket::createSentinel):
(JSC::HashMapBucket::offsetOfNext):
(JSC::HashMapBucket::offsetOfDeleted):
(JSC::HashMapImpl::offsetOfHead):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObject.h:
* runtime/JSMap.h:
* runtime/JSMapIterator.cpp:
(JSC::JSMapIterator::clone): Deleted.
* runtime/JSMapIterator.h:
(JSC::JSMapIterator::iteratedValue const):
* runtime/JSSet.h:
* runtime/JSSetIterator.cpp:
(JSC::JSSetIterator::clone): Deleted.
* runtime/JSSetIterator.h:
(JSC::JSSetIterator::iteratedValue const):
* runtime/MapConstructor.cpp:
(JSC::mapPrivateFuncMapBucketHead):
(JSC::mapPrivateFuncMapBucketNext):
(JSC::mapPrivateFuncMapBucketKey):
(JSC::mapPrivateFuncMapBucketValue):
* runtime/MapConstructor.h:
* runtime/MapIteratorPrototype.cpp:
(JSC::MapIteratorPrototype::finishCreation):
(JSC::MapIteratorPrototypeFuncNext): Deleted.
* runtime/MapPrototype.cpp:
(JSC::MapPrototype::finishCreation):
(JSC::mapProtoFuncValues): Deleted.
(JSC::mapProtoFuncEntries): Deleted.
(JSC::mapProtoFuncKeys): Deleted.
(JSC::privateFuncMapIterator): Deleted.
(JSC::privateFuncMapIteratorNext): Deleted.
* runtime/MapPrototype.h:
* runtime/SetConstructor.cpp:
(JSC::setPrivateFuncSetBucketHead):
(JSC::setPrivateFuncSetBucketNext):
(JSC::setPrivateFuncSetBucketKey):
* runtime/SetConstructor.h:
* runtime/SetIteratorPrototype.cpp:
(JSC::SetIteratorPrototype::finishCreation):
(JSC::SetIteratorPrototypeFuncNext): Deleted.
* runtime/SetPrototype.cpp:
(JSC::SetPrototype::finishCreation):
(JSC::setProtoFuncSize):
(JSC::setProtoFuncValues): Deleted.
(JSC::setProtoFuncEntries): Deleted.
(JSC::privateFuncSetIterator): Deleted.
(JSC::privateFuncSetIteratorNext): Deleted.
* runtime/SetPrototype.h:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:

Source/WebCore:

* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneSerializer::serialize):

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

60 files changed:
JSTests/ChangeLog
JSTests/stress/map-iterator-result-should-have-expected-shape.js [new file with mode: 0644]
JSTests/stress/set-iterator-result-should-have-expected-shape.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/DerivedSources.make
Source/JavaScriptCore/builtins/ArrayPrototype.js
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/builtins/MapIteratorPrototype.js [new file with mode: 0644]
Source/JavaScriptCore/builtins/MapPrototype.js
Source/JavaScriptCore/builtins/SetIteratorPrototype.js [new file with mode: 0644]
Source/JavaScriptCore/builtins/SetPrototype.js
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.cpp
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/bytecode/SpeculatedType.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
Source/JavaScriptCore/runtime/HashMapImpl.h
Source/JavaScriptCore/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSMap.h
Source/JavaScriptCore/runtime/JSMapIterator.cpp
Source/JavaScriptCore/runtime/JSMapIterator.h
Source/JavaScriptCore/runtime/JSSet.h
Source/JavaScriptCore/runtime/JSSetIterator.cpp
Source/JavaScriptCore/runtime/JSSetIterator.h
Source/JavaScriptCore/runtime/MapConstructor.cpp
Source/JavaScriptCore/runtime/MapConstructor.h
Source/JavaScriptCore/runtime/MapIteratorPrototype.cpp
Source/JavaScriptCore/runtime/MapPrototype.cpp
Source/JavaScriptCore/runtime/MapPrototype.h
Source/JavaScriptCore/runtime/SetConstructor.cpp
Source/JavaScriptCore/runtime/SetConstructor.h
Source/JavaScriptCore/runtime/SetIteratorPrototype.cpp
Source/JavaScriptCore/runtime/SetPrototype.cpp
Source/JavaScriptCore/runtime/SetPrototype.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/SerializedScriptValue.cpp

index 02c07c9e7806693d5b306e3f56ff8148619aafec..a01040ac4ee92faa545aa3900ba0e43515cd5610 100644 (file)
@@ -1,3 +1,19 @@
+2017-08-23  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Optimize Map iteration with intrinsic
+        https://bugs.webkit.org/show_bug.cgi?id=174355
+
+        Reviewed by Saam Barati.
+
+        * stress/map-iterator-result-should-have-expected-shape.js: Added.
+        (shouldBe):
+        (throw.new.Error):
+        * stress/set-iterator-result-should-have-expected-shape.js: Added.
+        (shouldBe):
+        (throw.new.Error.let.iterator.set Symbol):
+        (throw.new.Error.set add):
+        (let.iterator.set Symbol):
+
 2017-08-23  Robin Morisset  <rmorisset@apple.com>
 
         Add a micro-benchmark for checking that accessing a variable within a 'with'
diff --git a/JSTests/stress/map-iterator-result-should-have-expected-shape.js b/JSTests/stress/map-iterator-result-should-have-expected-shape.js
new file mode 100644 (file)
index 0000000..b34a2a2
--- /dev/null
@@ -0,0 +1,33 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+{
+    let map = new Map();
+    map.set(42, 42);
+    let iterator = map[Symbol.iterator]();
+    {
+        let result = iterator.next();
+        shouldBe(JSON.stringify(Object.getOwnPropertyNames(result).sort()), `["done","value"]`);
+        shouldBe(result.done, false);
+        shouldBe(JSON.stringify(result.value), `[42,42]`);
+    }
+    {
+        let result = iterator.next();
+        shouldBe(JSON.stringify(Object.getOwnPropertyNames(result).sort()), `["done","value"]`);
+        shouldBe(result.done, true);
+        shouldBe(result.value, undefined);
+    }
+}
+
+{
+    let map = new Map();
+    let iterator = map[Symbol.iterator]();
+    {
+        let result = iterator.next();
+        shouldBe(JSON.stringify(Object.getOwnPropertyNames(result).sort()), `["done","value"]`);
+        shouldBe(result.done, true);
+        shouldBe(result.value, undefined);
+    }
+}
diff --git a/JSTests/stress/set-iterator-result-should-have-expected-shape.js b/JSTests/stress/set-iterator-result-should-have-expected-shape.js
new file mode 100644 (file)
index 0000000..5b7d12b
--- /dev/null
@@ -0,0 +1,33 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+{
+    let set = new Set();
+    set.add(42);
+    let iterator = set[Symbol.iterator]();
+    {
+        let result = iterator.next();
+        shouldBe(JSON.stringify(Object.getOwnPropertyNames(result).sort()), `["done","value"]`);
+        shouldBe(result.done, false);
+        shouldBe(result.value, 42);
+    }
+    {
+        let result = iterator.next();
+        shouldBe(JSON.stringify(Object.getOwnPropertyNames(result).sort()), `["done","value"]`);
+        shouldBe(result.done, true);
+        shouldBe(result.value, undefined);
+    }
+}
+
+{
+    let set = new Set();
+    let iterator = set[Symbol.iterator]();
+    {
+        let result = iterator.next();
+        shouldBe(JSON.stringify(Object.getOwnPropertyNames(result).sort()), `["done","value"]`);
+        shouldBe(result.done, true);
+        shouldBe(result.value, undefined);
+    }
+}
index 7a0d440c657223d22eaa49a42dd5c979a8f6fd81..5d06295aec05bab95c336e28abc0f4e828f6e3b7 100644 (file)
@@ -1476,6 +1476,7 @@ set(JavaScriptCore_BUILTINS_SOURCES
     ${JAVASCRIPTCORE_DIR}/builtins/InternalPromiseConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/IteratorHelpers.js
     ${JAVASCRIPTCORE_DIR}/builtins/IteratorPrototype.js
+    ${JAVASCRIPTCORE_DIR}/builtins/MapIteratorPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/MapPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ModuleLoaderPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/NumberConstructor.js
@@ -1486,6 +1487,7 @@ set(JavaScriptCore_BUILTINS_SOURCES
     ${JAVASCRIPTCORE_DIR}/builtins/PromisePrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ReflectObject.js
     ${JAVASCRIPTCORE_DIR}/builtins/RegExpPrototype.js
+    ${JAVASCRIPTCORE_DIR}/builtins/SetIteratorPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/SetPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/StringConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/StringIteratorPrototype.js
index c1166e3e579c93b242d05a4a9cb1e4fa98e009b2..a0cedf0ca4b2539707c9f36464a18b5a4c8e18e6 100644 (file)
@@ -1,3 +1,175 @@
+2017-08-23  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Optimize Map iteration with intrinsic
+        https://bugs.webkit.org/show_bug.cgi?id=174355
+
+        Reviewed by Saam Barati.
+
+        This patch optimizes Map/Set iteration by taking the approach similar to Array iteration.
+        We create a simple iterator object instead of JSMapIterator and JSSetIterator. And we
+        directly handles Map/Set buckets in JS builtins. We carefully create mapIteratorNext and
+        setIteratorNext functions which should be inlined. This leads significant performance boost
+        when they are inlined in for-of iteration.
+
+        This patch changes how DFG and FTL handles MapBucket if the bucket is not found.
+        Previously, we use nullptr for that, and DFG and FTL specially handle this nullptr as bucket.
+        Instead, this patch introduces sentinel buckets. They are marked as deleted, and not linked
+        to any hash maps. And its key and value fields are filled with Undefined. By returning this
+        sentinel bucket instead of returning nullptr, we simplify DFG and FTL's LoadXXXFromMapBucket
+        code.
+
+        We still keep JSMapIterator and JSSetIterator because they are useful to serialize Map and Set
+        in WebCore. So they are not used in user observable JS. We change them from JS objects to JS cells.
+
+        Existing microbenchmarks shows performance improvements.
+
+        large-map-iteration                           164.1622+-4.1618     ^     56.6284+-1.5355        ^ definitely 2.8989x faster
+        set-for-of                                     15.4369+-1.0631     ^      9.2955+-0.5979        ^ definitely 1.6607x faster
+        map-for-each                                    7.5889+-0.5792     ^      6.3011+-0.4816        ^ definitely 1.2044x faster
+        map-for-of                                     32.3904+-1.3003     ^     12.6907+-0.6118        ^ definitely 2.5523x faster
+        map-rehash                                     13.9275+-0.9187     ^     11.5367+-0.6430        ^ definitely 1.2072x faster
+
+        * CMakeLists.txt:
+        * DerivedSources.make:
+        * builtins/ArrayPrototype.js:
+        (globalPrivate.createArrayIterator):
+        * builtins/BuiltinNames.h:
+        * builtins/MapIteratorPrototype.js: Copied from Source/JavaScriptCore/builtins/MapPrototype.js.
+        (globalPrivate.mapIteratorNext):
+        (next):
+        * builtins/MapPrototype.js:
+        (globalPrivate.createMapIterator):
+        (values):
+        (keys):
+        (entries):
+        (forEach):
+        * builtins/SetIteratorPrototype.js: Copied from Source/JavaScriptCore/builtins/MapPrototype.js.
+        (globalPrivate.setIteratorNext):
+        (next):
+        * builtins/SetPrototype.js:
+        (globalPrivate.createSetIterator):
+        (values):
+        (entries):
+        (forEach):
+        * bytecode/BytecodeIntrinsicRegistry.cpp:
+        (JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * bytecode/SpeculatedType.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGHeapLocation.cpp:
+        (WTF::printInternal):
+        * dfg/DFGHeapLocation.h:
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        (JSC::DFG::Node::hasBucketOwnerType):
+        (JSC::DFG::Node::bucketOwnerType):
+        (JSC::DFG::Node::OpInfoWrapper::as const):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetMapBucketHead):
+        (JSC::DFG::SpeculativeJIT::compileGetMapBucketNext):
+        (JSC::DFG::SpeculativeJIT::compileLoadKeyFromMapBucket):
+        (JSC::DFG::SpeculativeJIT::compileLoadValueFromMapBucket):
+        (JSC::DFG::SpeculativeJIT::compileCompareEqPtr): Deleted.
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compileCompareEqPtr):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compileCompareEqPtr):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetMapBucket):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetMapBucketHead):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetMapBucketNext):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLoadValueFromMapBucket):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLoadKeyFromMapBucket):
+        (JSC::FTL::DFG::LowerDFGToB3::setStorage):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLoadFromJSMapBucket): Deleted.
+        (JSC::FTL::DFG::LowerDFGToB3::compileIsNonEmptyMapBucket): Deleted.
+        (JSC::FTL::DFG::LowerDFGToB3::lowMapBucket): Deleted.
+        (JSC::FTL::DFG::LowerDFGToB3::setMapBucket): Deleted.
+        * inspector/JSInjectedScriptHost.cpp:
+        (Inspector::JSInjectedScriptHost::subtype):
+        (Inspector::JSInjectedScriptHost::getInternalProperties):
+        (Inspector::cloneMapIteratorObject):
+        (Inspector::cloneSetIteratorObject):
+        (Inspector::JSInjectedScriptHost::iteratorEntries):
+        * runtime/HashMapImpl.h:
+        (JSC::HashMapBucket::createSentinel):
+        (JSC::HashMapBucket::offsetOfNext):
+        (JSC::HashMapBucket::offsetOfDeleted):
+        (JSC::HashMapImpl::offsetOfHead):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObject.h:
+        * runtime/JSMap.h:
+        * runtime/JSMapIterator.cpp:
+        (JSC::JSMapIterator::clone): Deleted.
+        * runtime/JSMapIterator.h:
+        (JSC::JSMapIterator::iteratedValue const):
+        * runtime/JSSet.h:
+        * runtime/JSSetIterator.cpp:
+        (JSC::JSSetIterator::clone): Deleted.
+        * runtime/JSSetIterator.h:
+        (JSC::JSSetIterator::iteratedValue const):
+        * runtime/MapConstructor.cpp:
+        (JSC::mapPrivateFuncMapBucketHead):
+        (JSC::mapPrivateFuncMapBucketNext):
+        (JSC::mapPrivateFuncMapBucketKey):
+        (JSC::mapPrivateFuncMapBucketValue):
+        * runtime/MapConstructor.h:
+        * runtime/MapIteratorPrototype.cpp:
+        (JSC::MapIteratorPrototype::finishCreation):
+        (JSC::MapIteratorPrototypeFuncNext): Deleted.
+        * runtime/MapPrototype.cpp:
+        (JSC::MapPrototype::finishCreation):
+        (JSC::mapProtoFuncValues): Deleted.
+        (JSC::mapProtoFuncEntries): Deleted.
+        (JSC::mapProtoFuncKeys): Deleted.
+        (JSC::privateFuncMapIterator): Deleted.
+        (JSC::privateFuncMapIteratorNext): Deleted.
+        * runtime/MapPrototype.h:
+        * runtime/SetConstructor.cpp:
+        (JSC::setPrivateFuncSetBucketHead):
+        (JSC::setPrivateFuncSetBucketNext):
+        (JSC::setPrivateFuncSetBucketKey):
+        * runtime/SetConstructor.h:
+        * runtime/SetIteratorPrototype.cpp:
+        (JSC::SetIteratorPrototype::finishCreation):
+        (JSC::SetIteratorPrototypeFuncNext): Deleted.
+        * runtime/SetPrototype.cpp:
+        (JSC::SetPrototype::finishCreation):
+        (JSC::setProtoFuncSize):
+        (JSC::setProtoFuncValues): Deleted.
+        (JSC::setProtoFuncEntries): Deleted.
+        (JSC::privateFuncSetIterator): Deleted.
+        (JSC::privateFuncSetIteratorNext): Deleted.
+        * runtime/SetPrototype.h:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+
 2017-08-23  David Kilzer  <ddkilzer@apple.com>
 
         Fix -Wcast-qual warnings in JavaScriptCore with new clang compiler
index 178b2166b3ad797f3d52d6803b2051c1dbf9fb4e..789f60cf5982332351e6fa4a399b0acc89439194 100644 (file)
@@ -105,6 +105,7 @@ JavaScriptCore_BUILTINS_SOURCES = \
     $(JavaScriptCore)/builtins/InternalPromiseConstructor.js \
     $(JavaScriptCore)/builtins/IteratorHelpers.js \
     $(JavaScriptCore)/builtins/IteratorPrototype.js \
+    $(JavaScriptCore)/builtins/MapIteratorPrototype.js \
     $(JavaScriptCore)/builtins/MapPrototype.js \
     $(JavaScriptCore)/builtins/ModuleLoaderPrototype.js \
     $(JavaScriptCore)/builtins/NumberConstructor.js \
@@ -115,6 +116,7 @@ JavaScriptCore_BUILTINS_SOURCES = \
     $(JavaScriptCore)/builtins/PromisePrototype.js \
     $(JavaScriptCore)/builtins/ReflectObject.js \
     $(JavaScriptCore)/builtins/RegExpPrototype.js \
+    $(JavaScriptCore)/builtins/SetIteratorPrototype.js \
     $(JavaScriptCore)/builtins/SetPrototype.js \
     $(JavaScriptCore)/builtins/StringConstructor.js \
     $(JavaScriptCore)/builtins/StringIteratorPrototype.js \
index 7e3cc44fedcd2d422da15bd7e321de38d512ae16..30a797d255094aa7508263f7b69b02df1102bf53 100644 (file)
@@ -28,6 +28,8 @@
 @globalPrivate
 function createArrayIterator(iteratedObject, kind, iterationFunction)
 {
+    "use strict";
+
     this.@iteratedObject = iteratedObject;
     this.@arrayIteratorKind = kind;
     this.@arrayIteratorNextIndex = 0;
index d9961c7b124964dfa394ee5ceb4346fa7bfdc136..68fe7c1cd66d144045920a0f59b390a05cf0483e 100644 (file)
@@ -147,12 +147,19 @@ namespace JSC {
     macro(predictFinalLengthFromArgumunts) \
     macro(print) \
     macro(regExpCreate) \
-    macro(SetIterator) \
-    macro(setIteratorNext) \
     macro(replaceUsingRegExp) \
     macro(replaceUsingStringSearch) \
-    macro(MapIterator) \
-    macro(mapIteratorNext) \
+    macro(mapBucket) \
+    macro(mapBucketHead) \
+    macro(mapBucketNext) \
+    macro(mapBucketKey) \
+    macro(mapBucketValue) \
+    macro(mapIteratorKind) \
+    macro(setBucket) \
+    macro(setBucketHead) \
+    macro(setBucketNext) \
+    macro(setBucketKey) \
+    macro(setIteratorKind) \
     macro(regExpBuiltinExec) \
     macro(regExpMatchFast) \
     macro(regExpProtoFlagsGetter) \
diff --git a/Source/JavaScriptCore/builtins/MapIteratorPrototype.js b/Source/JavaScriptCore/builtins/MapIteratorPrototype.js
new file mode 100644 (file)
index 0000000..e65ede7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// We keep this function small very carefully to encourage inlining.
+@globalPrivate
+function mapIteratorNext(bucket, kind)
+{
+    "use strict";
+    var value;
+
+    bucket = @mapBucketNext(bucket);
+    this.@mapBucket = bucket;
+    var done = bucket === @sentinelMapBucket;
+    if (!done) {
+        var key = @mapBucketKey(bucket);
+        value = @mapBucketValue(bucket);
+        if (kind === @iterationKindKeyValue)
+            value = [ key, value ]
+        else if (kind === @iterationKindKey)
+            value = key;
+    }
+    return { done, value };
+}
+
+function next()
+{
+    "use strict";
+
+    if (this == null)
+        @throwTypeError("%MapIteratorPrototype%.next requires that |this| not be null or undefined");
+
+    var bucket = this.@mapBucket;
+    if (bucket === @undefined)
+        @throwTypeError("%MapIteratorPrototype%.next requires that |this| be a Map Iterator instance");
+    return @mapIteratorNext.@call(this, bucket, this.@mapIteratorKind);
+}
index 830260269a6c5558cb80e77dd3c0e3af8b895f13..4ec041afaf67fd7d4912ac4074963987957903b6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
+ * Copyright (C) 2016-2017 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
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+@constructor
+@globalPrivate
+function createMapIterator(iteratedObject, kind)
+{
+    "use strict";
+
+    @assert(@isMap(iteratedObject));
+    this.@iteratedObject = iteratedObject;
+    this.@mapIteratorKind = kind;
+    this.@mapBucket = @mapBucketHead(iteratedObject);
+}
+
+function values()
+{
+    "use strict";
+
+    if (!@isMap(this))
+        @throwTypeError("Map.prototype.values requires that |this| be Map");
+
+    return new @createMapIterator(this, @iterationKindValue);
+}
+
+function keys()
+{
+    "use strict";
+
+    if (!@isMap(this))
+        @throwTypeError("Map.prototype.keys requires that |this| be Map");
+
+    return new @createMapIterator(this, @iterationKindKey);
+}
+
+function entries()
+{
+    "use strict";
+
+    if (!@isMap(this))
+        @throwTypeError("Map.prototype.entries requires that |this| be Map");
+
+    return new @createMapIterator(this, @iterationKindKeyValue);
+}
+
 function forEach(callback /*, thisArg */)
 {
     "use strict";
@@ -34,13 +76,12 @@ function forEach(callback /*, thisArg */)
         @throwTypeError("Map.prototype.forEach callback must be a function");
 
     var thisArg = @argument(1);
-    var iterator = @MapIterator(this);
+    var bucket = @mapBucketHead(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))
+    do {
+        bucket = @mapBucketNext(bucket);
+        if (bucket === @sentinelMapBucket)
             break;
-        callback.@call(thisArg, value[1], value[0], this);
-    }
+        callback.@call(thisArg, @mapBucketValue(bucket), @mapBucketKey(bucket), this);
+    } while (true);
 }
diff --git a/Source/JavaScriptCore/builtins/SetIteratorPrototype.js b/Source/JavaScriptCore/builtins/SetIteratorPrototype.js
new file mode 100644 (file)
index 0000000..c086601
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// We keep this function small very carefully to encourage inlining.
+@globalPrivate
+function setIteratorNext(bucket, kind)
+{
+    "use strict";
+    var value;
+
+    bucket = @setBucketNext(bucket);
+    this.@setBucket = bucket;
+    var done = bucket === @sentinelSetBucket;
+    if (!done) {
+        value = @setBucketKey(bucket);
+        if (kind === @iterationKindKeyValue)
+            value = [ value, value ]
+    }
+    return { done, value };
+}
+
+function next()
+{
+    "use strict";
+
+    if (this == null)
+        @throwTypeError("%SetIteratorPrototype%.next requires that |this| not be null or undefined");
+
+    var bucket = this.@setBucket;
+    if (bucket === @undefined)
+        @throwTypeError("%SetIteratorPrototype%.next requires that |this| be a Set Iterator instance");
+    return @setIteratorNext.@call(this, bucket, this.@setIteratorKind);
+}
index e9b6626e9c7c629056a7235073ad3c18606c27c6..ba301704c4ebe8d2aa2388d3432460058f1406ac 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+@constructor
+@globalPrivate
+function createSetIterator(iteratedObject, kind)
+{
+    "use strict";
+
+    @assert(@isSet(iteratedObject));
+    this.@iteratedObject = iteratedObject;
+    this.@setIteratorKind = kind;
+    this.@setBucket = @setBucketHead(iteratedObject);
+}
+
+function values()
+{
+    "use strict";
+
+    if (!@isSet(this))
+        @throwTypeError("Set.prototype.values requires that |this| be Set");
+
+    return new @createSetIterator(this, @iterationKindValue);
+}
+
+function entries()
+{
+    "use strict";
+
+    if (!@isSet(this))
+        @throwTypeError("Set.prototype.entries requires that |this| be Set");
+
+    return new @createSetIterator(this, @iterationKindKeyValue);
+}
+
 function forEach(callback /*, thisArg */)
 {
     "use strict";
@@ -34,13 +66,13 @@ function forEach(callback /*, thisArg */)
         @throwTypeError("Set.prototype.forEach callback must be a function");
 
     var thisArg = @argument(1);
-    var iterator = @SetIterator(this);
+    var bucket = @setBucketHead(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))
+    do {
+        bucket = @setBucketNext(bucket);
+        if (bucket === @sentinelSetBucket)
             break;
-        callback.@call(thisArg, value[0], value[0], this);
-    }
+        var key = @setBucketKey(bucket);
+        callback.@call(thisArg, key, key, this);
+    } while (true);
 }
index e2ba90ecf778a5d7699b128e871003215c863fc9..329ffe842cc81b986c667a2f084144d7c17cf016 100644 (file)
@@ -69,6 +69,8 @@ BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry(VM& vm)
     m_promiseStatePending.set(m_vm, jsNumber(static_cast<unsigned>(JSPromise::Status::Pending)));
     m_promiseStateFulfilled.set(m_vm, jsNumber(static_cast<unsigned>(JSPromise::Status::Fulfilled)));
     m_promiseStateRejected.set(m_vm, jsNumber(static_cast<unsigned>(JSPromise::Status::Rejected)));
+    m_sentinelMapBucket.set(m_vm, m_vm.sentinelMapBucket.get());
+    m_sentinelSetBucket.set(m_vm, m_vm.sentinelSetBucket.get());
     m_GeneratorResumeModeNormal.set(m_vm, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::NormalMode)));
     m_GeneratorResumeModeThrow.set(m_vm, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::ThrowMode)));
     m_GeneratorResumeModeReturn.set(m_vm, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::ReturnMode)));
index d71eebd1fd37aa44c23f10d6f7d25fdd7eb8c442..9ae1ac872efd092496cd2944fa6394dd3d699826 100644 (file)
@@ -80,6 +80,8 @@ class Identifier;
     macro(promiseStatePending) \
     macro(promiseStateFulfilled) \
     macro(promiseStateRejected) \
+    macro(sentinelMapBucket) \
+    macro(sentinelSetBucket) \
     macro(GeneratorResumeModeNormal) \
     macro(GeneratorResumeModeThrow) \
     macro(GeneratorResumeModeReturn) \
index 7a6bb467ff59c437fbb8856a8df9fc8d60304116..be892f269a5b1b4d233a6fb30d6b18f5624dc082 100644 (file)
@@ -66,7 +66,7 @@ static const SpeculatedType SpecStringIdent        = 1ull << 21; // It's definit
 static const SpeculatedType SpecStringVar          = 1ull << 22; // It's definitely a JSString, and it's not an identifier.
 static const SpeculatedType SpecString             = SpecStringIdent | SpecStringVar; // It's definitely a JSString.
 static const SpeculatedType SpecSymbol             = 1ull << 23; // It's definitely a Symbol.
-static const SpeculatedType SpecCellOther          = 1ull << 24; // It's definitely a JSCell but not a subclass of JSObject and definitely not a JSString or a Symbol. FIXME: This shouldn't be part of heap-top or bytecode-top. https://bugs.webkit.org/show_bug.cgi?id=133078
+static const SpeculatedType SpecCellOther          = 1ull << 24; // It's definitely a JSCell but not a subclass of JSObject and definitely not a JSString or a Symbol.
 static const SpeculatedType SpecCell               = SpecObject | SpecString | SpecSymbol | SpecCellOther; // It's definitely a JSCell.
 static const SpeculatedType SpecBoolInt32          = 1ull << 25; // It's definitely an Int32 with value 0 or 1.
 static const SpeculatedType SpecNonBoolInt32       = 1ull << 26; // It's definitely an Int32 with value other than 0 or 1.
index f603c74299456394d8b87daa3728a4c789cb95ea..40eea94b2118e6b52959ff293eb454bfcb0476c8 100644 (file)
@@ -1048,16 +1048,28 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
 
-    case LoadFromJSMapBucket:
+    case LoadKeyFromMapBucket:
+    case LoadValueFromMapBucket:
         forNode(node).makeHeapTop();
         break;
 
     case GetMapBucket:
-        forNode(node).setType(m_graph, SpecCellOther);
+    case GetMapBucketHead:
+        if (node->child1().useKind() == MapObjectUse)
+            forNode(node).set(m_graph, m_vm.hashMapBucketMapStructure.get());
+        else {
+            ASSERT(node->child1().useKind() == SetObjectUse);
+            forNode(node).set(m_graph, m_vm.hashMapBucketSetStructure.get());
+        }
         break;
 
-    case IsNonEmptyMapBucket:
-        forNode(node).setType(SpecBoolean);
+    case GetMapBucketNext:
+        if (node->bucketOwnerType() == BucketOwnerType::Map)
+            forNode(node).set(m_graph, m_vm.hashMapBucketMapStructure.get());
+        else {
+            ASSERT(node->bucketOwnerType() == BucketOwnerType::Set);
+            forNode(node).set(m_graph, m_vm.hashMapBucketSetStructure.get());
+        }
         break;
 
     case IsEmpty:
index 54740f317466b6b4f4ee289db3bfb418b5383260..a3cbb20a98ce2ee4b74b052e9a0771e7769aa1ed 100644 (file)
@@ -2868,7 +2868,7 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         Node* key = get(virtualRegisterForArgument(1, registerOffset));
         Node* hash = addToGraph(MapHash, key);
         Node* bucket = addToGraph(GetMapBucket, Edge(map, MapObjectUse), Edge(key), Edge(hash));
-        Node* result = addToGraph(LoadFromJSMapBucket, OpInfo(), OpInfo(prediction), bucket);
+        Node* result = addToGraph(LoadValueFromMapBucket, OpInfo(), OpInfo(prediction), bucket);
         set(VirtualRegister(resultOperand), result);
         return true;
     }
@@ -2884,7 +2884,60 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         Node* hash = addToGraph(MapHash, key);
         UseKind useKind = intrinsic == JSSetHasIntrinsic ? SetObjectUse : MapObjectUse;
         Node* bucket = addToGraph(GetMapBucket, OpInfo(0), Edge(mapOrSet, useKind), Edge(key), Edge(hash));
-        Node* result = addToGraph(IsNonEmptyMapBucket, bucket);
+        JSCell* sentinel = nullptr;
+        if (intrinsic == JSMapHasIntrinsic)
+            sentinel = m_vm->sentinelMapBucket.get();
+        else
+            sentinel = m_vm->sentinelSetBucket.get();
+
+        FrozenValue* frozenPointer = m_graph.freeze(sentinel);
+        Node* invertedResult = addToGraph(CompareEqPtr, OpInfo(frozenPointer), bucket);
+        Node* result = addToGraph(LogicalNot, invertedResult);
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
+    case JSSetBucketHeadIntrinsic:
+    case JSMapBucketHeadIntrinsic: {
+        ASSERT(argumentCountIncludingThis == 2);
+
+        insertChecks();
+        Node* map = get(virtualRegisterForArgument(1, registerOffset));
+        UseKind useKind = intrinsic == JSSetBucketHeadIntrinsic ? SetObjectUse : MapObjectUse;
+        Node* result = addToGraph(GetMapBucketHead, Edge(map, useKind));
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
+    case JSSetBucketNextIntrinsic:
+    case JSMapBucketNextIntrinsic: {
+        ASSERT(argumentCountIncludingThis == 2);
+
+        insertChecks();
+        Node* bucket = get(virtualRegisterForArgument(1, registerOffset));
+        BucketOwnerType type = intrinsic == JSSetBucketNextIntrinsic ? BucketOwnerType::Set : BucketOwnerType::Map;
+        Node* result = addToGraph(GetMapBucketNext, OpInfo(type), bucket);
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
+    case JSSetBucketKeyIntrinsic:
+    case JSMapBucketKeyIntrinsic: {
+        ASSERT(argumentCountIncludingThis == 2);
+
+        insertChecks();
+        Node* bucket = get(virtualRegisterForArgument(1, registerOffset));
+        Node* result = addToGraph(LoadKeyFromMapBucket, OpInfo(), OpInfo(prediction), bucket);
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
+    case JSMapBucketValueIntrinsic: {
+        ASSERT(argumentCountIncludingThis == 2);
+
+        insertChecks();
+        Node* bucket = get(virtualRegisterForArgument(1, registerOffset));
+        Node* result = addToGraph(LoadValueFromMapBucket, OpInfo(), OpInfo(prediction), bucket);
         set(VirtualRegister(resultOperand), result);
         return true;
     }
index 4dbe4c5c0d19fd8f907647e97890356ddd91cf05..9a4bd382179a99aa70d8918e7a9b07b94f09047f 100644 (file)
@@ -148,6 +148,10 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         // We should enable CSE of LazyJSConstant. It's a little annoying since LazyJSValue has
         // more bits than we currently have in PureValue.
         return;
+
+    case CompareEqPtr:
+        def(PureValue(node, node->cellOperand()->cell()));
+        return;
         
     case ArithIMul:
     case ArithMin:
@@ -158,7 +162,6 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case GetGlobalObject:
     case StringCharCodeAt:
     case CompareStrictEq:
-    case CompareEqPtr:
     case IsEmpty:
     case IsUndefined:
     case IsBoolean:
@@ -1551,16 +1554,33 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(HeapLocation(MapBucketLoc, MiscFields, mapEdge, keyEdge), LazyNode(node));
         return;
     }
-    case LoadFromJSMapBucket: {
+    case GetMapBucketHead: {
+        read(MiscFields);
+        Edge& mapEdge = node->child1();
+        def(HeapLocation(MapBucketHeadLoc, MiscFields, mapEdge), LazyNode(node));
+        return;
+    }
+    case GetMapBucketNext: {
         read(MiscFields);
+        LocationKind locationKind = MapBucketMapNextLoc;
+        if (node->bucketOwnerType() == BucketOwnerType::Set)
+            locationKind = MapBucketSetNextLoc;
         Edge& bucketEdge = node->child1();
-        def(HeapLocation(JSMapGetLoc, MiscFields, bucketEdge), LazyNode(node));
+        def(HeapLocation(locationKind, MiscFields, bucketEdge), LazyNode(node));
         return;
     }
-    case IsNonEmptyMapBucket:
+    case LoadKeyFromMapBucket: {
         read(MiscFields);
-        def(HeapLocation(MapHasLoc, MiscFields, node->child1()), LazyNode(node));
+        Edge& bucketEdge = node->child1();
+        def(HeapLocation(MapBucketKeyLoc, MiscFields, bucketEdge), LazyNode(node));
         return;
+    }
+    case LoadValueFromMapBucket: {
+        read(MiscFields);
+        Edge& bucketEdge = node->child1();
+        def(HeapLocation(MapBucketValueLoc, MiscFields, bucketEdge), LazyNode(node));
+        return;
+    }
 
     case ToLowerCase:
         def(PureValue(node));
index 4a23de7ac3645e67095ba739c3d6c0dab4ebcf0d..07dca044bc9432033d1f5e44f9ebacbc70125db4 100644 (file)
@@ -194,8 +194,10 @@ bool doesGC(Graph& graph, Node* node)
     case StringFromCharCode:
     case MapHash:
     case GetMapBucket:
-    case LoadFromJSMapBucket:
-    case IsNonEmptyMapBucket:
+    case GetMapBucketHead:
+    case GetMapBucketNext:
+    case LoadKeyFromMapBucket:
+    case LoadValueFromMapBucket:
     case Unreachable:
     case ExtractOSREntryLocal:
     case CheckTierUpInLoop:
index a450fad447afbb6df1adc0cb0b6943ee82b31e74..daf0bd2c9300ea78eaded1dc19760064fc4224a0 100644 (file)
@@ -1795,12 +1795,19 @@ private:
             fixEdge<Int32Use>(node->child3());
             break;
 
-        case LoadFromJSMapBucket:
-            fixEdge<KnownCellUse>(node->child1());
+        case GetMapBucketHead:
+            if (node->child1().useKind() == MapObjectUse)
+                fixEdge<MapObjectUse>(node->child1());
+            else if (node->child1().useKind() == SetObjectUse)
+                fixEdge<SetObjectUse>(node->child1());
+            else
+                RELEASE_ASSERT_NOT_REACHED();
             break;
 
-        case IsNonEmptyMapBucket:
-            fixEdge<KnownCellUse>(node->child1());
+        case GetMapBucketNext:
+        case LoadKeyFromMapBucket:
+        case LoadValueFromMapBucket:
+            fixEdge<CellUse>(node->child1());
             break;
 
         case MapHash: {
index 6a5c95036d772f00240f3276c07b14cc9fe4db83..60bd5726bc58cb7468d78d5364bf445fd8b9864e 100644 (file)
@@ -162,11 +162,20 @@ void printInternal(PrintStream& out, LocationKind kind)
     case MapBucketLoc:
         out.print("MapBucketLoc");
         return;
-    case JSMapGetLoc:
-        out.print("JSMapGetLoc");
+    case MapBucketHeadLoc:
+        out.print("MapBucketHeadLoc");
         return;
-    case MapHasLoc:
-        out.print("MapHasLoc");
+    case MapBucketKeyLoc:
+        out.print("MapBucketKeyLoc");
+        return;
+    case MapBucketValueLoc:
+        out.print("MapBucketValueLoc");
+        return;
+    case MapBucketMapNextLoc:
+        out.print("MapBucketMapNextLoc");
+        return;
+    case MapBucketSetNextLoc:
+        out.print("MapBucketSetNextLoc");
         return;
     case DOMStateLoc:
         out.print("DOMStateLoc");
index 7c87f20ce6a83073f7456b094a69aee41979f0de..b82c922da27f7e00f27b5aa3077ee4055518eec4 100644 (file)
@@ -63,8 +63,11 @@ enum LocationKind {
     StackLoc,
     StackPayloadLoc,
     MapBucketLoc,
-    JSMapGetLoc,
-    MapHasLoc,
+    MapBucketHeadLoc,
+    MapBucketValueLoc,
+    MapBucketKeyLoc,
+    MapBucketMapNextLoc,
+    MapBucketSetNextLoc,
     DOMStateLoc,
 };
 
index 1b3ad6bfd09918bc210da85d0438d838c4f65aa1..cf4cb04ba3a4b9a222c6ce16e1a76991773dd06f 100644 (file)
@@ -243,6 +243,11 @@ struct CallDOMGetterData {
     unsigned identifierNumber { 0 };
 };
 
+enum class BucketOwnerType : uint32_t {
+    Map,
+    Set
+};
+
 // === Node ===
 //
 // Node represents a single operation in the data flow graph.
@@ -1515,7 +1520,8 @@ public:
         case StringReplace:
         case StringReplaceRegExp:
         case ToNumber:
-        case LoadFromJSMapBucket:
+        case LoadKeyFromMapBucket:
+        case LoadValueFromMapBucket:
         case CallDOMGetter:
         case CallDOM:
         case ParseInt:
@@ -2544,6 +2550,17 @@ public:
         return m_opInfo.as<unsigned>();
     }
 
+    bool hasBucketOwnerType()
+    {
+        return op() == GetMapBucketNext;
+    }
+
+    BucketOwnerType bucketOwnerType()
+    {
+        ASSERT(hasBucketOwnerType());
+        return m_opInfo.as<BucketOwnerType>();
+    }
+
     void dumpChildren(PrintStream& out)
     {
         if (!child1())
@@ -2652,14 +2669,14 @@ private:
             return static_cast<T>(u.constPointer);
         }
         template <typename T>
-        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_integral<T>::value && sizeof(T) == 4, T>::type
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<(std::is_integral<T>::value || std::is_enum<T>::value) && sizeof(T) == 4, T>::type
         {
-            return u.int32;
+            return static_cast<T>(u.int32);
         }
         template <typename T>
-        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_integral<T>::value && sizeof(T) == 8, T>::type
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<(std::is_integral<T>::value || std::is_enum<T>::value) && sizeof(T) == 8, T>::type
         {
-            return u.int64;
+            return static_cast<T>(u.int64);
         }
         ALWAYS_INLINE RegisteredStructure asRegisteredStructure() const
         {
index 277d4c3070965c5a686abfef86c6f9b76717d2a8..867a92c9a15ee9111ca5772c617500fc3e4b3ec1 100644 (file)
@@ -431,8 +431,10 @@ namespace JSC { namespace DFG {
     /* Nodes for JSMap and JSSet */ \
     macro(MapHash, NodeResultInt32) \
     macro(GetMapBucket, NodeResultJS) \
-    macro(LoadFromJSMapBucket, NodeResultJS) \
-    macro(IsNonEmptyMapBucket, NodeResultBoolean) \
+    macro(GetMapBucketHead, NodeResultJS) \
+    macro(GetMapBucketNext, NodeResultJS) \
+    macro(LoadKeyFromMapBucket, NodeResultJS) \
+    macro(LoadValueFromMapBucket, NodeResultJS) \
     \
     macro(ToLowerCase, NodeResultJS) \
     /* Nodes for DOM JIT */\
index cbb7f4c058e4740bf6a8254ae0d6bc7b9abd14a3..5c41c832cefc74949c931c408077a46cd40ccead 100644 (file)
@@ -2299,7 +2299,7 @@ JSCell* JIT_OPERATION operationJSMapFindBucket(ExecState* exec, JSCell* map, Enc
     NativeCallFrameTracer tracer(&vm, exec);
     JSMap::BucketType** bucket = jsCast<JSMap*>(map)->findBucket(exec, normalizeMapKey(JSValue::decode(key)), hash);
     if (!bucket)
-        return nullptr;
+        return vm.sentinelMapBucket.get();
     return *bucket;
 }
 
@@ -2309,7 +2309,7 @@ JSCell* JIT_OPERATION operationJSSetFindBucket(ExecState* exec, JSCell* map, Enc
     NativeCallFrameTracer tracer(&vm, exec);
     JSSet::BucketType** bucket = jsCast<JSSet*>(map)->findBucket(exec, normalizeMapKey(JSValue::decode(key)), hash);
     if (!bucket)
-        return nullptr;
+        return vm.sentinelSetBucket.get();
     return *bucket;
 }
 
index 084f72687adb87aea200fc9421b23281fdafc514..891d5e0952f3fe094182e03dfc24528175e2ec13 100644 (file)
@@ -717,7 +717,8 @@ private:
         case GetGlobalLexicalVariable:
         case GetClosureVar:
         case GetFromArguments:
-        case LoadFromJSMapBucket:
+        case LoadKeyFromMapBucket:
+        case LoadValueFromMapBucket:
         case ToNumber:
         case GetArgument:
         case CallDOMGetter:
@@ -756,12 +757,12 @@ private:
         case MapHash:
             setPrediction(SpecInt32Only);
             break;
+
         case GetMapBucket:
+        case GetMapBucketHead:
+        case GetMapBucketNext:
             setPrediction(SpecCellOther);
             break;
-        case IsNonEmptyMapBucket:
-            setPrediction(SpecBoolean);
-            break;
 
         case GetRestLength:
         case ArrayIndexOf: {
index 1ab1724003d01292a1c62212b6d9647032c38ffb..1bdc59ea2c2e8cee8b0bd39ee1047c37a42e057d 100644 (file)
@@ -383,8 +383,10 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case MapHash:
     case ToLowerCase:
     case GetMapBucket:
-    case LoadFromJSMapBucket:
-    case IsNonEmptyMapBucket:
+    case GetMapBucketHead:
+    case GetMapBucketNext:
+    case LoadKeyFromMapBucket:
+    case LoadValueFromMapBucket:
     case AtomicsAdd:
     case AtomicsAnd:
     case AtomicsCompareExchange:
index 186a5f03b7f2ccd8d802d0554eaf96a3d1e98d19..7c2c86b745c05b13a208bf2100f1a179c0afd834 100644 (file)
@@ -10008,19 +10008,6 @@ void SpeculativeJIT::compileRecordRegExpCachedResult(Node* node)
     noResult(node);
 }
 
-void SpeculativeJIT::compileCompareEqPtr(Node* node)
-{
-    JSValueOperand operand(this, node->child1());
-    GPRTemporary result(this);
-    JSValueRegs regs = operand.jsValueRegs();
-    GPRReg resultGPR = result.gpr();
-    m_jit.boxBooleanPayload(false, resultGPR);
-    JITCompiler::JumpList notEqual = m_jit.branchIfNotEqual(regs, node->cellOperand()->value());
-    m_jit.boxBooleanPayload(true, resultGPR);
-    notEqual.link(&m_jit);
-    blessedBooleanResult(resultGPR, node);
-}
-
 void SpeculativeJIT::compileDefineDataProperty(Node* node)
 {
 #if USE(JSVALUE64)
@@ -10195,6 +10182,82 @@ void SpeculativeJIT::emitAllocateButterfly(GPRReg storageResultGPR, GPRReg sizeG
     m_jit.store32(sizeGPR, MacroAssembler::Address(storageResultGPR, Butterfly::offsetOfVectorLength()));
 }
 
+void SpeculativeJIT::compileGetMapBucketHead(Node* node)
+{
+    SpeculateCellOperand map(this, node->child1());
+    GPRTemporary bucket(this);
+
+    GPRReg mapGPR = map.gpr();
+    GPRReg bucketGPR = bucket.gpr();
+
+    if (node->child1().useKind() == MapObjectUse)
+        speculateMapObject(node->child1(), mapGPR);
+    else if (node->child1().useKind() == SetObjectUse)
+        speculateSetObject(node->child1(), mapGPR);
+    else
+        RELEASE_ASSERT_NOT_REACHED();
+
+    ASSERT(HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead() == HashMapImpl<HashMapBucket<HashMapBucketDataKeyValue>>::offsetOfHead());
+    m_jit.loadPtr(MacroAssembler::Address(mapGPR, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()), bucketGPR);
+    cellResult(bucketGPR, node);
+}
+
+void SpeculativeJIT::compileGetMapBucketNext(Node* node)
+{
+    SpeculateCellOperand bucket(this, node->child1());
+    GPRTemporary result(this);
+
+    GPRReg bucketGPR = bucket.gpr();
+    GPRReg resultGPR = result.gpr();
+
+    ASSERT(HashMapBucket<HashMapBucketDataKey>::offsetOfNext() == HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext());
+    ASSERT(HashMapBucket<HashMapBucketDataKey>::offsetOfDeleted() == HashMapBucket<HashMapBucketDataKeyValue>::offsetOfDeleted());
+    m_jit.loadPtr(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext()), resultGPR);
+
+    MacroAssembler::Label loop = m_jit.label();
+    auto notBucket = m_jit.branchTestPtr(MacroAssembler::Zero, resultGPR);
+    auto done = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(resultGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfDeleted()));
+    m_jit.loadPtr(MacroAssembler::Address(resultGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext()), resultGPR);
+    m_jit.jump().linkTo(loop, &m_jit);
+
+    notBucket.link(&m_jit);
+    JSCell* sentinel = nullptr;
+    if (node->bucketOwnerType() == BucketOwnerType::Map)
+        sentinel = m_jit.vm()->sentinelMapBucket.get();
+    else {
+        ASSERT(node->bucketOwnerType() == BucketOwnerType::Set);
+        sentinel = m_jit.vm()->sentinelSetBucket.get();
+    }
+    m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), sentinel), resultGPR);
+    done.link(&m_jit);
+
+    cellResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileLoadKeyFromMapBucket(Node* node)
+{
+    SpeculateCellOperand bucket(this, node->child1());
+    JSValueRegsTemporary result(this);
+
+    GPRReg bucketGPR = bucket.gpr();
+    JSValueRegs resultRegs = result.regs();
+
+    m_jit.loadValue(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey()), resultRegs);
+    jsValueResult(resultRegs, node);
+}
+
+void SpeculativeJIT::compileLoadValueFromMapBucket(Node* node)
+{
+    SpeculateCellOperand bucket(this, node->child1());
+    JSValueRegsTemporary result(this);
+
+    GPRReg bucketGPR = bucket.gpr();
+    JSValueRegs resultRegs = result.regs();
+
+    m_jit.loadValue(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfValue()), resultRegs);
+    jsValueResult(resultRegs, node);
+}
+
 } } // namespace JSC::DFG
 
 #endif
index b65132818097b77981e3caec90cfcd82584397df..b6122ef06076f70047a62fe57d32f6d8d25ea42c 100644 (file)
@@ -2779,6 +2779,10 @@ public:
     void compileCallDOMGetter(Node*);
     void compileCallDOM(Node*);
     void compileCheckSubClass(Node*);
+    void compileGetMapBucketHead(Node*);
+    void compileGetMapBucketNext(Node*);
+    void compileLoadKeyFromMapBucket(Node*);
+    void compileLoadValueFromMapBucket(Node*);
     
 #if USE(JSVALUE32_64)
     template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType>
index 35501aa8874d18d88768eafe68d069201d0521fe..10316528f92363d9be064a4b875707f3fe01c1ca 100644 (file)
@@ -692,6 +692,19 @@ void SpeculativeJIT::compileMiscStrictEq(Node* node)
     booleanResult(result.gpr(), node);
 }
 
+void SpeculativeJIT::compileCompareEqPtr(Node* node)
+{
+    JSValueOperand operand(this, node->child1());
+    GPRTemporary result(this);
+    JSValueRegs regs = operand.jsValueRegs();
+    GPRReg resultGPR = result.gpr();
+    m_jit.boxBooleanPayload(false, resultGPR);
+    JITCompiler::JumpList notEqual = m_jit.branchIfNotEqual(regs, node->cellOperand()->value());
+    m_jit.boxBooleanPayload(true, resultGPR);
+    notEqual.link(&m_jit);
+    blessedBooleanResult(resultGPR, node);
+}
+
 void SpeculativeJIT::emitCall(Node* node)
 {
     CallLinkInfo::CallType callType;
@@ -4902,38 +4915,21 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
-    case LoadFromJSMapBucket: {
-        SpeculateCellOperand bucket(this, node->child1());
-        GPRTemporary resultPayload(this);
-        GPRTemporary resultTag(this);
-
-        GPRReg bucketGPR = bucket.gpr();
-        GPRReg resultPayloadGPR = resultPayload.gpr();
-        GPRReg resultTagGPR = resultTag.gpr();
-
-        auto notBucket = m_jit.branchTestPtr(MacroAssembler::Zero, bucketGPR);
-        m_jit.loadValue(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfValue()), JSValueRegs(resultTagGPR, resultPayloadGPR));
-        auto done = m_jit.jump();
-
-        notBucket.link(&m_jit);
-        m_jit.move(TrustedImm32(JSValue::UndefinedTag), resultTagGPR);
-        m_jit.move(TrustedImm32(0), resultPayloadGPR);
-        done.link(&m_jit);
-        jsValueResult(resultTagGPR, resultPayloadGPR, node);
+    case GetMapBucketHead:
+        compileGetMapBucketHead(node);
         break;
-    }
 
-    case IsNonEmptyMapBucket: {
-        SpeculateCellOperand bucket(this, node->child1());
-        GPRTemporary result(this);
+    case GetMapBucketNext:
+        compileGetMapBucketNext(node);
+        break;
 
-        GPRReg bucketGPR = bucket.gpr();
-        GPRReg resultGPR = result.gpr();
+    case LoadKeyFromMapBucket:
+        compileLoadKeyFromMapBucket(node);
+        break;
 
-        m_jit.comparePtr(MacroAssembler::NotEqual, bucketGPR, TrustedImm32(0), resultGPR);
-        booleanResult(resultGPR, node);
+    case LoadValueFromMapBucket:
+        compileLoadValueFromMapBucket(node);
         break;
-    }
 
     case Flush:
         break;
index fa63608ee21f7b833c8a7e634e12928009a56bb7..8326241956ae00e1b4d2504906c5afcb80f6c566 100644 (file)
@@ -1836,6 +1836,18 @@ void SpeculativeJIT::compileDoubleCompare(Node* node, MacroAssembler::DoubleCond
     jsValueResult(result.gpr(), node, DataFormatJSBoolean);
 }
 
+void SpeculativeJIT::compileCompareEqPtr(Node* node)
+{
+    JSValueOperand value(this, node->child1());
+    GPRTemporary result(this);
+    GPRReg valueGPR = value.gpr();
+    GPRReg resultGPR = result.gpr();
+
+    m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), node->cellOperand()->cell()), resultGPR);
+    m_jit.compare64(MacroAssembler::Equal, valueGPR, resultGPR, resultGPR);
+    unblessedBooleanResult(resultGPR, node);
+}
+
 void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse)
 {
     JSValueOperand value(this, nodeUse, ManualOperandSpeculation);
@@ -5254,42 +5266,30 @@ void SpeculativeJIT::compile(Node* node)
         }
 
         notPresentInTable.link(&m_jit);
-        m_jit.move(TrustedImmPtr(nullptr), resultGPR);
+        if (node->child1().useKind() == MapObjectUse)
+            m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), m_jit.vm()->sentinelMapBucket.get()), resultGPR);
+        else
+            m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), m_jit.vm()->sentinelSetBucket.get()), resultGPR);
         done.link(&m_jit);
         cellResult(resultGPR, node);
         break;
     }
 
-    case LoadFromJSMapBucket: {
-        SpeculateCellOperand bucket(this, node->child1());
-        GPRTemporary result(this);
-
-        GPRReg bucketGPR = bucket.gpr();
-        GPRReg resultGPR = result.gpr();
-
-        auto notBucket = m_jit.branchTestPtr(MacroAssembler::Zero, bucketGPR);
-        m_jit.load64(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfValue()), resultGPR);
-        auto done = m_jit.jump();
-
-        notBucket.link(&m_jit);
-        m_jit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsUndefined())), resultGPR);
-        done.link(&m_jit);
-        jsValueResult(resultGPR, node);
+    case GetMapBucketHead:
+        compileGetMapBucketHead(node);
         break;
-    }
 
-    case IsNonEmptyMapBucket: {
-        SpeculateCellOperand bucket(this, node->child1());
-        GPRTemporary result(this);
+    case GetMapBucketNext:
+        compileGetMapBucketNext(node);
+        break;
 
-        GPRReg bucketGPR = bucket.gpr();
-        GPRReg resultGPR = result.gpr();
+    case LoadKeyFromMapBucket:
+        compileLoadKeyFromMapBucket(node);
+        break;
 
-        m_jit.comparePtr(MacroAssembler::NotEqual, bucketGPR, TrustedImm32(0), resultGPR);
-        m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
-        jsValueResult(resultGPR, node, DataFormatJSBoolean);
+    case LoadValueFromMapBucket:
+        compileLoadValueFromMapBucket(node);
         break;
-    }
 
     case ToLowerCase: {
         compileToLowerCase(node);
index 570ca60bbf522f9d83b54981c449cc0f982840c4..f74865a2cba47cd00110b183ece4861be61f824e 100644 (file)
@@ -113,8 +113,11 @@ namespace JSC { namespace FTL {
     macro(Structure_indexingTypeIncludingHistory, Structure::indexingTypeIncludingHistoryOffset()) \
     macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
+    macro(HashMapImpl_head,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \
     macro(HashMapBucket_value, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfValue()) \
     macro(HashMapBucket_key, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey()) \
+    macro(HashMapBucket_next, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext()) \
+    macro(HashMapBucket_deleted, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfDeleted()) \
     macro(Symbol_symbolImpl, Symbol::offsetOfSymbolImpl()) \
     macro(JSFixedArray_size, JSFixedArray::offsetOfSize()) \
 
index 51f6af1e00d4efd692f93f324d947adec7643715..c215a632961f86e1be628bb1e9c96b68cbfef135 100644 (file)
@@ -195,8 +195,10 @@ inline CapabilityLevel canCompile(Node* node)
     case IsCellWithType:
     case MapHash:
     case GetMapBucket:
-    case LoadFromJSMapBucket:
-    case IsNonEmptyMapBucket:
+    case GetMapBucketHead:
+    case GetMapBucketNext:
+    case LoadKeyFromMapBucket:
+    case LoadValueFromMapBucket:
     case IsEmpty:
     case IsUndefined:
     case IsBoolean:
index 0acaa74e5f2f5bec535d81b810ea31162dafc9be..6335a0b66692e8c0fb0d39edf19f9ad6dc636fd7 100644 (file)
@@ -971,11 +971,17 @@ private:
         case GetMapBucket:
             compileGetMapBucket();
             break;
-        case LoadFromJSMapBucket:
-            compileLoadFromJSMapBucket();
+        case GetMapBucketHead:
+            compileGetMapBucketHead();
             break;
-        case IsNonEmptyMapBucket:
-            compileIsNonEmptyMapBucket();
+        case GetMapBucketNext:
+            compileGetMapBucketNext();
+            break;
+        case LoadKeyFromMapBucket:
+            compileLoadKeyFromMapBucket();
+            break;
+        case LoadValueFromMapBucket:
+            compileLoadValueFromMapBucket();
             break;
         case IsObject:
             compileIsObject();
@@ -8236,38 +8242,85 @@ private:
         m_out.jump(continuation);
 
         m_out.appendTo(notPresentInTable, continuation);
-        ValueFromBlock notPresentResult = m_out.anchor(m_out.constIntPtr(0));
+        ValueFromBlock notPresentResult;
+        if (m_node->child1().useKind() == MapObjectUse)
+            notPresentResult = m_out.anchor(weakPointer(vm().sentinelMapBucket.get()));
+        else if (m_node->child1().useKind() == SetObjectUse)
+            notPresentResult = m_out.anchor(weakPointer(vm().sentinelSetBucket.get()));
+        else
+            RELEASE_ASSERT_NOT_REACHED();
         m_out.jump(continuation);
 
         m_out.appendTo(continuation, lastNext);
-        setMapBucket(m_out.phi(pointerType(), bucketResult, slowPathResult, notPresentResult));
+        setJSValue(m_out.phi(pointerType(), bucketResult, slowPathResult, notPresentResult));
     }
 
-    void compileLoadFromJSMapBucket()
+    void compileGetMapBucketHead()
     {
-        LValue mapBucket = lowMapBucket(m_node->child1());
+        LValue map;
+        if (m_node->child1().useKind() == MapObjectUse)
+            map = lowMapObject(m_node->child1());
+        else if (m_node->child1().useKind() == SetObjectUse)
+            map = lowSetObject(m_node->child1());
+        else
+            RELEASE_ASSERT_NOT_REACHED();
 
+        ASSERT(HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead() == HashMapImpl<HashMapBucket<HashMapBucketDataKeyValue>>::offsetOfHead());
+        setJSValue(m_out.loadPtr(map, m_heaps.HashMapImpl_head));
+    }
+
+    void compileGetMapBucketNext()
+    {
+        LBasicBlock loopStart = m_out.newBlock();
         LBasicBlock continuation = m_out.newBlock();
+        LBasicBlock noBucket = m_out.newBlock();
         LBasicBlock hasBucket = m_out.newBlock();
+        LBasicBlock nextBucket = m_out.newBlock();
 
-        ValueFromBlock noBucketResult = m_out.anchor(m_out.constInt64(JSValue::encode(jsUndefined())));
+        LBasicBlock lastNext = m_out.insertNewBlocksBefore(loopStart);
 
-        m_out.branch(m_out.equal(mapBucket, m_out.constIntPtr(0)),
-            unsure(continuation), unsure(hasBucket));
+        ASSERT(HashMapBucket<HashMapBucketDataKey>::offsetOfNext() == HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext());
+        ASSERT(HashMapBucket<HashMapBucketDataKey>::offsetOfDeleted() == HashMapBucket<HashMapBucketDataKeyValue>::offsetOfDeleted());
+        LValue mapBucketPrev = lowCell(m_node->child1());
+        ValueFromBlock mapBucketStart = m_out.anchor(m_out.loadPtr(mapBucketPrev, m_heaps.HashMapBucket_next));
+        m_out.jump(loopStart);
 
-        LBasicBlock lastNext = m_out.appendTo(hasBucket, continuation);
-        ValueFromBlock bucketResult = m_out.anchor(m_out.load64(mapBucket, m_heaps.HashMapBucket_value));
+        m_out.appendTo(loopStart, noBucket);
+        LValue mapBucket = m_out.phi(pointerType(), mapBucketStart);
+        m_out.branch(m_out.isNull(mapBucket), unsure(noBucket), unsure(hasBucket));
+
+        m_out.appendTo(noBucket, hasBucket);
+        ValueFromBlock noBucketResult;
+        if (m_node->bucketOwnerType() == BucketOwnerType::Map)
+            noBucketResult = m_out.anchor(weakPointer(vm().sentinelMapBucket.get()));
+        else {
+            ASSERT(node->bucketOwnerType() == BucketOwnerType::Set);
+            noBucketResult = m_out.anchor(weakPointer(vm().sentinelSetBucket.get()));
+        }
         m_out.jump(continuation);
 
+        m_out.appendTo(hasBucket, nextBucket);
+        ValueFromBlock bucketResult = m_out.anchor(mapBucket);
+        m_out.branch(m_out.isZero32(m_out.load32(mapBucket, m_heaps.HashMapBucket_deleted)), unsure(continuation), unsure(nextBucket));
+
+        m_out.appendTo(nextBucket, continuation);
+        m_out.addIncomingToPhi(mapBucket, m_out.anchor(m_out.loadPtr(mapBucket, m_heaps.HashMapBucket_next)));
+        m_out.jump(loopStart);
+
         m_out.appendTo(continuation, lastNext);
-        setJSValue(m_out.phi(Int64, noBucketResult, bucketResult));
+        setJSValue(m_out.phi(pointerType(), noBucketResult, bucketResult));
     }
 
-    void compileIsNonEmptyMapBucket()
+    void compileLoadValueFromMapBucket()
     {
-        LValue bucket = lowMapBucket(m_node->child1());
-        LValue result = m_out.notEqual(bucket, m_out.constIntPtr(0));
-        setBoolean(result);
+        LValue mapBucket = lowCell(m_node->child1());
+        setJSValue(m_out.load64(mapBucket, m_heaps.HashMapBucket_value));
+    }
+
+    void compileLoadKeyFromMapBucket()
+    {
+        LValue mapBucket = lowCell(m_node->child1());
+        setJSValue(m_out.load64(mapBucket, m_heaps.HashMapBucket_key));
     }
 
     void compileIsObjectOrNull()
@@ -12864,17 +12917,6 @@ private:
         return result;
     }
 
-    LValue lowMapBucket(Edge edge)
-    {
-        LoweredNodeValue value = m_mapBucketValues.get(edge.node());
-        if (isValid(value))
-            return value.value();
-        
-        LValue result = lowCell(edge);
-        setStorage(edge.node(), result);
-        return result;
-    }
-    
     LValue strictInt52ToInt32(Edge edge, LValue value)
     {
         LValue result = m_out.castToInt32(value);
@@ -14388,10 +14430,6 @@ private:
     {
         m_storageValues.set(node, LoweredNodeValue(value, m_highBlock));
     }
-    void setMapBucket(Node* node, LValue value)
-    {
-        m_mapBucketValues.set(node, LoweredNodeValue(value, m_highBlock));
-    }
     void setDouble(Node* node, LValue value)
     {
         m_doubleValues.set(node, LoweredNodeValue(value, m_highBlock));
@@ -14425,10 +14463,6 @@ private:
     {
         setStorage(m_node, value);
     }
-    void setMapBucket(LValue value)
-    {
-        setMapBucket(m_node, value);
-    }
     void setDouble(LValue value)
     {
         setDouble(m_node, value);
@@ -14618,7 +14652,6 @@ private:
     HashMap<Node*, LoweredNodeValue> m_jsValueValues;
     HashMap<Node*, LoweredNodeValue> m_booleanValues;
     HashMap<Node*, LoweredNodeValue> m_storageValues;
-    HashMap<Node*, LoweredNodeValue> m_mapBucketValues;
     HashMap<Node*, LoweredNodeValue> m_doubleValues;
     
     // This is a bit of a hack. It prevents B3 from having to do CSE on loading of arguments.
index c812101a48d829faa253dd837b1939b5cadacec1..f2b724010c20b3275f413937f21709c3632721e9 100644 (file)
@@ -33,6 +33,7 @@
 #include "DirectArguments.h"
 #include "Error.h"
 #include "InjectedScriptHost.h"
+#include "IterationKind.h"
 #include "IteratorOperations.h"
 #include "IteratorPrototype.h"
 #include "JSArray.h"
 #include "JSGlobalObjectFunctions.h"
 #include "JSInjectedScriptHostPrototype.h"
 #include "JSMap.h"
-#include "JSMapIterator.h"
 #include "JSPromise.h"
 #include "JSSet.h"
-#include "JSSetIterator.h"
 #include "JSStringIterator.h"
 #include "JSTypedArrays.h"
 #include "JSWeakMap.h"
 #include "JSWeakSet.h"
 #include "JSWithScope.h"
+#include "MapIteratorPrototype.h"
 #include "ObjectConstructor.h"
 #include "ProxyObject.h"
 #include "RegExpObject.h"
 #include "ScopedArguments.h"
+#include "SetIteratorPrototype.h"
 #include "SourceCode.h"
 #include "TypedArrayInlines.h"
 #include "WeakMapData.h"
@@ -182,13 +183,15 @@ JSValue JSInjectedScriptHost::subtype(ExecState* exec)
     if (value.inherits(vm, JSWeakSet::info()))
         return jsNontrivialString(exec, ASCIILiteral("weakset"));
 
-    if (value.inherits(vm, JSMapIterator::info())
-        || value.inherits(vm, JSSetIterator::info())
-        || value.inherits(vm, JSStringIterator::info()))
+    if (value.inherits(vm, JSStringIterator::info()))
         return jsNontrivialString(exec, ASCIILiteral("iterator"));
 
-    if (object && object->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().arrayIteratorNextIndexPrivateName()))
-        return jsNontrivialString(exec, ASCIILiteral("iterator"));
+    if (object) {
+        if (object->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())
+            || object->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().mapBucketPrivateName())
+            || object->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().setBucketPrivateName()))
+            return jsNontrivialString(exec, ASCIILiteral("iterator"));
+    }
 
     if (value.inherits(vm, JSInt8Array::info())
         || value.inherits(vm, JSInt16Array::info())
@@ -335,52 +338,54 @@ JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", kind));
             return array;
         }
-    }
 
-    if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(vm, value)) {
-        String kind;
-        switch (mapIterator->kind()) {
-        case IterateKey:
-            kind = ASCIILiteral("key");
-            break;
-        case IterateValue:
-            kind = ASCIILiteral("value");
-            break;
-        case IterateKeyValue:
-            kind = ASCIILiteral("key+value");
-            break;
+        if (iteratorObject->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().mapBucketPrivateName())) {
+            JSValue iteratedValue = iteratorObject->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().iteratedObjectPrivateName());
+            String kind;
+            switch (static_cast<IterationKind>(iteratorObject->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().mapIteratorKindPrivateName()).asInt32())) {
+            case IterateKey:
+                kind = ASCIILiteral("key");
+                break;
+            case IterateValue:
+                kind = ASCIILiteral("value");
+                break;
+            case IterateKeyValue:
+                kind = ASCIILiteral("key+value");
+                break;
+            }
+            unsigned index = 0;
+            JSArray* array = constructEmptyArray(exec, nullptr, 2);
+            RETURN_IF_EXCEPTION(scope, JSValue());
+            array->putDirectIndex(exec, index++, constructInternalProperty(exec, "map", iteratedValue));
+            RETURN_IF_EXCEPTION(scope, JSValue());
+            scope.release();
+            array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
+            return array;
         }
-        unsigned index = 0;
-        JSArray* array = constructEmptyArray(exec, nullptr, 2);
-        RETURN_IF_EXCEPTION(scope, JSValue());
-        array->putDirectIndex(exec, index++, constructInternalProperty(exec, "map", mapIterator->iteratedValue()));
-        RETURN_IF_EXCEPTION(scope, JSValue());
-        scope.release();
-        array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
-        return array;
-    }
 
-    if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(vm, value)) {
-        String kind;
-        switch (setIterator->kind()) {
-        case IterateKey:
-            kind = ASCIILiteral("key");
-            break;
-        case IterateValue:
-            kind = ASCIILiteral("value");
-            break;
-        case IterateKeyValue:
-            kind = ASCIILiteral("key+value");
-            break;
+        if (iteratorObject->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().setBucketPrivateName())) {
+            JSValue iteratedValue = iteratorObject->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().iteratedObjectPrivateName());
+            String kind;
+            switch (static_cast<IterationKind>(iteratorObject->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().setIteratorKindPrivateName()).asInt32())) {
+            case IterateKey:
+                kind = ASCIILiteral("key");
+                break;
+            case IterateValue:
+                kind = ASCIILiteral("value");
+                break;
+            case IterateKeyValue:
+                kind = ASCIILiteral("key+value");
+                break;
+            }
+            unsigned index = 0;
+            JSArray* array = constructEmptyArray(exec, nullptr, 2);
+            RETURN_IF_EXCEPTION(scope, JSValue());
+            array->putDirectIndex(exec, index++, constructInternalProperty(exec, "set", iteratedValue));
+            RETURN_IF_EXCEPTION(scope, JSValue());
+            scope.release();
+            array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
+            return array;
         }
-        unsigned index = 0;
-        JSArray* array = constructEmptyArray(exec, nullptr, 2);
-        RETURN_IF_EXCEPTION(scope, JSValue());
-        array->putDirectIndex(exec, index++, constructInternalProperty(exec, "set", setIterator->iteratedValue()));
-        RETURN_IF_EXCEPTION(scope, JSValue());
-        scope.release();
-        array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
-        return array;
     }
 
     if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
@@ -522,6 +527,26 @@ static JSObject* cloneArrayIteratorObject(ExecState* exec, VM& vm, JSObject* ite
     return clone;
 }
 
+static JSObject* cloneMapIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue mapBucket, JSValue iteratedObject)
+{
+    ASSERT(iteratorObject->type() == FinalObjectType);
+    JSObject* clone = constructEmptyObject(exec, MapIteratorPrototype::create(vm, globalObject, MapIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
+    clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
+    clone->putDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName()));
+    clone->putDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName(), mapBucket);
+    return clone;
+}
+
+static JSObject* cloneSetIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue setBucket, JSValue iteratedObject)
+{
+    ASSERT(iteratorObject->type() == FinalObjectType);
+    JSObject* clone = constructEmptyObject(exec, SetIteratorPrototype::create(vm, globalObject, SetIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
+    clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
+    clone->putDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName()));
+    clone->putDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName(), setBucket);
+    return clone;
+}
+
 JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec)
 {
     if (exec->argumentCount() < 1)
@@ -533,19 +558,13 @@ JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec)
     JSValue iterator;
     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
     JSValue value = exec->uncheckedArgument(0);
-    if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(vm, value)) {
-        if (jsCast<JSMap*>(mapIterator->iteratedValue())->isIteratorProtocolFastAndNonObservable())
-            iterator = mapIterator->clone(exec);
-    } else if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(vm, value)) {
-        if (jsCast<JSSet*>(setIterator->iteratedValue())->isIteratorProtocolFastAndNonObservable())
-            iterator = setIterator->clone(exec);
-    } else if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
+    if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
         if (globalObject->isStringPrototypeIteratorProtocolFastAndNonObservable())
             iterator = stringIterator->clone(exec);
     } else if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(vm, value)) {
         // Detect an ArrayIterator by checking for one of its unique private properties.
+        JSValue iteratedObject = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
         if (JSValue nextIndex = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) {
-            JSValue iteratedObject = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
             if (isJSArray(iteratedObject)) {
                 JSArray* array = jsCast<JSArray*>(iteratedObject);
                 if (array->isIteratorProtocolFastAndNonObservable())
@@ -554,6 +573,12 @@ JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec)
                 if (globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable())
                     iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, globalObject, nextIndex, iteratedObject);
             }
+        } else if (JSValue mapBucket = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName())) {
+            if (jsCast<JSMap*>(iteratedObject)->isIteratorProtocolFastAndNonObservable())
+                iterator = cloneMapIteratorObject(exec, vm, iteratorObject, globalObject, mapBucket, iteratedObject);
+        } else if (JSValue setBucket = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName())) {
+            if (jsCast<JSSet*>(iteratedObject)->isIteratorProtocolFastAndNonObservable())
+                iterator = cloneSetIteratorObject(exec, vm, iteratorObject, globalObject, setBucket, iteratedObject);
         }
     }
     RETURN_IF_EXCEPTION(scope, { });
index 2908a10bc81a406d3e34a9d741c354e49c21461b..eadc56ab4f329c248b401fd2ddd86406122ac202 100644 (file)
@@ -97,6 +97,15 @@ public:
         return bucket;
     }
 
+    static HashMapBucket* createSentinel(VM& vm)
+    {
+        auto* bucket = create(vm);
+        bucket->setDeleted(true);
+        bucket->setKey(vm, jsUndefined());
+        bucket->setValue(vm, jsUndefined());
+        return bucket;
+    }
+
     HashMapBucket(VM& vm, Structure* structure)
         : Base(vm, structure)
     { }
@@ -150,6 +159,16 @@ public:
         return OBJECT_OFFSETOF(HashMapBucket, m_data) + OBJECT_OFFSETOF(Data, value);
     }
 
+    static ptrdiff_t offsetOfNext()
+    {
+        return OBJECT_OFFSETOF(HashMapBucket, m_next);
+    }
+
+    static ptrdiff_t offsetOfDeleted()
+    {
+        return OBJECT_OFFSETOF(HashMapBucket, m_deleted);
+    }
+
     template <typename T = Data>
     ALWAYS_INLINE static typename std::enable_if<std::is_same<T, HashMapBucketDataKeyValue>::value, JSValue>::type extractValue(const HashMapBucket& bucket)
     {
@@ -163,10 +182,10 @@ public:
     }
 
 private:
-    Data m_data;
     WriteBarrier<HashMapBucket> m_next;
     WriteBarrier<HashMapBucket> m_prev;
-    bool m_deleted { false };
+    uint32_t m_deleted { false };
+    Data m_data;
 };
 
 template <typename BucketType>
@@ -469,6 +488,11 @@ public:
         return m_capacity * sizeof(HashMapBucketType*);
     }
 
+    static ptrdiff_t offsetOfHead()
+    {
+        return OBJECT_OFFSETOF(HashMapImpl<HashMapBucketType>, m_head);
+    }
+
     static ptrdiff_t offsetOfBuffer()
     {
         return OBJECT_OFFSETOF(HashMapImpl<HashMapBucketType>, m_buffer);
index 4d46090c375850374b2a5d794bbda8e462032889..f98563aba71703b112dcf886ec455a705fb5ed0b 100644 (file)
@@ -137,8 +137,22 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "JSMapGetIntrinsic";
     case JSMapHasIntrinsic:
         return "JSMapHasIntrinsic";
+    case JSMapBucketHeadIntrinsic:
+        return "JSMapBucketHeadIntrinsic";
+    case JSMapBucketNextIntrinsic:
+        return "JSMapBucketNextIntrinsic";
+    case JSMapBucketKeyIntrinsic:
+        return "JSMapBucketKeyIntrinsic";
+    case JSMapBucketValueIntrinsic:
+        return "JSMapBucketValueIntrinsic";
     case JSSetHasIntrinsic:
         return "JSSetHasIntrinsic";
+    case JSSetBucketHeadIntrinsic:
+        return "JSSetBucketHeadIntrinsic";
+    case JSSetBucketNextIntrinsic:
+        return "JSSetBucketNextIntrinsic";
+    case JSSetBucketKeyIntrinsic:
+        return "JSSetBucketKeyIntrinsic";
     case HasOwnPropertyIntrinsic:
         return "HasOwnPropertyIntrinsic";
     case AtomicsAddIntrinsic:
index d70cd48e53da90dd60b7c2bc9bdff7b675c6064f..4c92a4d6440438ea190bd79cb31ca4c8a369c04c 100644 (file)
@@ -81,7 +81,14 @@ enum Intrinsic {
     BoundThisNoArgsFunctionCallIntrinsic,
     JSMapGetIntrinsic,
     JSMapHasIntrinsic,
+    JSMapBucketHeadIntrinsic,
+    JSMapBucketNextIntrinsic,
+    JSMapBucketKeyIntrinsic,
+    JSMapBucketValueIntrinsic,
     JSSetHasIntrinsic,
+    JSSetBucketHeadIntrinsic,
+    JSSetBucketNextIntrinsic,
+    JSSetBucketKeyIntrinsic,
     HasOwnPropertyIntrinsic,
     AtomicsAddIntrinsic,
     AtomicsAndIntrinsic,
index e63b4c5db7060f8a7e332ae548effdb65e91855b..33097a949a2f30c5f135cf521e4425a256f5165a 100644 (file)
@@ -99,7 +99,6 @@
 #include "JSLexicalEnvironment.h"
 #include "JSLock.h"
 #include "JSMap.h"
-#include "JSMapIterator.h"
 #include "JSModuleEnvironment.h"
 #include "JSModuleLoader.h"
 #include "JSModuleNamespaceObject.h"
 #include "JSPromiseConstructor.h"
 #include "JSPromisePrototype.h"
 #include "JSSet.h"
-#include "JSSetIterator.h"
 #include "JSStringIterator.h"
 #include "JSTemplateRegistryKey.h"
 #include "JSTypedArrayConstructors.h"
@@ -752,6 +750,13 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     JSFunction* privateFuncIsArraySlow = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArraySlow);
     JSFunction* privateFuncConcatMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncConcatMemcpy);
     JSFunction* privateFuncAppendMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncAppendMemcpy);
+    JSFunction* privateFuncMapBucketHead = JSFunction::create(vm, this, 0, String(), mapPrivateFuncMapBucketHead, JSMapBucketHeadIntrinsic);
+    JSFunction* privateFuncMapBucketNext = JSFunction::create(vm, this, 0, String(), mapPrivateFuncMapBucketNext, JSMapBucketNextIntrinsic);
+    JSFunction* privateFuncMapBucketKey = JSFunction::create(vm, this, 0, String(), mapPrivateFuncMapBucketKey, JSMapBucketKeyIntrinsic);
+    JSFunction* privateFuncMapBucketValue = JSFunction::create(vm, this, 0, String(), mapPrivateFuncMapBucketValue, JSMapBucketValueIntrinsic);
+    JSFunction* privateFuncSetBucketHead = JSFunction::create(vm, this, 0, String(), setPrivateFuncSetBucketHead, JSSetBucketHeadIntrinsic);
+    JSFunction* privateFuncSetBucketNext = JSFunction::create(vm, this, 0, String(), setPrivateFuncSetBucketNext, JSSetBucketNextIntrinsic);
+    JSFunction* privateFuncSetBucketKey = JSFunction::create(vm, this, 0, String(), setPrivateFuncSetBucketKey, JSSetBucketKeyIntrinsic);
 
     JSObject* regExpProtoFlagsGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->flags);
     catchScope.assertNoException();
@@ -784,6 +789,12 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     JSObject* asyncFromSyncIteratorPrototype = AsyncFromSyncIteratorPrototype::create(vm, this, AsyncFromSyncIteratorPrototype::createStructure(vm, this, m_iteratorPrototype.get()));
     AsyncFromSyncIteratorConstructorPrivateFunction->putDirect(vm, vm.propertyNames->prototype, asyncFromSyncIteratorPrototype);
 
+    JSObject* mapIteratorPrototype = MapIteratorPrototype::create(vm, this, MapIteratorPrototype::createStructure(vm, this, m_iteratorPrototype.get()));
+    createMapIteratorPrivateFunction->putDirect(vm, vm.propertyNames->prototype, mapIteratorPrototype);
+
+    JSObject* setIteratorPrototype = SetIteratorPrototype::create(vm, this, SetIteratorPrototype::createStructure(vm, this, m_iteratorPrototype.get()));
+    createSetIteratorPrivateFunction->putDirect(vm, vm.propertyNames->prototype, setIteratorPrototype);
+
     GlobalPropertyInfo staticGlobals[] = {
 #define INIT_PRIVATE_GLOBAL(name, code) GlobalPropertyInfo(vm.propertyNames->builtinNames().name ## PrivateName(), name ## PrivateFunction, DontEnum | DontDelete | ReadOnly),
         JSC_FOREACH_BUILTIN_FUNCTION_PRIVATE_GLOBAL_NAME(INIT_PRIVATE_GLOBAL)
@@ -818,15 +829,11 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->builtinNames().InternalPromisePrivateName(), internalPromiseConstructor, DontEnum | DontDelete | ReadOnly),
 
         GlobalPropertyInfo(vm.propertyNames->builtinNames().repeatCharacterPrivateName(), JSFunction::create(vm, this, 2, String(), stringProtoFuncRepeatCharacter), DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().SetIteratorPrivateName(), JSFunction::create(vm, this, 1, String(), privateFuncSetIterator), DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().setIteratorNextPrivateName(), JSFunction::create(vm, this, 0, String(), privateFuncSetIteratorNext), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isArrayPrivateName(), arrayConstructor->getDirect(vm, vm.propertyNames->isArray), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isArraySlowPrivateName(), privateFuncIsArraySlow, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isArrayConstructorPrivateName(), privateFuncIsArrayConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().concatMemcpyPrivateName(), privateFuncConcatMemcpy, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().appendMemcpyPrivateName(), privateFuncAppendMemcpy, DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().MapIteratorPrivateName(), JSFunction::create(vm, this, 1, String(), privateFuncMapIterator), DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().mapIteratorNextPrivateName(), JSFunction::create(vm, this, 0, String(), privateFuncMapIteratorNext), DontEnum | DontDelete | ReadOnly),
 
         GlobalPropertyInfo(vm.propertyNames->builtinNames().hostPromiseRejectionTrackerPrivateName(), JSFunction::create(vm, this, 2, String(), globalFuncHostPromiseRejectionTracker), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().InspectorInstrumentationPrivateName(), InspectorInstrumentationObject::create(vm, this, InspectorInstrumentationObject::createStructure(vm, this, m_objectPrototype.get())), DontEnum | DontDelete | ReadOnly),
@@ -867,6 +874,15 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         // Function prototype helpers.
         GlobalPropertyInfo(vm.propertyNames->builtinNames().makeBoundFunctionPrivateName(), JSFunction::create(vm, this, 5, String(), makeBoundFunction), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().hasOwnLengthPropertyPrivateName(), JSFunction::create(vm, this, 1, String(), hasOwnLengthProperty), DontEnum | DontDelete | ReadOnly),
+
+        // Map and Set helpers.
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().mapBucketHeadPrivateName(), privateFuncMapBucketHead, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().mapBucketNextPrivateName(), privateFuncMapBucketNext, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().mapBucketKeyPrivateName(), privateFuncMapBucketKey, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().mapBucketValuePrivateName(), privateFuncMapBucketValue, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().setBucketHeadPrivateName(), privateFuncSetBucketHead, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().setBucketNextPrivateName(), privateFuncSetBucketNext, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().setBucketKeyPrivateName(), privateFuncSetBucketKey, DontEnum | DontDelete | ReadOnly),
     };
     addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));
     
@@ -956,7 +972,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         }
 
         {
-            ObjectPropertyCondition condition = setupAdaptiveWatchpoint(m_mapIteratorPrototype.get(), m_vm.propertyNames->next);
+            ObjectPropertyCondition condition = setupAdaptiveWatchpoint(mapIteratorPrototype, m_vm.propertyNames->next);
             m_mapIteratorPrototypeNextWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_mapIteratorProtocolWatchpoint);
             m_mapIteratorPrototypeNextWatchpoint->install();
         }
@@ -967,7 +983,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         }
 
         {
-            ObjectPropertyCondition condition = setupAdaptiveWatchpoint(m_setIteratorPrototype.get(), m_vm.propertyNames->next);
+            ObjectPropertyCondition condition = setupAdaptiveWatchpoint(setIteratorPrototype, m_vm.propertyNames->next);
             m_setIteratorPrototypeNextWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_setIteratorProtocolWatchpoint);
             m_setIteratorPrototypeNextWatchpoint->install();
         }
index 46f0fc4676bf2715cc3defed9bc1bf5d107b66e5..6c29d1a1a89502884397621ff3b75326074b5b24 100644 (file)
@@ -126,8 +126,6 @@ template<typename Watchpoint> class ObjectPropertyChangeAdaptiveWatchpoint;
     macro(JSPromise, promise, promise, JSPromise, Promise, object)
 
 #define FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(macro) \
-    macro(MapIterator, mapIterator, mapIterator, JSMapIterator, MapIterator, iterator) \
-    macro(SetIterator, setIterator, setIterator, JSSetIterator, SetIterator, iterator) \
     macro(StringIterator, stringIterator, stringIterator, JSStringIterator, StringIterator, iterator) \
 
 #define FOR_EACH_SIMPLE_BUILTIN_TYPE(macro) \
index 81e326da0b2afb4603e24e5a49b73ef185e468fc..9c9d5065cae60261aad2f1088f1c5820ffd72467 100644 (file)
 
 namespace JSC {
 
-class JSMapIterator;
-
 class JSMap final : public HashMapImpl<HashMapBucket<HashMapBucketDataKeyValue>> {
     using Base = HashMapImpl<HashMapBucket<HashMapBucketDataKeyValue>>;
 public:
-    friend class JSMapIterator;
 
     DECLARE_EXPORT_INFO;
 
index 9957170a8a728595c46b1f30e33828eb0bcb5f63..7478fa63801bef13bf03e27673b72cfdcb530a6d 100644 (file)
@@ -31,7 +31,7 @@
 
 namespace JSC {
 
-const ClassInfo JSMapIterator::s_info = { "Map Iterator", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMapIterator) };
+const ClassInfo JSMapIterator::s_info = { "Map Iterator", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSMapIterator) };
 
 void JSMapIterator::finishCreation(VM& vm, JSMap* iteratedObject)
 {
@@ -58,12 +58,4 @@ JSValue JSMapIterator::createPair(CallFrame* callFrame, JSValue key, JSValue val
     return constructArray(callFrame, 0, globalObject, args);
 }
 
-JSMapIterator* JSMapIterator::clone(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto clone = JSMapIterator::create(vm, exec->jsCallee()->globalObject()->mapIteratorStructure(), m_map.get(), m_kind);
-    clone->setIterator(vm, m_iter.get());
-    return clone;
-}
-
 }
index 6e977c22bb18d7483d2d4ccf41be0b240bec0f56..576309bd7f28506a875057979055ed81b1ee1b03 100644 (file)
 
 namespace JSC {
 
-class JSMapIterator : public JSNonFinalObject {
+// Now, it is only used for serialization.
+class JSMapIterator : public JSCell {
     typedef HashMapBucket<HashMapBucketDataKeyValue> HashMapBucketType;
 public:
-    typedef JSNonFinalObject Base;
+    using Base = JSCell;
 
     DECLARE_EXPORT_INFO;
 
@@ -93,7 +94,6 @@ public:
 
     IterationKind kind() const { return m_kind; }
     JSValue iteratedValue() const { return m_map.get(); }
-    JSMapIterator* clone(ExecState*);
 
 private:
     JSMapIterator(VM& vm, Structure* structure, JSMap*, IterationKind kind)
index 4283293ea529fd2b3508798e8f4229e4c5c47814..8afcde7b82c2664781ba9cedd3ff19a84f03a326 100644 (file)
 
 namespace JSC {
 
-class JSSetIterator;
-
 class JSSet final : public HashMapImpl<HashMapBucket<HashMapBucketDataKey>> {
     using Base = HashMapImpl<HashMapBucket<HashMapBucketDataKey>>;
 public:
 
-    friend class JSSetIterator;
-
     DECLARE_EXPORT_INFO;
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
index fa9496a159734d615b03aa029a9ab91c2a6a0e67..e2f6e03be365d779bd3c75dc383c73ad24e26a40 100644 (file)
@@ -31,7 +31,7 @@
 
 namespace JSC {
 
-const ClassInfo JSSetIterator::s_info = { "Set Iterator", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSSetIterator) };
+const ClassInfo JSSetIterator::s_info = { "Set Iterator", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSSetIterator) };
 
 void JSSetIterator::finishCreation(VM& vm, JSSet* iteratedObject)
 {
@@ -58,12 +58,4 @@ JSValue JSSetIterator::createPair(CallFrame* callFrame, JSValue key, JSValue val
     return constructArray(callFrame, 0, globalObject, args);
 }
 
-JSSetIterator* JSSetIterator::clone(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto clone = JSSetIterator::create(vm, exec->jsCallee()->globalObject()->setIteratorStructure(), m_set.get(), m_kind);
-    clone->setIterator(vm, m_iter.get());
-    return clone;
-}
-
 }
index a6fb1b62bdd21e2eefb6b7ea82e838cc52d0c0a3..051a9896b54d91e59cee0326731cf12d3bcfd573 100644 (file)
 
 namespace JSC {
 
-class JSSetIterator : public JSNonFinalObject {
+// Now, it is only used for serialization.
+class JSSetIterator : public JSCell {
     typedef HashMapBucket<HashMapBucketDataKey> HashMapBucketType;
 public:
-    typedef JSNonFinalObject Base;
+    using Base = JSCell;
 
     DECLARE_EXPORT_INFO;
 
@@ -81,7 +82,6 @@ public:
 
     IterationKind kind() const { return m_kind; }
     JSValue iteratedValue() const { return m_set.get(); }
-    JSSetIterator* clone(ExecState*);
 
 private:
     JSSetIterator(VM& vm, Structure* structure, JSSet*, IterationKind kind)
index f292c8ee869c1a3526092d1d20c11393bd4c7fa9..7985083193b3c2cfe9cc261b2943c8fe98d3290e 100644 (file)
@@ -120,4 +120,43 @@ CallType MapConstructor::getCallData(JSCell*, CallData& callData)
     return CallType::Host;
 }
 
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketHead(ExecState* exec)
+{
+    ASSERT(isJSMap(exec->argument(0)));
+    JSMap* map = jsCast<JSMap*>(exec->uncheckedArgument(0));
+    auto* head = map->head();
+    ASSERT(head);
+    return JSValue::encode(head);
+}
+
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketNext(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSMap::BucketType*>(exec->vm(), exec->argument(0)));
+    auto* bucket = jsCast<JSMap::BucketType*>(exec->uncheckedArgument(0));
+    ASSERT(bucket);
+    bucket = bucket->next();
+    while (bucket) {
+        if (!bucket->deleted())
+            return JSValue::encode(bucket);
+        bucket = bucket->next();
+    }
+    return JSValue::encode(exec->vm().sentinelMapBucket.get());
+}
+
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketKey(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSMap::BucketType*>(exec->vm(), exec->argument(0)));
+    auto* bucket = jsCast<JSMap::BucketType*>(exec->uncheckedArgument(0));
+    ASSERT(bucket);
+    return JSValue::encode(bucket->key());
+}
+
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketValue(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSMap::BucketType*>(exec->vm(), exec->argument(0)));
+    auto* bucket = jsCast<JSMap::BucketType*>(exec->uncheckedArgument(0));
+    ASSERT(bucket);
+    return JSValue::encode(bucket->value());
+}
+
 }
index 99ff93bc803b513708e57c1ca7d0c5a929aadeda..c48a35ad0528a1f8378ef04f8332e1e505198539 100644 (file)
@@ -60,4 +60,9 @@ private:
     static CallType getCallData(JSCell*, CallData&);
 };
 
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketHead(ExecState*);
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketNext(ExecState*);
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketKey(ExecState*);
+EncodedJSValue JSC_HOST_CALL mapPrivateFuncMapBucketValue(ExecState*);
+
 } // namespace JSC
index ebfa80099ae13909ffe688f18b16d6b69dd2c931..aada481e900cc88e7666dc514ccae2e79fef05b0 100644 (file)
 #include "config.h"
 #include "MapIteratorPrototype.h"
 
-#include "IteratorOperations.h"
+#include "JSCBuiltins.h"
 #include "JSCInlines.h"
-#include "JSMapIterator.h"
 
 namespace JSC {
 
 const ClassInfo MapIteratorPrototype::s_info = { "Map Iterator", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(MapIteratorPrototype) };
 
-static EncodedJSValue JSC_HOST_CALL MapIteratorPrototypeFuncNext(ExecState*);
-
 void MapIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
     vm.prototypeMap.addPrototype(this);
 
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, MapIteratorPrototypeFuncNext, DontEnum, 0);
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("next", mapIteratorPrototypeNextCodeGenerator, DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "Map Iterator"), DontEnum | ReadOnly);
 }
 
-EncodedJSValue JSC_HOST_CALL MapIteratorPrototypeFuncNext(CallFrame* callFrame)
-{
-    VM& vm = callFrame->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSMapIterator* iterator = jsDynamicCast<JSMapIterator*>(vm, callFrame->thisValue());
-    if (!iterator)
-        return JSValue::encode(throwTypeError(callFrame, scope, ASCIILiteral("Cannot call MapIterator.next() on a non-MapIterator object")));
-
-    JSValue result;
-    if (iterator->next(callFrame, result)) {
-        scope.release();
-        return JSValue::encode(createIteratorResultObject(callFrame, result, false));
-    }
-    scope.release();
-    return JSValue::encode(createIteratorResultObject(callFrame, jsUndefined(), true));
-}
-
-
 }
index f49730379ebfcf579e0233a50d6d444ca8b296dd..2dc104bfca4ceadb5a7474dc12c55ac35e345103 100644 (file)
@@ -32,7 +32,6 @@
 #include "IteratorOperations.h"
 #include "JSCInlines.h"
 #include "JSMap.h"
-#include "JSMapIterator.h"
 #include "Lookup.h"
 
 #include "MapPrototype.lut.h"
@@ -44,6 +43,8 @@ const ClassInfo MapPrototype::s_info = { "Map", &Base::s_info, &mapPrototypeTabl
 /* Source for MapPrototype.lut.h
 @begin mapPrototypeTable
   forEach   JSBuiltin  DontEnum|Function 0
+  values    JSBuiltin  DontEnum|Function 0
+  keys      JSBuiltin  DontEnum|Function 0
 @end
 */
 
@@ -52,9 +53,6 @@ static EncodedJSValue JSC_HOST_CALL mapProtoFuncDelete(ExecState*);
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncGet(ExecState*);
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncHas(ExecState*);
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncSet(ExecState*);
-static EncodedJSValue JSC_HOST_CALL mapProtoFuncKeys(ExecState*);
-static EncodedJSValue JSC_HOST_CALL mapProtoFuncValues(ExecState*);
-static EncodedJSValue JSC_HOST_CALL mapProtoFuncEntries(ExecState*);
 
 static EncodedJSValue JSC_HOST_CALL mapProtoFuncSize(ExecState*);
     
@@ -69,13 +67,11 @@ void MapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->get, mapProtoFuncGet, DontEnum, 1, JSMapGetIntrinsic);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->has, mapProtoFuncHas, DontEnum, 1, JSMapHasIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->set, mapProtoFuncSet, DontEnum, 2);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), mapProtoFuncKeys, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().valuesPublicName(), mapProtoFuncValues, DontEnum, 0);
 
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getPrivateName(), mapProtoFuncGet, DontEnum, 1, JSMapGetIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().setPrivateName(), mapProtoFuncSet, DontEnum, 2);
 
-    JSFunction* entries = JSFunction::create(vm, globalObject, 0, vm.propertyNames->builtinNames().entriesPublicName().string(), mapProtoFuncEntries);
+    JSFunction* entries = JSFunction::create(vm, mapPrototypeEntriesCodeGenerator(vm), globalObject);
     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPublicName(), entries, DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, entries, DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "Map"), DontEnum | ReadOnly);
@@ -150,62 +146,4 @@ EncodedJSValue JSC_HOST_CALL mapProtoFuncSize(CallFrame* callFrame)
     return JSValue::encode(jsNumber(map->size()));
 }
 
-EncodedJSValue JSC_HOST_CALL mapProtoFuncValues(CallFrame* callFrame)
-{
-    VM& vm = callFrame->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSMap* thisObj = jsDynamicCast<JSMap*>(vm, callFrame->thisValue());
-    if (!thisObj)
-        return JSValue::encode(throwTypeError(callFrame, scope, ASCIILiteral("Cannot create a Map value iterator for a non-Map object.")));
-    return JSValue::encode(JSMapIterator::create(vm, callFrame->jsCallee()->globalObject()->mapIteratorStructure(), thisObj, IterateValue));
-}
-
-EncodedJSValue JSC_HOST_CALL mapProtoFuncEntries(CallFrame* callFrame)
-{
-    VM& vm = callFrame->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSMap* thisObj = jsDynamicCast<JSMap*>(vm, callFrame->thisValue());
-    if (!thisObj)
-        return JSValue::encode(throwTypeError(callFrame, scope, ASCIILiteral("Cannot create a Map entry iterator for a non-Map object.")));
-    return JSValue::encode(JSMapIterator::create(vm, callFrame->jsCallee()->globalObject()->mapIteratorStructure(), thisObj, IterateKeyValue));
-}
-
-EncodedJSValue JSC_HOST_CALL mapProtoFuncKeys(CallFrame* callFrame)
-{
-    VM& vm = callFrame->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSMap* thisObj = jsDynamicCast<JSMap*>(vm, callFrame->thisValue());
-    if (!thisObj)
-        return JSValue::encode(throwTypeError(callFrame, scope, ASCIILiteral("Cannot create a Map key iterator for a non-Map object.")));
-    return JSValue::encode(JSMapIterator::create(vm, callFrame->jsCallee()->globalObject()->mapIteratorStructure(), thisObj, IterateKey));
-}
-
-EncodedJSValue JSC_HOST_CALL privateFuncMapIterator(ExecState* exec)
-{
-    ASSERT(jsDynamicCast<JSMap*>(exec->vm(), exec->uncheckedArgument(0)));
-    JSMap* map = jsCast<JSMap*>(exec->uncheckedArgument(0));
-    return JSValue::encode(JSMapIterator::create(exec->vm(), exec->jsCallee()->globalObject()->mapIteratorStructure(), map, IterateKeyValue));
-}
-
-EncodedJSValue JSC_HOST_CALL privateFuncMapIteratorNext(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    ASSERT(jsDynamicCast<JSMapIterator*>(vm, exec->thisValue()));
-    JSMapIterator* iterator = jsCast<JSMapIterator*>(exec->thisValue());
-    JSValue key, value;
-    if (iterator->nextKeyValue(exec, key, value)) {
-        JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
-        resultArray->putDirectIndex(exec, 0, key);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
-        scope.release();
-        resultArray->putDirectIndex(exec, 1, value);
-        return JSValue::encode(jsBoolean(false));
-    }
-    return JSValue::encode(jsBoolean(true));
-}
-
 }
index 8bde8335d4a744a30ca7991aefd8438f7a0feabb..d5c5525c3a5632774308a44f56e3a4ddf6a88e05 100644 (file)
@@ -57,7 +57,4 @@ private:
     void finishCreation(VM&, JSGlobalObject*);
 };
 
-EncodedJSValue JSC_HOST_CALL privateFuncMapIterator(ExecState*);
-EncodedJSValue JSC_HOST_CALL privateFuncMapIteratorNext(ExecState*);
-
 } // namespace JSC
index 0f82943cf63501156131dad80499717344f29c82..286ca39e906a35d8723f885ee3b8b39dfdcf2b0f 100644 (file)
@@ -106,4 +106,35 @@ CallType SetConstructor::getCallData(JSCell*, CallData& callData)
     return CallType::Host;
 }
 
+EncodedJSValue JSC_HOST_CALL setPrivateFuncSetBucketHead(ExecState* exec)
+{
+    ASSERT(isJSSet(exec->argument(0)));
+    JSSet* set = jsCast<JSSet*>(exec->uncheckedArgument(0));
+    auto* head = set->head();
+    ASSERT(head);
+    return JSValue::encode(head);
+}
+
+EncodedJSValue JSC_HOST_CALL setPrivateFuncSetBucketNext(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSSet::BucketType*>(exec->vm(), exec->argument(0)));
+    auto* bucket = jsCast<JSSet::BucketType*>(exec->uncheckedArgument(0));
+    ASSERT(bucket);
+    bucket = bucket->next();
+    while (bucket) {
+        if (!bucket->deleted())
+            return JSValue::encode(bucket);
+        bucket = bucket->next();
+    }
+    return JSValue::encode(exec->vm().sentinelSetBucket.get());
+}
+
+EncodedJSValue JSC_HOST_CALL setPrivateFuncSetBucketKey(ExecState* exec)
+{
+    ASSERT(jsDynamicCast<JSSet::BucketType*>(exec->vm(), exec->argument(0)));
+    auto* bucket = jsCast<JSSet::BucketType*>(exec->uncheckedArgument(0));
+    ASSERT(bucket);
+    return JSValue::encode(bucket->key());
+}
+
 }
index 33d5a30babb21aa113d15c17603fccd671fc2e82..45f01306502f697220d545b4710b0c6501f67842 100644 (file)
@@ -60,4 +60,8 @@ private:
     static CallType getCallData(JSCell*, CallData&);
 };
 
+EncodedJSValue JSC_HOST_CALL setPrivateFuncSetBucketHead(ExecState*);
+EncodedJSValue JSC_HOST_CALL setPrivateFuncSetBucketNext(ExecState*);
+EncodedJSValue JSC_HOST_CALL setPrivateFuncSetBucketKey(ExecState*);
+
 } // namespace JSC
index d8a309e09f9d1d99cc4249d19afe2dd97e6febe5..838deef7f5bcb7576fa05952502c57dc0069c723 100644 (file)
 #include "config.h"
 #include "SetIteratorPrototype.h"
 
-#include "IteratorOperations.h"
+#include "JSCBuiltins.h"
 #include "JSCInlines.h"
-#include "JSSetIterator.h"
 
 namespace JSC {
 
 const ClassInfo SetIteratorPrototype::s_info = { "Set Iterator", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(SetIteratorPrototype) };
 
-static EncodedJSValue JSC_HOST_CALL SetIteratorPrototypeFuncNext(ExecState*);
-
 void SetIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
     vm.prototypeMap.addPrototype(this);
 
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, SetIteratorPrototypeFuncNext, DontEnum, 0);
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("next", setIteratorPrototypeNextCodeGenerator, DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "Set Iterator"), DontEnum | ReadOnly);
 }
 
-EncodedJSValue JSC_HOST_CALL SetIteratorPrototypeFuncNext(CallFrame* callFrame)
-{
-    VM& vm = callFrame->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue result;
-    JSSetIterator* iterator = jsDynamicCast<JSSetIterator*>(vm, callFrame->thisValue());
-    if (!iterator)
-        return JSValue::encode(throwTypeError(callFrame, scope, ASCIILiteral("Cannot call SetIterator.next() on a non-SetIterator object")));
-
-    if (iterator->next(callFrame, result)) {
-        scope.release();
-        return JSValue::encode(createIteratorResultObject(callFrame, result, false));
-    }
-    scope.release();
-    return JSValue::encode(createIteratorResultObject(callFrame, jsUndefined(), true));
-}
-
 }
index 3f79a33388fc964e1288cbc59cade0850e6fbd3f..3b20ed1668294c2bb13d8babba8d7a23225987c7 100644 (file)
@@ -32,7 +32,6 @@
 #include "IteratorOperations.h"
 #include "JSCInlines.h"
 #include "JSSet.h"
-#include "JSSetIterator.h"
 #include "Lookup.h"
 
 #include "SetPrototype.lut.h"
@@ -44,6 +43,7 @@ const ClassInfo SetPrototype::s_info = { "Set", &Base::s_info, &setPrototypeTabl
 /* Source for SetIteratorPrototype.lut.h
 @begin setPrototypeTable
   forEach   JSBuiltin  DontEnum|Function 0
+  entries   JSBuiltin  DontEnum|Function 0
 @end
 */
 
@@ -51,8 +51,6 @@ 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 setProtoFuncHas(ExecState*);
-static EncodedJSValue JSC_HOST_CALL setProtoFuncValues(ExecState*);
-static EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(ExecState*);
 
 
 static EncodedJSValue JSC_HOST_CALL setProtoFuncSize(ExecState*);
@@ -69,9 +67,8 @@ void SetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->has, setProtoFuncHas, DontEnum, 1, JSSetHasIntrinsic);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().hasPrivateName(), setProtoFuncHas, DontEnum, 1, JSSetHasIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().addPrivateName(), setProtoFuncAdd, DontEnum, 1);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), setProtoFuncEntries, DontEnum, 0);
 
-    JSFunction* values = JSFunction::create(vm, globalObject, 0, vm.propertyNames->builtinNames().valuesPublicName().string(), setProtoFuncValues);
+    JSFunction* values = JSFunction::create(vm, setPrototypeValuesCodeGenerator(vm), globalObject);
     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), values, DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPublicName(), values, DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, values, DontEnum);
@@ -137,48 +134,5 @@ EncodedJSValue JSC_HOST_CALL setProtoFuncSize(CallFrame* callFrame)
         return JSValue::encode(jsUndefined());
     return JSValue::encode(jsNumber(set->size()));
 }
-    
-EncodedJSValue JSC_HOST_CALL setProtoFuncValues(CallFrame* callFrame)
-{
-    VM& vm = callFrame->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSSet* thisObj = jsDynamicCast<JSSet*>(vm, callFrame->thisValue());
-    if (!thisObj)
-        return JSValue::encode(throwTypeError(callFrame, scope, ASCIILiteral("Cannot create a Set value iterator for a non-Set object.")));
-    return JSValue::encode(JSSetIterator::create(vm, callFrame->jsCallee()->globalObject()->setIteratorStructure(), thisObj, IterateValue));
-}
-
-EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(CallFrame* callFrame)
-{
-    VM& vm = callFrame->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSSet* thisObj = jsDynamicCast<JSSet*>(vm, callFrame->thisValue());
-    if (!thisObj)
-        return JSValue::encode(throwTypeError(callFrame, scope, ASCIILiteral("Cannot create a Set entry iterator for a non-Set object.")));
-    return JSValue::encode(JSSetIterator::create(vm, callFrame->jsCallee()->globalObject()->setIteratorStructure(), thisObj, IterateKeyValue));
-}
-
-EncodedJSValue JSC_HOST_CALL privateFuncSetIterator(ExecState* exec)
-{
-
-    ASSERT(jsDynamicCast<JSSet*>(exec->vm(), exec->uncheckedArgument(0)));
-    JSSet* set = jsCast<JSSet*>(exec->uncheckedArgument(0));
-    return JSValue::encode(JSSetIterator::create(exec->vm(), exec->jsCallee()->globalObject()->setIteratorStructure(), set, IterateKey));
-}
-
-EncodedJSValue JSC_HOST_CALL privateFuncSetIteratorNext(ExecState* exec)
-{
-    ASSERT(jsDynamicCast<JSSetIterator*>(exec->vm(), 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));
-    }
-    return JSValue::encode(jsBoolean(true));
-}
 
 }
index 3d453b623ce6d54b9d08a301692de5198c78c2e7..190f3096b55caca43c99a18beee15c1f3cc85408 100644 (file)
@@ -57,7 +57,4 @@ private:
     void finishCreation(VM&, JSGlobalObject*);
 };
 
-EncodedJSValue JSC_HOST_CALL privateFuncSetIterator(ExecState*);
-EncodedJSValue JSC_HOST_CALL privateFuncSetIteratorNext(ExecState*);
-
 } // namespace JSC
index dd1092e3c7665216bc40993f3238a8e1bc83b0b0..0968361eac1406ba5ebfb78f8d0a79fbaec836ec 100644 (file)
 #include "JSInternalPromiseDeferred.h"
 #include "JSLock.h"
 #include "JSMap.h"
+#include "JSMapIterator.h"
 #include "JSPromiseDeferred.h"
 #include "JSPropertyNameEnumerator.h"
 #include "JSScriptFetcher.h"
+#include "JSSet.h"
+#include "JSSetIterator.h"
 #include "JSSourceCode.h"
 #include "JSTemplateRegistryKey.h"
 #include "JSWebAssembly.h"
@@ -275,6 +278,11 @@ VM::VM(VMType vmType, HeapType heapType)
     functionCodeBlockStructure.set(*this, FunctionCodeBlock::createStructure(*this, 0, jsNull()));
     hashMapBucketSetStructure.set(*this, HashMapBucket<HashMapBucketDataKey>::createStructure(*this, 0, jsNull()));
     hashMapBucketMapStructure.set(*this, HashMapBucket<HashMapBucketDataKeyValue>::createStructure(*this, 0, jsNull()));
+    setIteratorStructure.set(*this, JSSetIterator::createStructure(*this, 0, jsNull()));
+    mapIteratorStructure.set(*this, JSMapIterator::createStructure(*this, 0, jsNull()));
+
+    sentinelSetBucket.set(*this, JSSet::BucketType::createSentinel(*this));
+    sentinelMapBucket.set(*this, JSMap::BucketType::createSentinel(*this));
 
     nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull()));
     smallStrings.initializeCommonStrings(*this);
index 1fa1f1b50ff0ffe66ab5089b74e97e7a6f355516..cc1f5c1f1a9bd8b53ee1a888636c86369458b886 100644 (file)
@@ -384,8 +384,12 @@ public:
     Strong<Structure> functionCodeBlockStructure;
     Strong<Structure> hashMapBucketSetStructure;
     Strong<Structure> hashMapBucketMapStructure;
+    Strong<Structure> setIteratorStructure;
+    Strong<Structure> mapIteratorStructure;
 
     Strong<JSCell> emptyPropertyNameEnumerator;
+    Strong<JSCell> sentinelSetBucket;
+    Strong<JSCell> sentinelMapBucket;
 
     std::unique_ptr<PromiseDeferredTimer> promiseDeferredTimer;
     
index bac219030f8c663ff640b5a8dc137c4d0412eb74..9c1631fc1cecdea19c68ba71f92355d69cc5ba5d 100644 (file)
@@ -1,3 +1,13 @@
+2017-08-23  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Optimize Map iteration with intrinsic
+        https://bugs.webkit.org/show_bug.cgi?id=174355
+
+        Reviewed by Saam Barati.
+
+        * bindings/js/SerializedScriptValue.cpp:
+        (WebCore::CloneSerializer::serialize):
+
 2017-08-23  Alex Christensen  <achristensen@webkit.org>
 
         Stop using PolicyCallback for new window policies
index 6fd83829f8aed358130da0aadffa73860a32f586..93b890c20edee1131c250cd33fab01bbe38abf6b 100644 (file)
@@ -1593,7 +1593,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
                 JSMap* inMap = jsCast<JSMap*>(inValue);
                 if (!startMap(inMap))
                     break;
-                JSMapIterator* iterator = JSMapIterator::create(vm, m_exec->lexicalGlobalObject()->mapIteratorStructure(), inMap, IterateKeyValue);
+                JSMapIterator* iterator = JSMapIterator::create(vm, vm.mapIteratorStructure.get(), inMap, IterateKeyValue);
                 m_gcBuffer.append(inMap);
                 m_gcBuffer.append(iterator);
                 mapIteratorStack.append(iterator);
@@ -1637,7 +1637,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
                 JSSet* inSet = jsCast<JSSet*>(inValue);
                 if (!startSet(inSet))
                     break;
-                JSSetIterator* iterator = JSSetIterator::create(vm, m_exec->lexicalGlobalObject()->setIteratorStructure(), inSet, IterateKey);
+                JSSetIterator* iterator = JSSetIterator::create(vm, vm.setIteratorStructure.get(), inSet, IterateKey);
                 m_gcBuffer.append(inSet);
                 m_gcBuffer.append(iterator);
                 setIteratorStack.append(iterator);