+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'
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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);
+ }
+}
${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
${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
+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
$(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 \
$(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 \
@globalPrivate
function createArrayIterator(iteratedObject, kind, iterationFunction)
{
+ "use strict";
+
this.@iteratedObject = iteratedObject;
this.@arrayIteratorKind = kind;
this.@arrayIteratorNextIndex = 0;
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) \
--- /dev/null
+/*
+ * 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);
+}
/*
- * 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";
@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);
}
--- /dev/null
+/*
+ * 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);
+}
* 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";
@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);
}
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)));
macro(promiseStatePending) \
macro(promiseStateFulfilled) \
macro(promiseStateRejected) \
+ macro(sentinelMapBucket) \
+ macro(sentinelSetBucket) \
macro(GeneratorResumeModeNormal) \
macro(GeneratorResumeModeThrow) \
macro(GeneratorResumeModeReturn) \
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.
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:
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;
}
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;
}
// 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:
case GetGlobalObject:
case StringCharCodeAt:
case CompareStrictEq:
- case CompareEqPtr:
case IsEmpty:
case IsUndefined:
case IsBoolean:
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));
case StringFromCharCode:
case MapHash:
case GetMapBucket:
- case LoadFromJSMapBucket:
- case IsNonEmptyMapBucket:
+ case GetMapBucketHead:
+ case GetMapBucketNext:
+ case LoadKeyFromMapBucket:
+ case LoadValueFromMapBucket:
case Unreachable:
case ExtractOSREntryLocal:
case CheckTierUpInLoop:
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: {
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");
StackLoc,
StackPayloadLoc,
MapBucketLoc,
- JSMapGetLoc,
- MapHasLoc,
+ MapBucketHeadLoc,
+ MapBucketValueLoc,
+ MapBucketKeyLoc,
+ MapBucketMapNextLoc,
+ MapBucketSetNextLoc,
DOMStateLoc,
};
unsigned identifierNumber { 0 };
};
+enum class BucketOwnerType : uint32_t {
+ Map,
+ Set
+};
+
// === Node ===
//
// Node represents a single operation in the data flow graph.
case StringReplace:
case StringReplaceRegExp:
case ToNumber:
- case LoadFromJSMapBucket:
+ case LoadKeyFromMapBucket:
+ case LoadValueFromMapBucket:
case CallDOMGetter:
case CallDOM:
case ParseInt:
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())
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
{
/* 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 */\
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;
}
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;
}
case GetGlobalLexicalVariable:
case GetClosureVar:
case GetFromArguments:
- case LoadFromJSMapBucket:
+ case LoadKeyFromMapBucket:
+ case LoadValueFromMapBucket:
case ToNumber:
case GetArgument:
case CallDOMGetter:
case MapHash:
setPrediction(SpecInt32Only);
break;
+
case GetMapBucket:
+ case GetMapBucketHead:
+ case GetMapBucketNext:
setPrediction(SpecCellOther);
break;
- case IsNonEmptyMapBucket:
- setPrediction(SpecBoolean);
- break;
case GetRestLength:
case ArrayIndexOf: {
case MapHash:
case ToLowerCase:
case GetMapBucket:
- case LoadFromJSMapBucket:
- case IsNonEmptyMapBucket:
+ case GetMapBucketHead:
+ case GetMapBucketNext:
+ case LoadKeyFromMapBucket:
+ case LoadValueFromMapBucket:
case AtomicsAdd:
case AtomicsAnd:
case AtomicsCompareExchange:
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)
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
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>
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;
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;
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);
}
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);
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()) \
case IsCellWithType:
case MapHash:
case GetMapBucket:
- case LoadFromJSMapBucket:
- case IsNonEmptyMapBucket:
+ case GetMapBucketHead:
+ case GetMapBucketNext:
+ case LoadKeyFromMapBucket:
+ case LoadValueFromMapBucket:
case IsEmpty:
case IsUndefined:
case IsBoolean:
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();
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()
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);
{
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));
{
setStorage(m_node, value);
}
- void setMapBucket(LValue value)
- {
- setMapBucket(m_node, value);
- }
void setDouble(LValue value)
{
setDouble(m_node, value);
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.
#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"
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())
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)) {
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)
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())
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, { });
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)
{ }
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)
{
}
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>
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);
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:
BoundThisNoArgsFunctionCallIntrinsic,
JSMapGetIntrinsic,
JSMapHasIntrinsic,
+ JSMapBucketHeadIntrinsic,
+ JSMapBucketNextIntrinsic,
+ JSMapBucketKeyIntrinsic,
+ JSMapBucketValueIntrinsic,
JSSetHasIntrinsic,
+ JSSetBucketHeadIntrinsic,
+ JSSetBucketNextIntrinsic,
+ JSSetBucketKeyIntrinsic,
HasOwnPropertyIntrinsic,
AtomicsAddIntrinsic,
AtomicsAndIntrinsic,
#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"
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();
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)
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),
// 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));
}
{
- 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();
}
}
{
- 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();
}
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) \
namespace JSC {
-class JSMapIterator;
-
class JSMap final : public HashMapImpl<HashMapBucket<HashMapBucketDataKeyValue>> {
using Base = HashMapImpl<HashMapBucket<HashMapBucketDataKeyValue>>;
public:
- friend class JSMapIterator;
DECLARE_EXPORT_INFO;
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)
{
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;
-}
-
}
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;
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)
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)
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)
{
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;
-}
-
}
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;
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)
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());
+}
+
}
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
#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));
-}
-
-
}
#include "IteratorOperations.h"
#include "JSCInlines.h"
#include "JSMap.h"
-#include "JSMapIterator.h"
#include "Lookup.h"
#include "MapPrototype.lut.h"
/* Source for MapPrototype.lut.h
@begin mapPrototypeTable
forEach JSBuiltin DontEnum|Function 0
+ values JSBuiltin DontEnum|Function 0
+ keys JSBuiltin DontEnum|Function 0
@end
*/
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*);
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);
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));
-}
-
}
void finishCreation(VM&, JSGlobalObject*);
};
-EncodedJSValue JSC_HOST_CALL privateFuncMapIterator(ExecState*);
-EncodedJSValue JSC_HOST_CALL privateFuncMapIteratorNext(ExecState*);
-
} // namespace JSC
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());
+}
+
}
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
#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));
-}
-
}
#include "IteratorOperations.h"
#include "JSCInlines.h"
#include "JSSet.h"
-#include "JSSetIterator.h"
#include "Lookup.h"
#include "SetPrototype.lut.h"
/* Source for SetIteratorPrototype.lut.h
@begin setPrototypeTable
forEach JSBuiltin DontEnum|Function 0
+ entries JSBuiltin DontEnum|Function 0
@end
*/
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*);
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);
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));
-}
}
void finishCreation(VM&, JSGlobalObject*);
};
-EncodedJSValue JSC_HOST_CALL privateFuncSetIterator(ExecState*);
-EncodedJSValue JSC_HOST_CALL privateFuncSetIteratorNext(ExecState*);
-
} // namespace JSC
#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"
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);
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;
+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
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);
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);