[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 02c07c9..a01040a 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 7a0d440..5d06295 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 c1166e3..a0cedf0 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 178b216..789f60c 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 7e3cc44..30a797d 100644 (file)
@@ -28,6 +28,8 @@
 @globalPrivate
 function createArrayIterator(iteratedObject, kind, iterationFunction)
 {
+    "use strict";
+
     this.@iteratedObject = iteratedObject;
     this.@arrayIteratorKind = kind;
     this.@arrayIteratorNextIndex = 0;
index d9961c7..68fe7c1 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 8302602..4ec041a 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 e9b6626..ba30170 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 e2ba90e..329ffe8 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 d71eebd..9ae1ac8 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 7a6bb46..be892f2 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 f603c74..40eea94 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 54740f3..a3cbb20 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 4dbe4c5..9a4bd38 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 4a23de7..07dca04 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 a450fad..daf0bd2 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 6a5c950..60bd572 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 7c87f20..b82c922 100644 (file)
@@ -63,8 +63,11 @@ enum LocationKind {
     StackLoc,
     StackPayloadLoc,
     MapBucketLoc,
-    JSMapGetLoc,
-    MapHasLoc,
+    MapBucketHeadLoc,
+    MapBucketValueLoc,
+    MapBucketKeyLoc,
+    MapBucketMapNextLoc,
+    MapBucketSetNextLoc,
     DOMStateLoc,
 };
 
index 1b3ad6b..cf4cb04 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 277d4c3..867a92c 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 cbb7f4c..5c41c83 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 084f726..891d5e0 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 1ab1724..1bdc59e 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 186a5f0..7c2c86b 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 b651328..b6122ef 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 35501aa..1031652 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 fa63608..8326241 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 570ca60..f74865a 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 51f6af1..c215a63 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 0acaa74..6335a0b 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 c812101..f2b7240 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 2908a10..eadc56a 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 4d46090..f98563a 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 d70cd48..4c92a4d 100644 (file)
@@ -81,7 +81,14 @@ enum Intrinsic {
     BoundThisNoArgsFunctionCallIntrinsic,
     JSMapGetIntrinsic,
     JSMapHasIntrinsic,
+    JSMapBucketHeadIntrinsic,
+    JSMapBucketNextIntrinsic,
+    JSMapBucketKeyIntrinsic,
+    JSMapBucketValueIntrinsic,
     JSSetHasIntrinsic,
+    JSSetBucketHeadIntrinsic,
+    JSSetBucketNextIntrinsic,
+    JSSetBucketKeyIntrinsic,
     HasOwnPropertyIntrinsic,
     AtomicsAddIntrinsic,
     AtomicsAndIntrinsic,
index e63b4c5..33097a9 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 46f0fc4..6c29d1a 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 81e326d..9c9d506 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 9957170..7478fa6 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 6e977c2..576309b 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 4283293..8afcde7 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 fa9496a..e2f6e03 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 a6fb1b6..051a989 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 f292c8e..7985083 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 99ff93b..c48a35a 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 ebfa800..aada481 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 f497303..2dc104b 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 8bde833..d5c5525 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 0f82943..286ca39 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 33d5a30..45f0130 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 d8a309e..838deef 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 3f79a33..3b20ed1 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 3d453b6..190f309 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 dd1092e..0968361 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 1fa1f1b..cc1f5c1 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 bac2190..9c1631f 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 6fd8382..93b890c 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);