From d2081119d20c80f934136c469030efd35de50e38 Mon Sep 17 00:00:00 2001 From: "shvaikalesh@gmail.com" Date: Wed, 16 Dec 2020 00:33:34 +0000 Subject: [PATCH] Non-enumerable property fails to shadow inherited enumerable property from for-in https://bugs.webkit.org/show_bug.cgi?id=38970 Reviewed by Keith Miller. JSTests: * stress/arguments-bizarre-behaviour-disable-enumerability.js: * stress/for-in-redefine-enumerable.js: Added. * stress/for-in-shadow-non-enumerable.js: Added. * test262/expectations.yaml: Mark 4 test cases as passing. Source/JavaScriptCore: While for/in was initially specified with notion of "shadowing", it wasn't clarified until ES5 that [[Enumerable]] attributes are ignored when determining if a property has already been processed. Recently, for/in spec was expanded [1] to pin down common case enumeration as it's currently implemented by V8 and SpiderMonkey. Since keeping track of DontEnum properties is a massive slowdown for uncached runs (with any data structure used), this patch simply adds [[Enumerable]] check to has_{indexed,structure,generic}_property bytecode ops and does renaming chores. Common code is now shared between HasIndexedProperty (emitted for `0 in arr`) and HasEnumerableIndexedProperty DFG nodes via passing different slow path ops rather than having OpInfo with PropertySlot::InternalMethodType, which is a nice refactor. While this change aligns common case for/in enumeration with the spec and other engines, it also introduces a few observable discrepancies from V8 and SpiderMonkey, which are permitted by the spec [2]: a) properties that have been redefined as DontEnum within loop body are skipped, which matches the spec [3] and seems like expected behavior; b) "shadowing" is broken if a DontEnum property of already visited object is added / deleted / redefined within loop body, which (pretty much) never happens. This patch introduces a new invariant: all properties getOwn*PropertyNames() returns in DontEnumPropertiesMode::Exclude should be reported as [[Enumerable]] by getOwnPropertySlot(). JSCallbackObject and RuntimeArray are fixed to follow it. for/in and Object.keys microbenchmarks are neutral. This change does not affect JSPropertyNameEnumerator caching, nor fast paths of its bytecodes. [1]: https://github.com/tc39/ecma262/pull/1791 [2]: https://tc39.es/ecma262/#sec-enumerate-object-properties (last paragraph) [3]: https://tc39.es/ecma262/#sec-%foriniteratorprototype%.next (step 7.b.iii) * API/JSCallbackObjectFunctions.h: (JSC::JSCallbackObject::getOwnPropertySlot): * API/tests/testapi.c: * API/tests/testapiScripts/testapi.js: * bytecode/BytecodeList.rb: * bytecode/BytecodeUseDef.cpp: (JSC::computeUsesForBytecodeIndexImpl): (JSC::computeDefsForBytecodeIndexImpl): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::finishCreation): * bytecode/Opcode.h: * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitHasEnumerableIndexedProperty): (JSC::BytecodeGenerator::emitHasEnumerableStructureProperty): (JSC::BytecodeGenerator::emitHasEnumerableProperty): (JSC::BytecodeGenerator::emitHasGenericProperty): Deleted. (JSC::BytecodeGenerator::emitHasIndexedProperty): Deleted. (JSC::BytecodeGenerator::emitHasStructureProperty): Deleted. * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::ForInNode::emitBytecode): * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter::executeEffects): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGClobberize.h: (JSC::DFG::clobberize): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): (JSC::DFG::FixupPhase::convertToHasIndexedProperty): * dfg/DFGNode.h: (JSC::DFG::Node::hasArrayMode): (JSC::DFG::Node::hasInternalMethodType const): Deleted. (JSC::DFG::Node::internalMethodType const): Deleted. (JSC::DFG::Node::setInternalMethodType): Deleted. * dfg/DFGNodeType.h: * dfg/DFGOperations.cpp: (JSC::DFG::JSC_DEFINE_JIT_OPERATION): * dfg/DFGOperations.h: * dfg/DFGPredictionPropagationPhase.cpp: * dfg/DFGSSALoweringPhase.cpp: (JSC::DFG::SSALoweringPhase::handleNode): * dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileHasEnumerableProperty): (JSC::DFG::SpeculativeJIT::compileHasEnumerableStructureProperty): (JSC::DFG::SpeculativeJIT::compileHasIndexedProperty): (JSC::DFG::SpeculativeJIT::compileHasGenericProperty): Deleted. (JSC::DFG::SpeculativeJIT::compileHasStructureProperty): Deleted. * dfg/DFGSpeculativeJIT.h: * dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compile): * ftl/FTLCapabilities.cpp: (JSC::FTL::canCompile): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileNode): (JSC::FTL::DFG::LowerDFGToB3::compileHasIndexedProperty): (JSC::FTL::DFG::LowerDFGToB3::compileHasEnumerableProperty): (JSC::FTL::DFG::LowerDFGToB3::compileHasEnumerableStructureProperty): (JSC::FTL::DFG::LowerDFGToB3::compileHasGenericProperty): Deleted. (JSC::FTL::DFG::LowerDFGToB3::compileHasStructureProperty): Deleted. * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): (JSC::JIT::privateCompileSlowCases): * jit/JIT.h: * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_has_enumerable_structure_property): (JSC::JIT::emit_op_has_enumerable_indexed_property): (JSC::JIT::emitSlow_op_has_enumerable_indexed_property): (JSC::JIT::emit_op_has_structure_property): Deleted. (JSC::JIT::emit_op_has_indexed_property): Deleted. (JSC::JIT::emitSlow_op_has_indexed_property): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_has_enumerable_structure_property): (JSC::JIT::emit_op_has_enumerable_indexed_property): (JSC::JIT::emitSlow_op_has_enumerable_indexed_property): (JSC::JIT::emit_op_has_structure_property): Deleted. (JSC::JIT::emit_op_has_indexed_property): Deleted. (JSC::JIT::emitSlow_op_has_indexed_property): Deleted. * jit/JITOperations.cpp: (JSC::JSC_DEFINE_JIT_OPERATION): * jit/JITOperations.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter64.asm: * runtime/CommonSlowPaths.cpp: (JSC::JSC_DEFINE_COMMON_SLOW_PATH): * runtime/CommonSlowPaths.h: * runtime/JSObject.cpp: (JSC::JSObject::hasProperty const): (JSC::JSObject::hasEnumerableProperty const): (JSC::JSObject::hasPropertyGeneric const): Deleted. * runtime/JSObject.h: Source/WebCore: Report RuntimeArray indices as [[Enumerable]]. Test: platform/mac/fast/dom/wrapper-classes-objc.html * bridge/runtime_array.cpp: (JSC::RuntimeArray::getOwnPropertySlot): (JSC::RuntimeArray::getOwnPropertySlotByIndex): LayoutTests: * platform/mac/fast/dom/wrapper-classes-objc-expected.txt: * platform/mac/fast/dom/wrapper-classes-objc.html: git-svn-id: http://svn.webkit.org/repository/webkit/trunk@270874 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- JSTests/ChangeLog | 12 ++ ...bizarre-behaviour-disable-enumerability.js | 2 +- JSTests/stress/for-in-redefine-enumerable.js | 107 +++++++++++ .../stress/for-in-shadow-non-enumerable.js | 174 ++++++++++++++++++ JSTests/test262/expectations.yaml | 6 - LayoutTests/ChangeLog | 10 + .../dom/wrapper-classes-objc-expected.txt | 1 + .../mac/fast/dom/wrapper-classes-objc.html | 1 + .../API/JSCallbackObjectFunctions.h | 19 +- Source/JavaScriptCore/API/tests/testapi.c | 2 + .../API/tests/testapiScripts/testapi.js | 24 ++- Source/JavaScriptCore/ChangeLog | 139 ++++++++++++++ .../JavaScriptCore/bytecode/BytecodeList.rb | 6 +- .../bytecode/BytecodeUseDef.cpp | 12 +- Source/JavaScriptCore/bytecode/CodeBlock.cpp | 2 +- Source/JavaScriptCore/bytecode/Opcode.h | 2 +- .../bytecompiler/BytecodeGenerator.cpp | 12 +- .../bytecompiler/BytecodeGenerator.h | 6 +- .../bytecompiler/NodesCodegen.cpp | 6 +- .../dfg/DFGAbstractInterpreterInlines.h | 7 +- .../JavaScriptCore/dfg/DFGByteCodeParser.cpp | 28 +-- Source/JavaScriptCore/dfg/DFGCapabilities.cpp | 6 +- Source/JavaScriptCore/dfg/DFGClobberize.h | 8 +- Source/JavaScriptCore/dfg/DFGDoesGC.cpp | 5 +- Source/JavaScriptCore/dfg/DFGFixupPhase.cpp | 8 +- Source/JavaScriptCore/dfg/DFGNode.h | 18 +- Source/JavaScriptCore/dfg/DFGNodeType.h | 5 +- Source/JavaScriptCore/dfg/DFGOperations.cpp | 23 ++- Source/JavaScriptCore/dfg/DFGOperations.h | 5 +- .../dfg/DFGPredictionPropagationPhase.cpp | 7 +- .../dfg/DFGSSALoweringPhase.cpp | 1 + Source/JavaScriptCore/dfg/DFGSafeToExecute.h | 5 +- .../JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 12 +- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h | 6 +- .../dfg/DFGSpeculativeJIT32_64.cpp | 14 +- .../dfg/DFGSpeculativeJIT64.cpp | 14 +- Source/JavaScriptCore/ftl/FTLCapabilities.cpp | 7 +- Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp | 35 ++-- Source/JavaScriptCore/jit/JIT.cpp | 10 +- Source/JavaScriptCore/jit/JIT.h | 6 +- Source/JavaScriptCore/jit/JITOpcodes.cpp | 12 +- Source/JavaScriptCore/jit/JITOpcodes32_64.cpp | 12 +- Source/JavaScriptCore/jit/JITOperations.cpp | 4 +- Source/JavaScriptCore/jit/JITOperations.h | 1 + .../llint/LowLevelInterpreter.asm | 6 +- .../llint/LowLevelInterpreter64.asm | 4 +- .../runtime/CommonSlowPaths.cpp | 18 +- .../JavaScriptCore/runtime/CommonSlowPaths.h | 6 +- Source/JavaScriptCore/runtime/JSObject.cpp | 31 +++- Source/JavaScriptCore/runtime/JSObject.h | 4 +- Source/WebCore/ChangeLog | 15 ++ Source/WebCore/bridge/runtime_array.cpp | 4 +- 52 files changed, 693 insertions(+), 197 deletions(-) create mode 100644 JSTests/stress/for-in-redefine-enumerable.js create mode 100644 JSTests/stress/for-in-shadow-non-enumerable.js diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog index 57139afcee1ee..94099180d9368 100644 --- a/JSTests/ChangeLog +++ b/JSTests/ChangeLog @@ -1,3 +1,15 @@ +2020-12-15 Alexey Shvayka + + Non-enumerable property fails to shadow inherited enumerable property from for-in + https://bugs.webkit.org/show_bug.cgi?id=38970 + + Reviewed by Keith Miller. + + * stress/arguments-bizarre-behaviour-disable-enumerability.js: + * stress/for-in-redefine-enumerable.js: Added. + * stress/for-in-shadow-non-enumerable.js: Added. + * test262/expectations.yaml: Mark 4 test cases as passing. + 2020-12-15 Yusuke Suzuki Suppress noise from JSTests/stress/lars-sab-workers.js diff --git a/JSTests/stress/arguments-bizarre-behaviour-disable-enumerability.js b/JSTests/stress/arguments-bizarre-behaviour-disable-enumerability.js index ab5735f3d221e..86903c4ac151e 100644 --- a/JSTests/stress/arguments-bizarre-behaviour-disable-enumerability.js +++ b/JSTests/stress/arguments-bizarre-behaviour-disable-enumerability.js @@ -18,7 +18,7 @@ var array = []; for (var s in result[2]) array.push(s); -if (array.join(",") != "0") +if (array.join(",") != "") throw new Error(); if (Object.keys(result[2]).join(",") != "0") diff --git a/JSTests/stress/for-in-redefine-enumerable.js b/JSTests/stress/for-in-redefine-enumerable.js new file mode 100644 index 0000000000000..d2c7849347e43 --- /dev/null +++ b/JSTests/stress/for-in-redefine-enumerable.js @@ -0,0 +1,107 @@ +function assert(x) { + if (!x) + throw new Error("Bad assertion!"); +} + +function shouldBe(actual, expected) { + if (actual !== expected) + throw new Error(`Bad value: ${actual}.`); +} + +const enumDesc = { value: 0, writable: true, enumerable: true, configurable: true }; +const dontEnumDesc = { value: 0, writable: true, enumerable: false, configurable: true }; + +// indexed property +(() => { + function test() { + var arr = Object.defineProperties([0, 0, 0], { 1: dontEnumDesc }); + for (var i in arr) { + assert(i in arr); + shouldBe(arr[i], 0); + ++arr[i]; + if (i === "0") + Object.defineProperties(arr, { 1: enumDesc, 2: dontEnumDesc }); + } + shouldBe(arr[0], 1); + shouldBe(arr[1], 0); + shouldBe(arr[2], 0); + } + + for (var i = 0; i < 1e5; ++i) + test(); +})(); + +// structure property +(() => { + function test() { + var obj = Object.create(null, { a: enumDesc, b: enumDesc, c: dontEnumDesc }); + for (var key in obj) { + assert(key in obj); + shouldBe(obj[key], 0); + ++obj[key]; + if (key === "a") + Object.defineProperties(obj, { b: dontEnumDesc, c: enumDesc }); + } + shouldBe(obj.a, 1); + shouldBe(obj.b, 0); + shouldBe(obj.c, 0); + } + + for (var i = 0; i < 1e5; ++i) + test(); +})(); + +// generic property (Proxy) +(() => { + function test() { + var target = { a: 0, b: 0, c: 0 }; + var enumMap = { a: true, b: true, c: false }; + var proxy = new Proxy(target, { + getOwnPropertyDescriptor: (_, key) => { + return { value: target[key], writable: true, enumerable: enumMap[key], configurable: true }; + }, + }); + + for (var key in proxy) { + assert(key in proxy); + shouldBe(proxy[key], 0); + ++target[key]; + if (key === "a") { + enumMap.b = false; + enumMap.c = true; + } + } + shouldBe(target.a, 1); + shouldBe(target.b, 0); + shouldBe(target.c, 0); + } + + for (var i = 0; i < 1e5; ++i) + test(); +})(); + +// generic property (in prototype) +(() => { + function test() { + var seen = {}; + var proto = Object.create(null, { b: enumDesc, c: dontEnumDesc, d: enumDesc, e: enumDesc }); + var heir = Object.create(proto, { a: enumDesc, e: dontEnumDesc }); + for (var key in heir) { + assert(key in heir); + shouldBe(heir[key], 0); + seen[key] = true; + if (key === "a") + Object.defineProperties(proto, { b: dontEnumDesc, c: enumDesc }); + if (key === "d") + Object.defineProperties(heir, { e: enumDesc }); + } + assert(seen.a); + assert(!seen.b); + assert(!seen.c); + assert(seen.d); + assert(seen.e); + } + + for (var i = 0; i < 1e5; ++i) + test(); +})(); diff --git a/JSTests/stress/for-in-shadow-non-enumerable.js b/JSTests/stress/for-in-shadow-non-enumerable.js new file mode 100644 index 0000000000000..d02dec0d989f7 --- /dev/null +++ b/JSTests/stress/for-in-shadow-non-enumerable.js @@ -0,0 +1,174 @@ +const dontEnumDesc = { value: 1, writable: true, enumerable: false, configurable: true }; +const testCases = [ + { + name: "Object", + createObject: () => Object.create(null, { foo: dontEnumDesc, bar: dontEnumDesc }), + dontEnumKeys: ["foo", "bar"], + }, + { + name: "Error", + createObject: () => new Error(), + dontEnumKeys: ["line", "column", "sourceURL", "stack"], + }, + { + name: "Array (empty)", + createObject: () => [], + dontEnumKeys: ["length"], + }, + { + name: "Array (sparse)", + createObject: () => Object.defineProperties([0, 1, 2], { 0: dontEnumDesc, 2: dontEnumDesc }), + dontEnumKeys: ["0", "2", "length"], + }, + { + name: "Function (strict)", + createObject: () => function() { "use strict"; }, + dontEnumKeys: ["length", "name", "prototype"], + }, + { + name: "Function (non-strict)", + createObject: () => function() {}, + dontEnumKeys: ["arguments", "caller", "length", "name", "prototype"], + }, + { + name: "RegExp", + createObject: () => /(?:)/g, + dontEnumKeys: ["lastIndex"], + }, + { + name: "String", + createObject: () => new String("foo"), + dontEnumKeys: ["length"], + }, + { + name: "Arguments (strict)", + createObject: function(foo) { "use strict"; return arguments; }, + dontEnumKeys: ["length", "callee"], + }, + { + name: "Arguments (non-strict)", + createObject: function(foo) { return arguments; }, + dontEnumKeys: ["length", "callee"], + }, + { + name: "Reflect", + createObject: () => $vm.createGlobalObject().Reflect, + dontEnumKeys: ["apply", "get", "has", "set"], + }, + { + name: "Date.prototype", + createObject: () => $vm.createGlobalObject().Date.prototype, + dontEnumKeys: ["toISOString", "getTime", "setYear"], + }, +]; + +// basic tests +for (const t of testCases) { + assert(!contains(forIn(t.createObject()), t.dontEnumKeys), t.name); + assert(!contains(Object.keys(t.createObject()), t.dontEnumKeys), t.name); + + assert(contains(Object.getOwnPropertyNames(t.createObject()), t.dontEnumKeys), t.name); + assert(contains(Reflect.ownKeys(t.createObject()), t.dontEnumKeys), t.name); +} + +// shadowing: DontEnum => Enum +for (const t of testCases) { + assert( + !contains( + forIn(makePrototypeChain(t.createObject(), makeObject(t.dontEnumKeys))), + t.dontEnumKeys, + ), + t.name, + ); +} + +// shadowing: {} => DontEnum => {} => Enum +for (const t of testCases) { + assert( + !contains( + forIn(makePrototypeChain({}, t.createObject(), {}, makeObject(t.dontEnumKeys))), + t.dontEnumKeys, + ), + t.name, + ); +} + +// shadowing: DontEnum => {} => Enum => {} => Enum +for (const t of testCases) { + assert( + !contains( + forIn(makePrototypeChain(t.createObject(), {}, makeObject(t.dontEnumKeys), {}, makeObject(t.dontEnumKeys))), + t.dontEnumKeys, + ), + t.name, + ); +} + +// shadowing: {} => DontEnum (enumerable: true => false) => {} => Enum +for (const t of testCases) { + const dontEnumObject = t.createObject(); + const target = makePrototypeChain({}, dontEnumObject, {}, makeObject(t.dontEnumKeys)); + assert(!contains(forIn(target), t.dontEnumKeys), t.name); + + const enumKeys = t.dontEnumKeys.filter(key => Reflect.defineProperty(dontEnumObject, key, { enumerable: true })); + assert(contains(forIn(target), enumKeys), t.name); +} + +// shadowing: {} => DontEnum (delete non-enumerable keys) => {} => Enum +for (const t of testCases) { + const dontEnumObject = t.createObject(); + const target = makePrototypeChain({}, dontEnumObject, {}, makeObject(t.dontEnumKeys)); + assert(!contains(forIn(target), t.dontEnumKeys), t.name); + + const enumKeys = t.dontEnumKeys.filter(key => Reflect.deleteProperty(dontEnumObject, key)); + assert(contains(forIn(target), enumKeys), t.name); +} + +// shadowing: {} => DontEnum (materialized) => {} => Enum +for (const t of testCases) { + const dontEnumObject = t.createObject(); + const target = makePrototypeChain({}, dontEnumObject, {}, makeObject(t.dontEnumKeys)); + assert(!contains(forIn(target), t.dontEnumKeys), t.name); + + const dontEnumKeys = t.dontEnumKeys.filter(key => { + const desc = Object.getOwnPropertyDescriptor(dontEnumObject, key); + if (!Reflect.deleteProperty(dontEnumObject, key)) return false; + Object.defineProperty(dontEnumObject, key, desc); + return true; + }); + + if (dontEnumKeys.length) + assert(!contains(forIn(target), dontEnumKeys), t.name); +} + +// helpers +function assert(value, name) { + if (!value) + throw new Error(`Bad value: ${value}. Test case: ${name}.`); +} + +function contains(array, subarray) { + return subarray.every(item => array.includes(item)); +} + +function forIn(object) { + const keys = []; + for (const key in object) + keys.push(key); + return keys; +} + +function makePrototypeChain(...objects) { + objects.reduce((object, prototype) => { + Object.setPrototypeOf(object, prototype); + return prototype; + }); + return objects[0]; +} + +function makeObject(keys) { + const object = {}; + for (const key of keys) + object[key] = key; + return object; +} diff --git a/JSTests/test262/expectations.yaml b/JSTests/test262/expectations.yaml index a344854220126..d13eaaed8b01c 100644 --- a/JSTests/test262/expectations.yaml +++ b/JSTests/test262/expectations.yaml @@ -1858,16 +1858,10 @@ test/language/statements/for-await-of/ticks-with-async-iter-resolved-promise-and test/language/statements/for-await-of/ticks-with-sync-iter-resolved-promise-and-constructor-lookup.js: default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Expected [pre, tick 1, constructor, constructor, tick 2, tick 3, loop, tick 4, constructor] and [pre, constructor, constructor, tick 1, tick 2, loop, constructor, tick 3, tick 4, post] to have the same contents. Ticks and constructor lookups' strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Expected [pre, tick 1, constructor, constructor, tick 2, tick 3, loop, tick 4, constructor] and [pre, constructor, constructor, tick 1, tick 2, loop, constructor, tick 3, tick 4, post] to have the same contents. Ticks and constructor lookups' -test/language/statements/for-in/12.6.4-2.js: - default: 'Test262Error: accessedProp Expected SameValue(«true», «false») to be true' - strict mode: 'Test262Error: accessedProp Expected SameValue(«true», «false») to be true' test/language/statements/for-in/head-lhs-let.js: default: "SyntaxError: Cannot use the keyword 'in' as a lexical variable name." test/language/statements/for-in/identifier-let-allowed-as-lefthandside-expression-not-strict.js: default: "SyntaxError: Cannot use the keyword 'in' as a lexical variable name." -test/language/statements/for-in/order-enumerable-shadowed.js: - default: 'Test262Error: Expected [p1, p2] and [p1] to have the same contents. ' - strict mode: 'Test262Error: Expected [p1, p2] and [p1] to have the same contents. ' test/language/statements/for-in/scope-body-lex-open.js: default: 'Test262Error: Expected a ReferenceError to be thrown but no exception was thrown at all' strict mode: 'Test262Error: Expected a ReferenceError to be thrown but no exception was thrown at all' diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog index d984e032fc1c8..82aa35cfc5395 100644 --- a/LayoutTests/ChangeLog +++ b/LayoutTests/ChangeLog @@ -1,3 +1,13 @@ +2020-12-15 Alexey Shvayka + + Non-enumerable property fails to shadow inherited enumerable property from for-in + https://bugs.webkit.org/show_bug.cgi?id=38970 + + Reviewed by Keith Miller. + + * platform/mac/fast/dom/wrapper-classes-objc-expected.txt: + * platform/mac/fast/dom/wrapper-classes-objc.html: + 2020-12-15 Alex Christensen REGRESSION: [macOS] http/tests/inspector/network/resource-response-service-worker.html is a flaky failure diff --git a/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc-expected.txt b/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc-expected.txt index 78706755b9d46..2e40cca73dc92 100644 --- a/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc-expected.txt +++ b/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc-expected.txt @@ -189,6 +189,7 @@ PASS typeof objCObjectOfClass('NSCFString') is 'string' PASS typeof objCObjectOfClass('WebScriptObject') is 'object' PASS objCObjectOfClass('NSArray') instanceof Array is true PASS concatenateArray(objCArrayOfString()) is 'onetwothree' +PASS objCArrayOfString().every((_, i, arr) => arr.propertyIsEnumerable(i)) is true PASS let arr = objCArrayOfString(); arr.length is 3 PASS let arr = objCArrayOfString(); arr.length = 0 threw exception RangeError: Range error. PASS let arr = objCArrayOfString(); arr.length = 5 threw exception RangeError: Range error. diff --git a/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc.html b/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc.html index 27bc10c416872..dcfa6aa2cb30c 100644 --- a/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc.html +++ b/LayoutTests/platform/mac/fast/dom/wrapper-classes-objc.html @@ -286,6 +286,7 @@ shouldBeTrue("objCObjectOfClass('NSArray') instanceof Array"); shouldBe("concatenateArray(objCArrayOfString())", "'onetwothree'"); + shouldBeTrue("objCArrayOfString().every((_, i, arr) => arr.propertyIsEnumerable(i))"); shouldBe("let arr = objCArrayOfString(); arr.length", "3"); shouldThrow("let arr = objCArrayOfString(); arr.length = 0"); diff --git a/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h b/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h index e4b1304d0bbed..3cbe76eca8c03 100644 --- a/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h +++ b/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h @@ -162,6 +162,9 @@ bool JSCallbackObject::getOwnPropertySlot(JSObject* object, JSGlobalObje RefPtr propertyNameRef; if (StringImpl* name = propertyName.uid()) { + // FIXME: Set ReadOnly conditionally, based on setProperty presence in class inheritance chain. + // https://bugs.webkit.org/show_bug.cgi?id=219924 + unsigned attributes = static_cast(PropertyAttribute::ReadOnly); for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { // optional optimization to bypass getProperty in cases when we only need to know if the property exists if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) { @@ -169,7 +172,7 @@ bool JSCallbackObject::getOwnPropertySlot(JSObject* object, JSGlobalObje propertyNameRef = OpaqueJSString::tryCreate(name); JSLock::DropAllLocks dropAllLocks(globalObject); if (hasProperty(ctx, thisRef, propertyNameRef.get())) { - slot.setCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, getCallbackGetter()); + slot.setCustom(thisObject, attributes, getCallbackGetter()); return true; } } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { @@ -183,29 +186,31 @@ bool JSCallbackObject::getOwnPropertySlot(JSObject* object, JSGlobalObje } if (exception) { throwException(globalObject, scope, toJS(globalObject, exception)); - slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, jsUndefined()); + slot.setValue(thisObject, attributes, jsUndefined()); return true; } if (value) { - slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, toJS(globalObject, value)); + slot.setValue(thisObject, attributes, toJS(globalObject, value)); return true; } } if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) { - if (staticValues->contains(name)) { + if (StaticValueEntry* entry = staticValues->get(name)) { + // FIXME: getStaticValue() performs the same loop & checks just to acquire `entry`. + // https://bugs.webkit.org/show_bug.cgi?id=219925 JSValue value = thisObject->getStaticValue(globalObject, propertyName); RETURN_IF_EXCEPTION(scope, false); if (value) { - slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, value); + slot.setValue(thisObject, entry->attributes, value); return true; } } } if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) { - if (staticFunctions->contains(name)) { - slot.setCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, getStaticFunctionGetter()); + if (StaticFunctionEntry* entry = staticFunctions->get(name)) { + slot.setCustom(thisObject, entry->attributes, getStaticFunctionGetter()); return true; } } diff --git a/Source/JavaScriptCore/API/tests/testapi.c b/Source/JavaScriptCore/API/tests/testapi.c index 103896eb1d9c4..b77f811937ff3 100644 --- a/Source/JavaScriptCore/API/tests/testapi.c +++ b/Source/JavaScriptCore/API/tests/testapi.c @@ -1017,12 +1017,14 @@ static JSValueRef functionGC(JSContextRef context, JSObjectRef function, JSObjec static JSStaticValue globalObject_staticValues[] = { { "globalStaticValue", globalObject_get, globalObject_set, kJSPropertyAttributeNone }, + { "globalStaticValue2", globalObject_get, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum }, { 0, 0, 0, 0 } }; static JSStaticFunction globalObject_staticFunctions[] = { { "globalStaticFunction", globalObject_call, kJSPropertyAttributeNone }, { "globalStaticFunction2", globalObject_call, kJSPropertyAttributeNone }, + { "globalStaticFunction3", globalObject_call, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum }, { "gc", functionGC, kJSPropertyAttributeNone }, { 0, 0, 0 } }; diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/testapi.js b/Source/JavaScriptCore/API/tests/testapiScripts/testapi.js index 483290b092c77..9d776948b88e1 100644 --- a/Source/JavaScriptCore/API/tests/testapiScripts/testapi.js +++ b/Source/JavaScriptCore/API/tests/testapiScripts/testapi.js @@ -84,6 +84,16 @@ this.globalStaticFunction2 = function() { return 20; } shouldBe("globalStaticFunction2();", 20); shouldBe("this.globalStaticFunction2();", 20); +var globalStaticValue2Descriptor = Object.getOwnPropertyDescriptor(this, "globalStaticValue2"); +shouldBe('typeof globalStaticValue2Descriptor', "object"); +shouldBe('globalStaticValue2Descriptor.writable', false); +shouldBe('globalStaticValue2Descriptor.enumerable', false); + +var globalStaticFunction3Descriptor = Object.getOwnPropertyDescriptor(this, "globalStaticFunction3"); +shouldBe('typeof globalStaticFunction3Descriptor', "object"); +shouldBe('globalStaticFunction3Descriptor.writable', false); +shouldBe('globalStaticFunction3Descriptor.enumerable', false); + function iAmNotAStaticFunction() { return 10; } shouldBe("iAmNotAStaticFunction();", 10); this.iAmNotAStaticFunction = function() { return 20; } @@ -134,12 +144,12 @@ var alwaysOneDescriptor = Object.getOwnPropertyDescriptor(MyObject, "alwaysOne") shouldBe('typeof alwaysOneDescriptor', "object"); shouldBe('alwaysOneDescriptor.value', MyObject.alwaysOne); shouldBe('alwaysOneDescriptor.configurable', true); -shouldBe('alwaysOneDescriptor.enumerable', false); // Actually it is. +shouldBe('alwaysOneDescriptor.enumerable', true); var cantFindDescriptor = Object.getOwnPropertyDescriptor(MyObject, "cantFind"); shouldBe('typeof cantFindDescriptor', "object"); shouldBe('cantFindDescriptor.value', MyObject.cantFind); shouldBe('cantFindDescriptor.configurable', true); -shouldBe('cantFindDescriptor.enumerable', false); +shouldBe('cantFindDescriptor.enumerable', true); try { // If getOwnPropertyDescriptor() returned an access descriptor, this wouldn't throw. Object.getOwnPropertyDescriptor(MyObject, "throwOnGet"); @@ -150,7 +160,7 @@ var myPropertyNameDescriptor = Object.getOwnPropertyDescriptor(MyObject, "myProp shouldBe('typeof myPropertyNameDescriptor', "object"); shouldBe('myPropertyNameDescriptor.value', MyObject.myPropertyName); shouldBe('myPropertyNameDescriptor.configurable', true); -shouldBe('myPropertyNameDescriptor.enumerable', false); // Actually it is. +shouldBe('myPropertyNameDescriptor.enumerable', true); try { // if getOwnPropertyDescriptor() returned an access descriptor, this wouldn't throw. Object.getOwnPropertyDescriptor(MyObject, "hasPropertyLie"); @@ -237,23 +247,23 @@ var baseDupDescriptor = Object.getOwnPropertyDescriptor(derived, "baseDup"); shouldBe('typeof baseDupDescriptor', "object"); shouldBe('baseDupDescriptor.value', derived.baseDup); shouldBe('baseDupDescriptor.configurable', true); -shouldBe('baseDupDescriptor.enumerable', false); +shouldBe('baseDupDescriptor.enumerable', true); var baseOnlyDescriptor = Object.getOwnPropertyDescriptor(derived, "baseOnly"); shouldBe('typeof baseOnlyDescriptor', "object"); shouldBe('baseOnlyDescriptor.value', derived.baseOnly); shouldBe('baseOnlyDescriptor.configurable', true); -shouldBe('baseOnlyDescriptor.enumerable', false); +shouldBe('baseOnlyDescriptor.enumerable', true); shouldBe('Object.getOwnPropertyDescriptor(derived, "protoOnly")', undefined); var protoDupDescriptor = Object.getOwnPropertyDescriptor(derived, "protoDup"); shouldBe('typeof protoDupDescriptor', "object"); shouldBe('protoDupDescriptor.value', derived.protoDup); shouldBe('protoDupDescriptor.configurable', true); -shouldBe('protoDupDescriptor.enumerable', false); +shouldBe('protoDupDescriptor.enumerable', true); var derivedOnlyDescriptor = Object.getOwnPropertyDescriptor(derived, "derivedOnly"); shouldBe('typeof derivedOnlyDescriptor', "object"); shouldBe('derivedOnlyDescriptor.value', derived.derivedOnly); shouldBe('derivedOnlyDescriptor.configurable', true); -shouldBe('derivedOnlyDescriptor.enumerable', false); +shouldBe('derivedOnlyDescriptor.enumerable', true); shouldBe("undefined instanceof MyObject", false); EvilExceptionObject.hasInstance = function f() { return f(); }; diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog index 3e1fa01a726b4..eaa31d5876f4e 100644 --- a/Source/JavaScriptCore/ChangeLog +++ b/Source/JavaScriptCore/ChangeLog @@ -1,3 +1,142 @@ +2020-12-15 Alexey Shvayka + + Non-enumerable property fails to shadow inherited enumerable property from for-in + https://bugs.webkit.org/show_bug.cgi?id=38970 + + Reviewed by Keith Miller. + + While for/in was initially specified with notion of "shadowing", it wasn't clarified + until ES5 that [[Enumerable]] attributes are ignored when determining if a property + has already been processed. Recently, for/in spec was expanded [1] to pin down common + case enumeration as it's currently implemented by V8 and SpiderMonkey. + + Since keeping track of DontEnum properties is a massive slowdown for uncached runs + (with any data structure used), this patch simply adds [[Enumerable]] check to + has_{indexed,structure,generic}_property bytecode ops and does renaming chores. + + Common code is now shared between HasIndexedProperty (emitted for `0 in arr`) and + HasEnumerableIndexedProperty DFG nodes via passing different slow path ops rather + than having OpInfo with PropertySlot::InternalMethodType, which is a nice refactor. + + While this change aligns common case for/in enumeration with the spec and other + engines, it also introduces a few observable discrepancies from V8 and SpiderMonkey, + which are permitted by the spec [2]: + a) properties that have been redefined as DontEnum within loop body are skipped, + which matches the spec [3] and seems like expected behavior; + b) "shadowing" is broken if a DontEnum property of already visited object is + added / deleted / redefined within loop body, which (pretty much) never happens. + + This patch introduces a new invariant: all properties getOwn*PropertyNames() returns + in DontEnumPropertiesMode::Exclude should be reported as [[Enumerable]] by + getOwnPropertySlot(). JSCallbackObject and RuntimeArray are fixed to follow it. + + for/in and Object.keys microbenchmarks are neutral. This change does not affect + JSPropertyNameEnumerator caching, nor fast paths of its bytecodes. + + [1]: https://github.com/tc39/ecma262/pull/1791 + [2]: https://tc39.es/ecma262/#sec-enumerate-object-properties (last paragraph) + [3]: https://tc39.es/ecma262/#sec-%foriniteratorprototype%.next (step 7.b.iii) + + * API/JSCallbackObjectFunctions.h: + (JSC::JSCallbackObject::getOwnPropertySlot): + * API/tests/testapi.c: + * API/tests/testapiScripts/testapi.js: + * bytecode/BytecodeList.rb: + * bytecode/BytecodeUseDef.cpp: + (JSC::computeUsesForBytecodeIndexImpl): + (JSC::computeDefsForBytecodeIndexImpl): + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::finishCreation): + * bytecode/Opcode.h: + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::emitHasEnumerableIndexedProperty): + (JSC::BytecodeGenerator::emitHasEnumerableStructureProperty): + (JSC::BytecodeGenerator::emitHasEnumerableProperty): + (JSC::BytecodeGenerator::emitHasGenericProperty): Deleted. + (JSC::BytecodeGenerator::emitHasIndexedProperty): Deleted. + (JSC::BytecodeGenerator::emitHasStructureProperty): Deleted. + * bytecompiler/BytecodeGenerator.h: + * bytecompiler/NodesCodegen.cpp: + (JSC::ForInNode::emitBytecode): + * dfg/DFGAbstractInterpreterInlines.h: + (JSC::DFG::AbstractInterpreter::executeEffects): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::parseBlock): + * dfg/DFGCapabilities.cpp: + (JSC::DFG::capabilityLevel): + * dfg/DFGClobberize.h: + (JSC::DFG::clobberize): + * dfg/DFGDoesGC.cpp: + (JSC::DFG::doesGC): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + (JSC::DFG::FixupPhase::convertToHasIndexedProperty): + * dfg/DFGNode.h: + (JSC::DFG::Node::hasArrayMode): + (JSC::DFG::Node::hasInternalMethodType const): Deleted. + (JSC::DFG::Node::internalMethodType const): Deleted. + (JSC::DFG::Node::setInternalMethodType): Deleted. + * dfg/DFGNodeType.h: + * dfg/DFGOperations.cpp: + (JSC::DFG::JSC_DEFINE_JIT_OPERATION): + * dfg/DFGOperations.h: + * dfg/DFGPredictionPropagationPhase.cpp: + * dfg/DFGSSALoweringPhase.cpp: + (JSC::DFG::SSALoweringPhase::handleNode): + * dfg/DFGSafeToExecute.h: + (JSC::DFG::safeToExecute): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::compileHasEnumerableProperty): + (JSC::DFG::SpeculativeJIT::compileHasEnumerableStructureProperty): + (JSC::DFG::SpeculativeJIT::compileHasIndexedProperty): + (JSC::DFG::SpeculativeJIT::compileHasGenericProperty): Deleted. + (JSC::DFG::SpeculativeJIT::compileHasStructureProperty): Deleted. + * dfg/DFGSpeculativeJIT.h: + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * ftl/FTLCapabilities.cpp: + (JSC::FTL::canCompile): + * ftl/FTLLowerDFGToB3.cpp: + (JSC::FTL::DFG::LowerDFGToB3::compileNode): + (JSC::FTL::DFG::LowerDFGToB3::compileHasIndexedProperty): + (JSC::FTL::DFG::LowerDFGToB3::compileHasEnumerableProperty): + (JSC::FTL::DFG::LowerDFGToB3::compileHasEnumerableStructureProperty): + (JSC::FTL::DFG::LowerDFGToB3::compileHasGenericProperty): Deleted. + (JSC::FTL::DFG::LowerDFGToB3::compileHasStructureProperty): Deleted. + * jit/JIT.cpp: + (JSC::JIT::privateCompileMainPass): + (JSC::JIT::privateCompileSlowCases): + * jit/JIT.h: + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_has_enumerable_structure_property): + (JSC::JIT::emit_op_has_enumerable_indexed_property): + (JSC::JIT::emitSlow_op_has_enumerable_indexed_property): + (JSC::JIT::emit_op_has_structure_property): Deleted. + (JSC::JIT::emit_op_has_indexed_property): Deleted. + (JSC::JIT::emitSlow_op_has_indexed_property): Deleted. + * jit/JITOpcodes32_64.cpp: + (JSC::JIT::emit_op_has_enumerable_structure_property): + (JSC::JIT::emit_op_has_enumerable_indexed_property): + (JSC::JIT::emitSlow_op_has_enumerable_indexed_property): + (JSC::JIT::emit_op_has_structure_property): Deleted. + (JSC::JIT::emit_op_has_indexed_property): Deleted. + (JSC::JIT::emitSlow_op_has_indexed_property): Deleted. + * jit/JITOperations.cpp: + (JSC::JSC_DEFINE_JIT_OPERATION): + * jit/JITOperations.h: + * llint/LowLevelInterpreter.asm: + * llint/LowLevelInterpreter64.asm: + * runtime/CommonSlowPaths.cpp: + (JSC::JSC_DEFINE_COMMON_SLOW_PATH): + * runtime/CommonSlowPaths.h: + * runtime/JSObject.cpp: + (JSC::JSObject::hasProperty const): + (JSC::JSObject::hasEnumerableProperty const): + (JSC::JSObject::hasPropertyGeneric const): Deleted. + * runtime/JSObject.h: + 2020-12-15 Saam Barati Switch to using a linked list for the TDZ environment instead of a Vector diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.rb b/Source/JavaScriptCore/bytecode/BytecodeList.rb index 186daa1dd3c5b..61619b541f9b9 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeList.rb +++ b/Source/JavaScriptCore/bytecode/BytecodeList.rb @@ -1125,7 +1125,7 @@ base: VirtualRegister, } -op :has_indexed_property, +op :has_enumerable_indexed_property, args: { dst: VirtualRegister, base: VirtualRegister, @@ -1135,7 +1135,7 @@ arrayProfile: ArrayProfile, } -op :has_structure_property, +op :has_enumerable_structure_property, args: { dst: VirtualRegister, base: VirtualRegister, @@ -1159,7 +1159,7 @@ enumerator: VirtualRegister, } -op :has_generic_property, +op :has_enumerable_property, args: { dst: VirtualRegister, base: VirtualRegister, diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp b/Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp index 460e973016693..b2698d878e4a9 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp +++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp @@ -221,8 +221,9 @@ void computeUsesForBytecodeIndexImpl(VirtualRegister scopeRegister, const Instru USES(OpGetFromArguments, arguments) USES(OpNewArrayBuffer, immutableButterfly) - USES(OpHasGenericProperty, base, property) - USES(OpHasIndexedProperty, base, property) + USES(OpHasEnumerableIndexedProperty, base, property) + USES(OpHasEnumerableStructureProperty, base, property, enumerator) + USES(OpHasEnumerableProperty, base, property) USES(OpEnumeratorStructurePname, enumerator, index) USES(OpEnumeratorGenericPname, enumerator, index) USES(OpGetByVal, base, property) @@ -260,7 +261,6 @@ void computeUsesForBytecodeIndexImpl(VirtualRegister scopeRegister, const Instru USES(OpGetByValWithThis, base, thisValue, property) USES(OpInstanceofCustom, value, constructor, hasInstanceValue) - USES(OpHasStructureProperty, base, property, enumerator) USES(OpHasOwnStructureProperty, base, property, enumerator) USES(OpInStructureProperty, base, property, enumerator) @@ -420,11 +420,11 @@ void computeDefsForBytecodeIndexImpl(unsigned numVars, const Instruction* instru DEFS(OpArgumentCount, dst) DEFS(OpToIndexString, dst) DEFS(OpGetEnumerableLength, dst) - DEFS(OpHasIndexedProperty, dst) - DEFS(OpHasStructureProperty, dst) + DEFS(OpHasEnumerableIndexedProperty, dst) + DEFS(OpHasEnumerableStructureProperty, dst) + DEFS(OpHasEnumerableProperty, dst) DEFS(OpHasOwnStructureProperty, dst) DEFS(OpInStructureProperty, dst) - DEFS(OpHasGenericProperty, dst) DEFS(OpGetDirectPname, dst) DEFS(OpGetPropertyEnumerator, dst) DEFS(OpEnumeratorStructurePname, dst) diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp index 91755ede6ac8f..af2ac4c91bb72 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp @@ -503,7 +503,7 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink OpcodeID opcodeID = instruction->opcodeID(); m_bytecodeCost += opcodeLengths[opcodeID]; switch (opcodeID) { - LINK(OpHasIndexedProperty) + LINK(OpHasEnumerableIndexedProperty) LINK(OpCallVarargs, profile) LINK(OpTailCallVarargs, profile) diff --git a/Source/JavaScriptCore/bytecode/Opcode.h b/Source/JavaScriptCore/bytecode/Opcode.h index a90dc4364c90d..fd7f89a58d8fe 100644 --- a/Source/JavaScriptCore/bytecode/Opcode.h +++ b/Source/JavaScriptCore/bytecode/Opcode.h @@ -126,7 +126,7 @@ static constexpr unsigned bitWidthForMaxOpcodeLength = WTF::getMSBSetConstexpr(m macro(OpRshift) \ #define FOR_EACH_OPCODE_WITH_ARRAY_PROFILE(macro) \ - macro(OpHasIndexedProperty) \ + macro(OpHasEnumerableIndexedProperty) \ macro(OpCallVarargs) \ macro(OpTailCallVarargs) \ macro(OpTailCallForwardArguments) \ diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp index 6efdf3e1a775e..617ca0cbc55bc 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -4365,21 +4365,21 @@ RegisterID* BytecodeGenerator::emitGetEnumerableLength(RegisterID* dst, Register return dst; } -RegisterID* BytecodeGenerator::emitHasGenericProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName) +RegisterID* BytecodeGenerator::emitHasEnumerableIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName) { - OpHasGenericProperty::emit(this, dst, base, propertyName); + OpHasEnumerableIndexedProperty::emit(this, dst, base, propertyName); return dst; } -RegisterID* BytecodeGenerator::emitHasIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName) +RegisterID* BytecodeGenerator::emitHasEnumerableStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator) { - OpHasIndexedProperty::emit(this, dst, base, propertyName); + OpHasEnumerableStructureProperty::emit(this, dst, base, propertyName, enumerator); return dst; } -RegisterID* BytecodeGenerator::emitHasStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator) +RegisterID* BytecodeGenerator::emitHasEnumerableProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName) { - OpHasStructureProperty::emit(this, dst, base, propertyName, enumerator); + OpHasEnumerableProperty::emit(this, dst, base, propertyName); return dst; } diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h index 2bf9b0da18b2a..50b5465f9b75b 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h @@ -902,10 +902,10 @@ namespace JSC { void emitEnter(); void emitCheckTraps(); - RegisterID* emitHasIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); - RegisterID* emitHasStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator); + RegisterID* emitHasEnumerableIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); + RegisterID* emitHasEnumerableStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator); + RegisterID* emitHasEnumerableProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); RegisterID* emitHasOwnStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator); - RegisterID* emitHasGenericProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); RegisterID* emitGetPropertyEnumerator(RegisterID* dst, RegisterID* base); RegisterID* emitGetEnumerableLength(RegisterID* dst, RegisterID* base); RegisterID* emitGetStructurePropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length); diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp index eb4651065a2dc..d4a89b44d367b 100644 --- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp +++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp @@ -3962,7 +3962,7 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) RefPtr result = generator.emitEqualityOp(generator.newTemporary(), i.get(), length.get()); generator.emitJumpIfFalse(result.get(), loopEnd.get()); - generator.emitHasIndexedProperty(result.get(), base.get(), i.get()); + generator.emitHasEnumerableIndexedProperty(result.get(), base.get(), i.get()); generator.emitJumpIfFalse(result.get(), *scope->continueTarget()); generator.emitToIndexString(propertyName.get(), i.get()); @@ -4003,7 +4003,7 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) RefPtr result = generator.emitIsNull(generator.newTemporary(), propertyName.get()); generator.emitJumpIfTrue(result.get(), loopEnd.get()); - generator.emitHasStructureProperty(result.get(), base.get(), propertyName.get(), enumerator.get()); + generator.emitHasEnumerableStructureProperty(result.get(), base.get(), propertyName.get(), enumerator.get()); generator.emitJumpIfFalse(result.get(), *scope->continueTarget()); this->emitLoopHeader(generator, propertyName.get()); @@ -4045,7 +4045,7 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) RefPtr result = generator.emitIsNull(generator.newTemporary(), propertyName.get()); generator.emitJumpIfTrue(result.get(), loopEnd.get()); - generator.emitHasGenericProperty(result.get(), base.get(), propertyName.get()); + generator.emitHasEnumerableProperty(result.get(), base.get(), propertyName.get()); generator.emitJumpIfFalse(result.get(), *scope->continueTarget()); this->emitLoopHeader(generator, propertyName.get()); diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h index e390eb61d53c8..73654ba48d808 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h @@ -4155,19 +4155,20 @@ bool AbstractInterpreter::executeEffects(unsigned clobberLimi setNonCellTypeForNode(node, SpecInt32Only); break; } - case HasGenericProperty: { + case HasEnumerableProperty: { setNonCellTypeForNode(node, SpecBoolean); clobberWorld(); break; } case InStructureProperty: case HasOwnStructureProperty: - case HasStructureProperty: { + case HasEnumerableStructureProperty: { setNonCellTypeForNode(node, SpecBoolean); clobberWorld(); break; } - case HasIndexedProperty: { + case HasIndexedProperty: + case HasEnumerableIndexedProperty: { ArrayMode mode = node->arrayMode(); switch (mode.type()) { case Array::Int32: diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 29e7c169d6d1c..9530ce1c80e21 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -8139,19 +8139,19 @@ void ByteCodeParser::parseBlock(unsigned limit) NEXT_OPCODE(op_get_enumerable_length); } - case op_has_generic_property: { - auto bytecode = currentInstruction->as(); - set(bytecode.m_dst, addToGraph(HasGenericProperty, get(bytecode.m_base), get(bytecode.m_property))); - NEXT_OPCODE(op_has_generic_property); - } - - case op_has_structure_property: { - auto bytecode = currentInstruction->as(); - set(bytecode.m_dst, addToGraph(HasStructureProperty, + case op_has_enumerable_structure_property: { + auto bytecode = currentInstruction->as(); + set(bytecode.m_dst, addToGraph(HasEnumerableStructureProperty, get(bytecode.m_base), get(bytecode.m_property), get(bytecode.m_enumerator))); - NEXT_OPCODE(op_has_structure_property); + NEXT_OPCODE(op_has_enumerable_structure_property); + } + + case op_has_enumerable_property: { + auto bytecode = currentInstruction->as(); + set(bytecode.m_dst, addToGraph(HasEnumerableProperty, get(bytecode.m_base), get(bytecode.m_property))); + NEXT_OPCODE(op_has_enumerable_property); } case op_has_own_structure_property: { @@ -8172,18 +8172,18 @@ void ByteCodeParser::parseBlock(unsigned limit) NEXT_OPCODE(op_in_structure_property); } - case op_has_indexed_property: { - auto bytecode = currentInstruction->as(); + case op_has_enumerable_indexed_property: { + auto bytecode = currentInstruction->as(); Node* base = get(bytecode.m_base); ArrayMode arrayMode = getArrayMode(bytecode.metadata(codeBlock).m_arrayProfile, Array::Read); Node* property = get(bytecode.m_property); addVarArgChild(base); addVarArgChild(property); addVarArgChild(nullptr); - Node* hasIterableProperty = addToGraph(Node::VarArg, HasIndexedProperty, OpInfo(arrayMode.asWord()), OpInfo(static_cast(PropertySlot::InternalMethodType::GetOwnProperty))); + Node* hasIterableProperty = addToGraph(Node::VarArg, HasEnumerableIndexedProperty, OpInfo(arrayMode.asWord())); m_exitOK = false; // HasIndexedProperty must be treated as if it clobbers exit state, since FixupPhase may make it generic. set(bytecode.m_dst, hasIterableProperty); - NEXT_OPCODE(op_has_indexed_property); + NEXT_OPCODE(op_has_enumerable_indexed_property); } case op_get_direct_pname: { diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp index b06150fccf4c7..10b85ad915a9b 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp @@ -253,11 +253,11 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I case op_get_scope: case op_get_from_scope: case op_get_enumerable_length: - case op_has_generic_property: - case op_has_structure_property: + case op_has_enumerable_indexed_property: + case op_has_enumerable_structure_property: + case op_has_enumerable_property: case op_has_own_structure_property: case op_in_structure_property: - case op_has_indexed_property: case op_get_direct_pname: case op_get_property_enumerator: case op_enumerator_structure_pname: diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h index f09dfcd4e5408..54e35e7c568a5 100644 --- a/Source/JavaScriptCore/dfg/DFGClobberize.h +++ b/Source/JavaScriptCore/dfg/DFGClobberize.h @@ -164,6 +164,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu case ArrayPop: case ArrayIndexOf: case HasIndexedProperty: + case HasEnumerableIndexedProperty: case AtomicsAdd: case AtomicsAnd: case AtomicsCompareExchange: @@ -348,7 +349,8 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu return; } - case HasIndexedProperty: { + case HasIndexedProperty: + case HasEnumerableIndexedProperty: { read(JSObject_butterfly); ArrayMode mode = node->arrayMode(); switch (mode.type()) { @@ -705,8 +707,8 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu case ResolveScopeForHoistingFuncDeclInEval: case ResolveScope: case ToObject: - case HasGenericProperty: - case HasStructureProperty: + case HasEnumerableStructureProperty: + case HasEnumerableProperty: case HasOwnStructureProperty: case InStructureProperty: case GetPropertyEnumerator: diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp index 2c3e01f06e745..e993fb061ab39 100644 --- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp +++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp @@ -295,10 +295,11 @@ bool doesGC(Graph& graph, Node* node) case GetDirectPname: case GetDynamicVar: case GetMapBucket: - case HasGenericProperty: case HasIndexedProperty: + case HasEnumerableIndexedProperty: + case HasEnumerableStructureProperty: + case HasEnumerableProperty: case HasOwnProperty: - case HasStructureProperty: case HasOwnStructureProperty: case InStructureProperty: case InById: diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 74eee12034909..2bfc2102b4740 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -2204,11 +2204,11 @@ class FixupPhase : public Phase { fixEdge(node->child1()); break; } - case HasGenericProperty: { + case HasEnumerableProperty: { fixEdge(node->child2()); break; } - case HasStructureProperty: { + case HasEnumerableStructureProperty: { fixEdge(node->child2()); fixEdge(node->child3()); break; @@ -2220,7 +2220,8 @@ class FixupPhase : public Phase { fixEdge(node->child3()); break; } - case HasIndexedProperty: { + case HasIndexedProperty: + case HasEnumerableIndexedProperty: { node->setArrayMode( node->arrayMode().refine( m_graph, node, @@ -3988,7 +3989,6 @@ class FixupPhase : public Phase { m_graph.varArgChild(node, 0)->prediction(), m_graph.varArgChild(node, 1)->prediction(), SpecNone)); - node->setInternalMethodType(PropertySlot::InternalMethodType::HasProperty); blessArrayOperation(m_graph.varArgChild(node, 0), m_graph.varArgChild(node, 1), m_graph.varArgChild(node, 2)); auto arrayMode = node->arrayMode(); diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index 7e9c6adab2156..2604c7e7ad934 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -2214,6 +2214,7 @@ struct Node { case ArrayPop: case ArrayIndexOf: case HasIndexedProperty: + case HasEnumerableIndexedProperty: case AtomicsAdd: case AtomicsAnd: case AtomicsCompareExchange: @@ -3003,23 +3004,6 @@ struct Node { return nullptr; } - bool hasInternalMethodType() const - { - return op() == HasIndexedProperty; - } - - PropertySlot::InternalMethodType internalMethodType() const - { - ASSERT(hasInternalMethodType()); - return static_cast(m_opInfo2.as()); - } - - void setInternalMethodType(PropertySlot::InternalMethodType type) - { - ASSERT(hasInternalMethodType()); - m_opInfo2 = static_cast(type); - } - Node* replacement() const { return m_misc.replacement; diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 783ac8ae7403d..6496c178041c9 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -498,10 +498,11 @@ namespace JSC { namespace DFG { macro(GetEnumerableLength, NodeMustGenerate | NodeResultJS) \ /* Must generate because of Proxies on the prototype chain */ \ macro(HasIndexedProperty, NodeMustGenerate | NodeResultBoolean | NodeHasVarArgs) \ - macro(HasStructureProperty, NodeResultBoolean) \ + macro(HasEnumerableIndexedProperty, NodeMustGenerate | NodeResultBoolean | NodeHasVarArgs) \ + macro(HasEnumerableStructureProperty, NodeResultBoolean) \ + macro(HasEnumerableProperty, NodeResultBoolean) \ macro(HasOwnStructureProperty, NodeResultBoolean | NodeMustGenerate) \ macro(InStructureProperty, NodeMustGenerate | NodeResultBoolean) \ - macro(HasGenericProperty, NodeResultBoolean) \ macro(GetDirectPname, NodeMustGenerate | NodeHasVarArgs | NodeResultJS) \ macro(GetPropertyEnumerator, NodeMustGenerate | NodeResultJS) \ macro(GetEnumeratorStructurePname, NodeMustGenerate | NodeResultJS) \ diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index ed98ad312994f..4d2c19d3bdeed 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -2258,7 +2258,7 @@ JSC_DEFINE_JIT_OPERATION(operationEnsureArrayStorage, char*, (VM* vmPointer, JSC return result; } -JSC_DEFINE_JIT_OPERATION(operationHasGenericProperty, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, JSCell* property)) +JSC_DEFINE_JIT_OPERATION(operationHasEnumerableProperty, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, JSCell* property)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); @@ -2275,7 +2275,7 @@ JSC_DEFINE_JIT_OPERATION(operationHasGenericProperty, EncodedJSValue, (JSGlobalO return JSValue::encode(JSValue()); auto propertyName = asString(property)->toIdentifier(globalObject); RETURN_IF_EXCEPTION(scope, { }); - RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(base->hasPropertyGeneric(globalObject, propertyName, PropertySlot::InternalMethodType::GetOwnProperty)))); + RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(base->hasEnumerableProperty(globalObject, propertyName)))); } JSC_DEFINE_JIT_OPERATION(operationInStructureProperty, EncodedJSValue, (JSGlobalObject* globalObject, JSCell* base, JSString* property)) @@ -2300,7 +2300,7 @@ JSC_DEFINE_JIT_OPERATION(operationHasOwnStructureProperty, EncodedJSValue, (JSGl return JSValue::encode(jsBoolean(objectPrototypeHasOwnProperty(globalObject, base, propertyName))); } -JSC_DEFINE_JIT_OPERATION(operationHasIndexedPropertyByInt, size_t, (JSGlobalObject* globalObject, JSCell* baseCell, int32_t subscript, int32_t internalMethodType)) +JSC_DEFINE_JIT_OPERATION(operationHasIndexedProperty, size_t, (JSGlobalObject* globalObject, JSCell* baseCell, int32_t subscript)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); @@ -2308,9 +2308,22 @@ JSC_DEFINE_JIT_OPERATION(operationHasIndexedPropertyByInt, size_t, (JSGlobalObje JSObject* object = baseCell->toObject(globalObject); if (UNLIKELY(subscript < 0)) { // Go the slowest way possible because negative indices don't use indexed storage. - return object->hasPropertyGeneric(globalObject, Identifier::from(vm, subscript), static_cast(internalMethodType)); + return object->hasProperty(globalObject, Identifier::from(vm, subscript)); } - return object->hasPropertyGeneric(globalObject, subscript, static_cast(internalMethodType)); + return object->hasProperty(globalObject, static_cast(subscript)); +} + +JSC_DEFINE_JIT_OPERATION(operationHasEnumerableIndexedProperty, size_t, (JSGlobalObject* globalObject, JSCell* baseCell, int32_t subscript)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSObject* object = baseCell->toObject(globalObject); + if (UNLIKELY(subscript < 0)) { + // Go the slowest way possible because negative indices don't use indexed storage. + return object->hasEnumerableProperty(globalObject, Identifier::from(vm, subscript)); + } + return object->hasEnumerableProperty(globalObject, subscript); } JSC_DEFINE_JIT_OPERATION(operationGetPropertyEnumerator, JSCell*, (JSGlobalObject* globalObject, EncodedJSValue encodedBase)) diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 152c24520df0c..909e59171f2ee 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -99,10 +99,11 @@ JSC_DECLARE_JIT_OPERATION(operationCallNumberConstructor, EncodedJSValue, (JSGlo JSC_DECLARE_JIT_OPERATION(operationGetByValWithThis, EncodedJSValue, (JSGlobalObject*, EncodedJSValue, EncodedJSValue, EncodedJSValue)); JSC_DECLARE_JIT_OPERATION(operationGetPrototypeOf, EncodedJSValue, (JSGlobalObject*, EncodedJSValue)); JSC_DECLARE_JIT_OPERATION(operationGetPrototypeOfObject, EncodedJSValue, (JSGlobalObject*, JSObject*)); -JSC_DECLARE_JIT_OPERATION(operationHasGenericProperty, EncodedJSValue, (JSGlobalObject*, EncodedJSValue, JSCell*)); +JSC_DECLARE_JIT_OPERATION(operationHasIndexedProperty, size_t, (JSGlobalObject*, JSCell*, int32_t)); +JSC_DECLARE_JIT_OPERATION(operationHasEnumerableIndexedProperty, size_t, (JSGlobalObject*, JSCell*, int32_t)); +JSC_DECLARE_JIT_OPERATION(operationHasEnumerableProperty, EncodedJSValue, (JSGlobalObject*, EncodedJSValue, JSCell*)); JSC_DECLARE_JIT_OPERATION(operationHasOwnStructureProperty, EncodedJSValue, (JSGlobalObject*, JSCell*, JSString*)); JSC_DECLARE_JIT_OPERATION(operationInStructureProperty, EncodedJSValue, (JSGlobalObject*, JSCell*, JSString*)); -JSC_DECLARE_JIT_OPERATION(operationHasIndexedPropertyByInt, size_t, (JSGlobalObject*, JSCell*, int32_t, int32_t)); JSC_DECLARE_JIT_OPERATION(operationGetPropertyEnumerator, JSCell*, (JSGlobalObject*, EncodedJSValue)); JSC_DECLARE_JIT_OPERATION(operationGetPropertyEnumeratorCell, JSCell*, (JSGlobalObject*, JSCell*)); JSC_DECLARE_JIT_OPERATION(operationToIndexString, JSCell*, (JSGlobalObject*, int32_t)); diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index ec011fa0f980a..ab1fe8aeca139 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -1212,11 +1212,12 @@ class PredictionPropagationPhase : public Phase { setPrediction(SpecInt32Only); break; } - case HasGenericProperty: - case HasStructureProperty: case HasOwnStructureProperty: case InStructureProperty: - case HasIndexedProperty: { + case HasIndexedProperty: + case HasEnumerableIndexedProperty: + case HasEnumerableStructureProperty: + case HasEnumerableProperty: { setPrediction(SpecBoolean); break; } diff --git a/Source/JavaScriptCore/dfg/DFGSSALoweringPhase.cpp b/Source/JavaScriptCore/dfg/DFGSSALoweringPhase.cpp index 6386b8a362e13..c9637a1d13faa 100644 --- a/Source/JavaScriptCore/dfg/DFGSSALoweringPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGSSALoweringPhase.cpp @@ -82,6 +82,7 @@ class SSALoweringPhase : public Phase { } case HasIndexedProperty: + case HasEnumerableIndexedProperty: lowerBoundsCheck(m_graph.child(m_node, 0), m_graph.child(m_node, 1), m_graph.child(m_node, 2)); break; diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h index 03f01433b29c9..a4f4c75774ad7 100644 --- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h +++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h @@ -292,6 +292,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno case BooleanToNumber: case FiatInt52: case HasIndexedProperty: + case HasEnumerableIndexedProperty: case GetEnumeratorStructurePname: case GetEnumeratorGenericPname: case ToIndexString: @@ -628,8 +629,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno case MultiPutByOffset: case MultiDeleteByOffset: case GetEnumerableLength: - case HasGenericProperty: - case HasStructureProperty: + case HasEnumerableStructureProperty: + case HasEnumerableProperty: case HasOwnStructureProperty: case InStructureProperty: case GetDirectPname: diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index a07f56c00ccba..02a9886d177e8 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -12838,7 +12838,7 @@ void SpeculativeJIT::compileGetEnumerableLength(Node* node) strictInt32Result(resultGPR, node); } -void SpeculativeJIT::compileHasGenericProperty(Node* node) +void SpeculativeJIT::compileHasEnumerableProperty(Node* node) { JSValueOperand base(this, node->child1()); SpeculateCellOperand property(this, node->child2()); @@ -12849,7 +12849,7 @@ void SpeculativeJIT::compileHasGenericProperty(Node* node) flushRegisters(); JSValueRegsFlushedCallResult result(this); JSValueRegs resultRegs = result.regs(); - callOperation(operationHasGenericProperty, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, propertyGPR); + callOperation(operationHasEnumerableProperty, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, propertyGPR); m_jit.exceptionCheck(); blessedBooleanResult(resultRegs.payloadGPR(), node); } @@ -12989,7 +12989,7 @@ void SpeculativeJIT::compileMatchStructure(Node* node) blessedBooleanResult(tempGPR, node); } -void SpeculativeJIT::compileHasStructureProperty(Node* node) +void SpeculativeJIT::compileHasEnumerableStructureProperty(Node* node) { JSValueOperand base(this, node->child1()); SpeculateCellOperand property(this, node->child2()); @@ -13011,7 +13011,7 @@ void SpeculativeJIT::compileHasStructureProperty(Node* node) moveTrueTo(resultRegs.payloadGPR()); - addSlowPathGenerator(slowPathCall(wrongStructure, this, operationHasGenericProperty, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, propertyGPR)); + addSlowPathGenerator(slowPathCall(wrongStructure, this, operationHasEnumerableProperty, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, propertyGPR)); blessedBooleanResult(resultRegs.payloadGPR(), node); } @@ -14267,7 +14267,7 @@ void SpeculativeJIT::compileAllocateNewArrayWithSize(JSGlobalObject* globalObjec sizeGPR, storageGPR)); } -void SpeculativeJIT::compileHasIndexedProperty(Node* node) +void SpeculativeJIT::compileHasIndexedProperty(Node* node, S_JITOperation_GCZ slowPathOperation) { SpeculateCellOperand base(this, m_graph.varArgChild(node, 0)); SpeculateStrictInt32Operand index(this, m_graph.varArgChild(node, 1)); @@ -14383,7 +14383,7 @@ void SpeculativeJIT::compileHasIndexedProperty(Node* node) } } - addSlowPathGenerator(slowPathCall(slowCases, this, operationHasIndexedPropertyByInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, indexGPR, static_cast(node->internalMethodType()))); + addSlowPathGenerator(slowPathCall(slowCases, this, slowPathOperation, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, indexGPR)); unblessedBooleanResult(resultGPR, node); } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index d552344d2c530..876a38291adbc 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1446,13 +1446,13 @@ class SpeculativeJIT { void compileThrow(Node*); void compileThrowStaticError(Node*); void compileGetEnumerableLength(Node*); - void compileHasGenericProperty(Node*); + void compileHasEnumerableStructureProperty(Node*); + void compileHasEnumerableProperty(Node*); void compileToIndexString(Node*); void compilePutByIdFlush(Node*); void compilePutById(Node*); void compilePutByIdDirect(Node*); void compilePutByIdWithThis(Node*); - void compileHasStructureProperty(Node*); template void compileHasOwnStructurePropertyImpl(Node*, Function); void compileHasOwnStructureProperty(Node*); @@ -1488,7 +1488,7 @@ class SpeculativeJIT { void compileCallNumberConstructor(Node*); void compileLogShadowChickenPrologue(Node*); void compileLogShadowChickenTail(Node*); - void compileHasIndexedProperty(Node*); + void compileHasIndexedProperty(Node*, S_JITOperation_GCZ); void compileExtractCatchLocal(Node*); void compileClearCatchLocals(Node*); void compileProfileType(Node*); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index e03728d0e20d1..1e8b8c296c402 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -4073,12 +4073,12 @@ void SpeculativeJIT::compile(Node* node) compileGetEnumerableLength(node); break; } - case HasGenericProperty: { - compileHasGenericProperty(node); + case HasEnumerableStructureProperty: { + compileHasEnumerableStructureProperty(node); break; } - case HasStructureProperty: { - compileHasStructureProperty(node); + case HasEnumerableProperty: { + compileHasEnumerableProperty(node); break; } case HasOwnStructureProperty: { @@ -4090,7 +4090,11 @@ void SpeculativeJIT::compile(Node* node) break; } case HasIndexedProperty: { - compileHasIndexedProperty(node); + compileHasIndexedProperty(node, operationHasIndexedProperty); + break; + } + case HasEnumerableIndexedProperty: { + compileHasIndexedProperty(node, operationHasEnumerableIndexedProperty); break; } case GetDirectPname: { diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 5cdeeb385894d..a53c2ba3596d2 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -5120,12 +5120,12 @@ void SpeculativeJIT::compile(Node* node) compileGetEnumerableLength(node); break; } - case HasGenericProperty: { - compileHasGenericProperty(node); + case HasEnumerableStructureProperty: { + compileHasEnumerableStructureProperty(node); break; } - case HasStructureProperty: { - compileHasStructureProperty(node); + case HasEnumerableProperty: { + compileHasEnumerableProperty(node); break; } case HasOwnStructureProperty: { @@ -5137,7 +5137,11 @@ void SpeculativeJIT::compile(Node* node) break; } case HasIndexedProperty: { - compileHasIndexedProperty(node); + compileHasIndexedProperty(node, operationHasIndexedProperty); + break; + } + case HasEnumerableIndexedProperty: { + compileHasIndexedProperty(node, operationHasEnumerableIndexedProperty); break; } case GetDirectPname: { diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp index 0e6812fde7588..35253b604e478 100644 --- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp +++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp @@ -280,11 +280,12 @@ inline CapabilityLevel canCompile(Node* node) case DoubleConstant: case Int52Constant: case BooleanToNumber: - case HasGenericProperty: - case HasStructureProperty: + case HasIndexedProperty: + case HasEnumerableIndexedProperty: + case HasEnumerableStructureProperty: + case HasEnumerableProperty: case HasOwnStructureProperty: case InStructureProperty: - case HasIndexedProperty: case GetDirectPname: case GetEnumerableLength: case GetIndexedPropertyStorage: diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp index e477d8a8f4d6b..e2952da0a393e 100644 --- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp +++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp @@ -1500,13 +1500,16 @@ class LowerDFGToB3 { compileStoreBarrier(); break; case HasIndexedProperty: - compileHasIndexedProperty(); + compileHasIndexedProperty(operationHasIndexedProperty); break; - case HasGenericProperty: - compileHasGenericProperty(); + case HasEnumerableIndexedProperty: + compileHasIndexedProperty(operationHasEnumerableIndexedProperty); break; - case HasStructureProperty: - compileHasStructureProperty(); + case HasEnumerableStructureProperty: + compileHasEnumerableStructureProperty(); + break; + case HasEnumerableProperty: + compileHasEnumerableProperty(); break; case HasOwnStructureProperty: compileHasOwnStructureProperty(); @@ -12492,7 +12495,7 @@ class LowerDFGToB3 { emitStoreBarrier(lowCell(m_node->child1()), m_node->op() == FencedStoreBarrier); } - void compileHasIndexedProperty() + void compileHasIndexedProperty(S_JITOperation_GCZ slowPathOperation) { JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic); LValue base = lowCell(m_graph.varArgChild(m_node, 0)); @@ -12503,7 +12506,6 @@ class LowerDFGToB3 { case Array::Int32: case Array::Contiguous: { LValue storage = lowStorage(m_graph.varArgChild(m_node, 2)); - LValue internalMethodType = m_out.constInt32(static_cast(m_node->internalMethodType())); IndexedAbstractHeap& heap = mode.type() == Array::Int32 ? m_heaps.indexedInt32Properties : m_heaps.indexedContiguousProperties; @@ -12534,7 +12536,7 @@ class LowerDFGToB3 { m_out.appendTo(slowCase, continuation); ValueFromBlock slowResult = m_out.anchor( - m_out.notZero64(vmCall(Int64, operationHasIndexedPropertyByInt, weakPointer(globalObject), base, index, internalMethodType))); + m_out.notZero64(vmCall(Int64, slowPathOperation, weakPointer(globalObject), base, index))); m_out.jump(continuation); m_out.appendTo(continuation, lastNext); @@ -12543,7 +12545,6 @@ class LowerDFGToB3 { } case Array::Double: { LValue storage = lowStorage(m_graph.varArgChild(m_node, 2)); - LValue internalMethodType = m_out.constInt32(static_cast(m_node->internalMethodType())); IndexedAbstractHeap& heap = m_heaps.indexedDoubleProperties; @@ -12573,7 +12574,7 @@ class LowerDFGToB3 { m_out.appendTo(slowCase, continuation); ValueFromBlock slowResult = m_out.anchor( - m_out.notZero64(vmCall(Int64, operationHasIndexedPropertyByInt, weakPointer(globalObject), base, index, internalMethodType))); + m_out.notZero64(vmCall(Int64, slowPathOperation, weakPointer(globalObject), base, index))); m_out.jump(continuation); m_out.appendTo(continuation, lastNext); @@ -12583,7 +12584,6 @@ class LowerDFGToB3 { case Array::ArrayStorage: { LValue storage = lowStorage(m_graph.varArgChild(m_node, 2)); - LValue internalMethodType = m_out.constInt32(static_cast(m_node->internalMethodType())); LBasicBlock slowCase = m_out.newBlock(); LBasicBlock continuation = m_out.newBlock(); @@ -12611,7 +12611,7 @@ class LowerDFGToB3 { m_out.appendTo(slowCase, continuation); ValueFromBlock slowResult = m_out.anchor( - m_out.notZero64(vmCall(Int64, operationHasIndexedPropertyByInt, weakPointer(globalObject), base, index, internalMethodType))); + m_out.notZero64(vmCall(Int64, slowPathOperation, weakPointer(globalObject), base, index))); m_out.jump(continuation); m_out.appendTo(continuation, lastNext); @@ -12620,19 +12620,18 @@ class LowerDFGToB3 { } default: { - LValue internalMethodType = m_out.constInt32(static_cast(m_node->internalMethodType())); - setBoolean(m_out.notZero64(vmCall(Int64, operationHasIndexedPropertyByInt, weakPointer(globalObject), base, index, internalMethodType))); + setBoolean(m_out.notZero64(vmCall(Int64, slowPathOperation, weakPointer(globalObject), base, index))); break; } } } - void compileHasGenericProperty() + void compileHasEnumerableProperty() { JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic); LValue base = lowJSValue(m_node->child1()); LValue property = lowCell(m_node->child2()); - setJSValue(vmCall(Int64, operationHasGenericProperty, weakPointer(globalObject), base, property)); + setJSValue(vmCall(Int64, operationHasEnumerableProperty, weakPointer(globalObject), base, property)); } template @@ -12672,9 +12671,9 @@ class LowerDFGToB3 { setBoolean(m_out.phi(Int32, correctStructureResult, slowPathResult)); } - void compileHasStructureProperty() + void compileHasEnumerableStructureProperty() { - compileHasStructurePropertyImpl(lowJSValue(m_node->child1()), operationHasGenericProperty); + compileHasStructurePropertyImpl(lowJSValue(m_node->child1()), operationHasEnumerableProperty); } void compileHasOwnStructureProperty() diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp index 2a5fc2930830d..bb3506ee86d07 100644 --- a/Source/JavaScriptCore/jit/JIT.cpp +++ b/Source/JavaScriptCore/jit/JIT.cpp @@ -304,7 +304,7 @@ void JIT::privateCompileMainPass() DEFINE_SLOW_OP(new_array_buffer) DEFINE_SLOW_OP(spread) DEFINE_SLOW_OP(get_enumerable_length) - DEFINE_SLOW_OP(has_generic_property) + DEFINE_SLOW_OP(has_enumerable_property) DEFINE_SLOW_OP(get_property_enumerator) DEFINE_SLOW_OP(to_index_string) DEFINE_SLOW_OP(create_direct_arguments) @@ -463,10 +463,10 @@ void JIT::privateCompileMainPass() DEFINE_OP(op_get_from_arguments) DEFINE_OP(op_put_to_arguments) - DEFINE_OP(op_has_structure_property) + DEFINE_OP(op_has_enumerable_indexed_property) + DEFINE_OP(op_has_enumerable_structure_property) DEFINE_OP(op_has_own_structure_property) DEFINE_OP(op_in_structure_property) - DEFINE_OP(op_has_indexed_property) DEFINE_OP(op_get_direct_pname) DEFINE_OP(op_enumerator_structure_pname) DEFINE_OP(op_enumerator_generic_pname) @@ -592,7 +592,7 @@ void JIT::privateCompileSlowCases() DEFINE_SLOWCASE_OP(op_del_by_val) DEFINE_SLOWCASE_OP(op_del_by_id) DEFINE_SLOWCASE_OP(op_sub) - DEFINE_SLOWCASE_OP(op_has_indexed_property) + DEFINE_SLOWCASE_OP(op_has_enumerable_indexed_property) DEFINE_SLOWCASE_OP(op_get_from_scope) DEFINE_SLOWCASE_OP(op_put_to_scope) @@ -625,7 +625,7 @@ void JIT::privateCompileSlowCases() DEFINE_SLOWCASE_SLOW_OP(nstricteq) DEFINE_SLOWCASE_SLOW_OP(get_direct_pname) DEFINE_SLOWCASE_SLOW_OP(get_prototype_of) - DEFINE_SLOWCASE_SLOW_OP(has_structure_property) + DEFINE_SLOWCASE_SLOW_OP(has_enumerable_structure_property) DEFINE_SLOWCASE_SLOW_OP(has_own_structure_property) DEFINE_SLOWCASE_SLOW_OP(in_structure_property) DEFINE_SLOWCASE_SLOW_OP(resolve_scope) diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h index afabd73400d7a..7c35b56734775 100644 --- a/Source/JavaScriptCore/jit/JIT.h +++ b/Source/JavaScriptCore/jit/JIT.h @@ -665,10 +665,10 @@ namespace JSC { void emit_op_urshift(const Instruction*); template void emit_op_has_structure_propertyImpl(const Instruction*); - void emit_op_has_structure_property(const Instruction*); + void emit_op_has_enumerable_indexed_property(const Instruction*); + void emit_op_has_enumerable_structure_property(const Instruction*); void emit_op_has_own_structure_property(const Instruction*); void emit_op_in_structure_property(const Instruction*); - void emit_op_has_indexed_property(const Instruction*); void emit_op_get_direct_pname(const Instruction*); void emit_op_enumerator_structure_pname(const Instruction*); void emit_op_enumerator_generic_pname(const Instruction*); @@ -723,7 +723,7 @@ namespace JSC { void emitSlow_op_put_by_val(const Instruction*, Vector::iterator&); void emitSlow_op_put_private_name(const Instruction*, Vector::iterator&); void emitSlow_op_sub(const Instruction*, Vector::iterator&); - void emitSlow_op_has_indexed_property(const Instruction*, Vector::iterator&); + void emitSlow_op_has_enumerable_indexed_property(const Instruction*, Vector::iterator&); void emit_op_resolve_scope(const Instruction*); void emit_op_get_from_scope(const Instruction*); diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp index ba10c89b56467..166836680911c 100644 --- a/Source/JavaScriptCore/jit/JITOpcodes.cpp +++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp @@ -1392,9 +1392,9 @@ void JIT::emit_op_has_structure_propertyImpl(const Instruction* currentInstructi emitPutVirtualRegister(dst); } -void JIT::emit_op_has_structure_property(const Instruction* currentInstruction) +void JIT::emit_op_has_enumerable_structure_property(const Instruction* currentInstruction) { - emit_op_has_structure_propertyImpl(currentInstruction); + emit_op_has_structure_propertyImpl(currentInstruction); } void JIT::emit_op_has_own_structure_property(const Instruction* currentInstruction) @@ -1434,9 +1434,9 @@ void JIT::privateCompileHasIndexedProperty(ByValInfo* byValInfo, ReturnAddressPt MacroAssembler::repatchCall(CodeLocationCall(MacroAssemblerCodePtr(returnAddress)), FunctionPtr(operationHasIndexedPropertyGeneric)); } -void JIT::emit_op_has_indexed_property(const Instruction* currentInstruction) +void JIT::emit_op_has_enumerable_indexed_property(const Instruction* currentInstruction) { - auto bytecode = currentInstruction->as(); + auto bytecode = currentInstruction->as(); auto& metadata = bytecode.metadata(m_codeBlock); VirtualRegister dst = bytecode.m_dst; VirtualRegister base = bytecode.m_base; @@ -1481,11 +1481,11 @@ void JIT::emit_op_has_indexed_property(const Instruction* currentInstruction) m_byValCompilationInfo.append(ByValCompilationInfo(byValInfo, m_bytecodeIndex, PatchableJump(), badType, mode, profile, done, nextHotPath)); } -void JIT::emitSlow_op_has_indexed_property(const Instruction* currentInstruction, Vector::iterator& iter) +void JIT::emitSlow_op_has_enumerable_indexed_property(const Instruction* currentInstruction, Vector::iterator& iter) { linkAllSlowCases(iter); - auto bytecode = currentInstruction->as(); + auto bytecode = currentInstruction->as(); VirtualRegister dst = bytecode.m_dst; VirtualRegister base = bytecode.m_base; VirtualRegister property = bytecode.m_property; diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp index ebfc0b6901c51..e93a91291fc2e 100644 --- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp +++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp @@ -1151,9 +1151,9 @@ void JIT::emit_op_has_structure_propertyImpl(const Instruction* currentInstructi emitStoreBool(dst, regT0); } -void JIT::emit_op_has_structure_property(const Instruction* currentInstruction) +void JIT::emit_op_has_enumerable_structure_property(const Instruction* currentInstruction) { - emit_op_has_structure_propertyImpl(currentInstruction); + emit_op_has_structure_propertyImpl(currentInstruction); } void JIT::emit_op_has_own_structure_property(const Instruction* currentInstruction) @@ -1193,9 +1193,9 @@ void JIT::privateCompileHasIndexedProperty(ByValInfo* byValInfo, ReturnAddressPt MacroAssembler::repatchCall(CodeLocationCall(MacroAssemblerCodePtr(returnAddress)), FunctionPtr(operationHasIndexedPropertyGeneric)); } -void JIT::emit_op_has_indexed_property(const Instruction* currentInstruction) +void JIT::emit_op_has_enumerable_indexed_property(const Instruction* currentInstruction) { - auto bytecode = currentInstruction->as(); + auto bytecode = currentInstruction->as(); auto& metadata = bytecode.metadata(m_codeBlock); VirtualRegister dst = bytecode.m_dst; VirtualRegister base = bytecode.m_base; @@ -1240,11 +1240,11 @@ void JIT::emit_op_has_indexed_property(const Instruction* currentInstruction) m_byValCompilationInfo.append(ByValCompilationInfo(byValInfo, m_bytecodeIndex, PatchableJump(), badType, mode, profile, done, nextHotPath)); } -void JIT::emitSlow_op_has_indexed_property(const Instruction* currentInstruction, Vector::iterator& iter) +void JIT::emitSlow_op_has_enumerable_indexed_property(const Instruction* currentInstruction, Vector::iterator& iter) { linkAllSlowCases(iter); - auto bytecode = currentInstruction->as(); + auto bytecode = currentInstruction->as(); VirtualRegister dst = bytecode.m_dst; VirtualRegister base = bytecode.m_base; VirtualRegister property = bytecode.m_property; diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp index 236692c8528f9..2b96c93c3110d 100644 --- a/Source/JavaScriptCore/jit/JITOperations.cpp +++ b/Source/JavaScriptCore/jit/JITOperations.cpp @@ -2445,7 +2445,7 @@ JSC_DEFINE_JIT_OPERATION(operationHasIndexedPropertyDefault, EncodedJSValue, (JS if (!CommonSlowPaths::canAccessArgumentIndexQuickly(*object, index)) byValInfo->arrayProfile->setOutOfBounds(); - return JSValue::encode(jsBoolean(object->hasPropertyGeneric(globalObject, index, PropertySlot::InternalMethodType::GetOwnProperty))); + return JSValue::encode(jsBoolean(object->hasEnumerableProperty(globalObject, index))); } JSC_DEFINE_JIT_OPERATION(operationHasIndexedPropertyGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)) @@ -2466,7 +2466,7 @@ JSC_DEFINE_JIT_OPERATION(operationHasIndexedPropertyGeneric, EncodedJSValue, (JS if (!CommonSlowPaths::canAccessArgumentIndexQuickly(*object, index)) byValInfo->arrayProfile->setOutOfBounds(); - return JSValue::encode(jsBoolean(object->hasPropertyGeneric(globalObject, index, PropertySlot::InternalMethodType::GetOwnProperty))); + return JSValue::encode(jsBoolean(object->hasEnumerableProperty(globalObject, index))); } static bool deleteById(JSGlobalObject* globalObject, VM& vm, DeletePropertySlot& slot, JSValue base, const Identifier& ident, ECMAMode ecmaMode) diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h index 3d0545e0a855a..9425b517f98ee 100644 --- a/Source/JavaScriptCore/jit/JITOperations.h +++ b/Source/JavaScriptCore/jit/JITOperations.h @@ -138,6 +138,7 @@ using V_JITOperation_GSsiJJC = void(JIT_OPERATION_ATTRIBUTES *)(JSGlobalObject*, using C_JITOperation_TT = uintptr_t(JIT_OPERATION_ATTRIBUTES *)(StringImpl*, StringImpl*); using C_JITOperation_B_GJssJss = uintptr_t(JIT_OPERATION_ATTRIBUTES *)(JSGlobalObject*, JSString*, JSString*); using S_JITOperation_GC = size_t(JIT_OPERATION_ATTRIBUTES *)(JSGlobalObject*, JSCell*); +using S_JITOperation_GCZ = size_t(JIT_OPERATION_ATTRIBUTES *)(JSGlobalObject*, JSCell*, int32_t); using S_JITOperation_GJJ = size_t(JIT_OPERATION_ATTRIBUTES *)(JSGlobalObject*, EncodedJSValue, EncodedJSValue); using V_JITOperation_GJJJ = void(JIT_OPERATION_ATTRIBUTES *)(JSGlobalObject*, EncodedJSValue, EncodedJSValue, EncodedJSValue); using J_JITOperation_GSsiJJ = EncodedJSValue(JIT_OPERATION_ATTRIBUTES *)(JSGlobalObject*, StructureStubInfo*, EncodedJSValue, EncodedJSValue); diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm index c6d654c791934..4ddb2a90131b8 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm @@ -1972,11 +1972,11 @@ slowPathOp(get_enumerable_length) slowPathOp(get_property_enumerator) slowPathOp(greater) slowPathOp(greatereq) -slowPathOp(has_generic_property) -slowPathOp(has_indexed_property) +slowPathOp(has_enumerable_indexed_property) +slowPathOp(has_enumerable_property) if not JSVALUE64 - slowPathOp(has_structure_property) + slowPathOp(has_enumerable_structure_property) slowPathOp(has_own_structure_property) slowPathOp(in_structure_property) slowPathOp(get_prototype_of) diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm index 7145f1a7a830c..12a21467ece49 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm @@ -3041,8 +3041,8 @@ macro hasStructurePropertyImpl(size, get, dispatch, return, slowPathCall) dispatch() end -llintOpWithReturn(op_has_structure_property, OpHasStructureProperty, macro (size, get, dispatch, return) - hasStructurePropertyImpl(size, get, dispatch, return, _slow_path_has_structure_property) +llintOpWithReturn(op_has_enumerable_structure_property, OpHasEnumerableStructureProperty, macro (size, get, dispatch, return) + hasStructurePropertyImpl(size, get, dispatch, return, _slow_path_has_enumerable_structure_property) end) llintOpWithReturn(op_has_own_structure_property, OpHasOwnStructureProperty, macro (size, get, dispatch, return) diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp index 140cd68cf280f..caf4e112b3bf3 100644 --- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp +++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp @@ -1040,23 +1040,23 @@ JSC_DEFINE_COMMON_SLOW_PATH(slow_path_get_enumerable_length) RETURN(jsNumber(enumerator->indexedLength())); } -JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_indexed_property) +JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_enumerable_indexed_property) { BEGIN(); - auto bytecode = pc->as(); + auto bytecode = pc->as(); auto& metadata = bytecode.metadata(codeBlock); JSObject* base = GET_C(bytecode.m_base).jsValue().toObject(globalObject); CHECK_EXCEPTION(); JSValue property = GET(bytecode.m_property).jsValue(); metadata.m_arrayProfile.observeStructure(base->structure(vm)); ASSERT(property.isUInt32AsAnyInt()); - RETURN(jsBoolean(base->hasPropertyGeneric(globalObject, property.asUInt32AsAnyInt(), PropertySlot::InternalMethodType::GetOwnProperty))); + RETURN(jsBoolean(base->hasEnumerableProperty(globalObject, property.asUInt32AsAnyInt()))); } -JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_structure_property) +JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_enumerable_structure_property) { BEGIN(); - auto bytecode = pc->as(); + auto bytecode = pc->as(); JSObject* base = GET_C(bytecode.m_base).jsValue().toObject(globalObject); CHECK_EXCEPTION(); JSValue property = GET(bytecode.m_property).jsValue(); @@ -1069,7 +1069,7 @@ JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_structure_property) JSString* string = asString(property); auto propertyName = string->toIdentifier(globalObject); CHECK_EXCEPTION(); - RETURN(jsBoolean(base->hasPropertyGeneric(globalObject, propertyName, PropertySlot::InternalMethodType::GetOwnProperty))); + RETURN(jsBoolean(base->hasEnumerableProperty(globalObject, propertyName))); } JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_own_structure_property) @@ -1108,10 +1108,10 @@ JSC_DEFINE_COMMON_SLOW_PATH(slow_path_in_structure_property) RETURN(jsBoolean(CommonSlowPaths::opInByVal(globalObject, base, asString(property)))); } -JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_generic_property) +JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_enumerable_property) { BEGIN(); - auto bytecode = pc->as(); + auto bytecode = pc->as(); JSObject* base = GET_C(bytecode.m_base).jsValue().toObject(globalObject); CHECK_EXCEPTION(); JSValue property = GET(bytecode.m_property).jsValue(); @@ -1119,7 +1119,7 @@ JSC_DEFINE_COMMON_SLOW_PATH(slow_path_has_generic_property) JSString* string = asString(property); auto propertyName = string->toIdentifier(globalObject); CHECK_EXCEPTION(); - RETURN(jsBoolean(base->hasPropertyGeneric(globalObject, propertyName, PropertySlot::InternalMethodType::GetOwnProperty))); + RETURN(jsBoolean(base->hasEnumerableProperty(globalObject, propertyName))); } JSC_DEFINE_COMMON_SLOW_PATH(slow_path_get_direct_pname) diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h index 524b6b81a22a5..0938ce621f072 100644 --- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h +++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h @@ -259,11 +259,11 @@ JSC_DECLARE_COMMON_SLOW_PATH(slow_path_strcat); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_to_primitive); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_to_property_key); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_get_enumerable_length); -JSC_DECLARE_COMMON_SLOW_PATH(slow_path_has_generic_property); -JSC_DECLARE_COMMON_SLOW_PATH(slow_path_has_structure_property); +JSC_DECLARE_COMMON_SLOW_PATH(slow_path_has_enumerable_indexed_property); +JSC_DECLARE_COMMON_SLOW_PATH(slow_path_has_enumerable_structure_property); +JSC_DECLARE_COMMON_SLOW_PATH(slow_path_has_enumerable_property); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_has_own_structure_property); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_in_structure_property); -JSC_DECLARE_COMMON_SLOW_PATH(slow_path_has_indexed_property); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_get_direct_pname); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_get_property_enumerator); JSC_DECLARE_COMMON_SLOW_PATH(slow_path_enumerator_structure_pname); diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index c7f2a0c813883..88084df77a5bb 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -1987,16 +1987,17 @@ void JSObject::putDirectNonIndexAccessorWithoutTransition(VM& vm, PropertyName p structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto); } -// HasProperty(O, P) from Section 7.3.10 of the spec. -// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasproperty +// https://tc39.es/ecma262/#sec-hasproperty bool JSObject::hasProperty(JSGlobalObject* globalObject, PropertyName propertyName) const { - return hasPropertyGeneric(globalObject, propertyName, PropertySlot::InternalMethodType::HasProperty); + PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty); + return const_cast(this)->getPropertySlot(globalObject, propertyName, slot); } bool JSObject::hasProperty(JSGlobalObject* globalObject, unsigned propertyName) const { - return hasPropertyGeneric(globalObject, propertyName, PropertySlot::InternalMethodType::HasProperty); + PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty); + return const_cast(this)->getPropertySlot(globalObject, propertyName, slot); } bool JSObject::hasProperty(JSGlobalObject* globalObject, uint64_t propertyName) const @@ -2007,16 +2008,24 @@ bool JSObject::hasProperty(JSGlobalObject* globalObject, uint64_t propertyName) return hasProperty(globalObject, Identifier::from(globalObject->vm(), propertyName)); } -bool JSObject::hasPropertyGeneric(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot::InternalMethodType internalMethodType) const +bool JSObject::hasEnumerableProperty(JSGlobalObject* globalObject, PropertyName propertyName) const { - PropertySlot slot(this, internalMethodType); - return const_cast(this)->getPropertySlot(globalObject, propertyName, slot); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty); + bool hasProperty = const_cast(this)->getPropertySlot(globalObject, propertyName, slot); + RETURN_IF_EXCEPTION(scope, false); + return hasProperty && !(slot.attributes() & PropertyAttribute::DontEnum); } -bool JSObject::hasPropertyGeneric(JSGlobalObject* globalObject, unsigned propertyName, PropertySlot::InternalMethodType internalMethodType) const +bool JSObject::hasEnumerableProperty(JSGlobalObject* globalObject, unsigned propertyName) const { - PropertySlot slot(this, internalMethodType); - return const_cast(this)->getPropertySlot(globalObject, propertyName, slot); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty); + bool hasProperty = const_cast(this)->getPropertySlot(globalObject, propertyName, slot); + RETURN_IF_EXCEPTION(scope, false); + return hasProperty && !(slot.attributes() & PropertyAttribute::DontEnum); } // ECMA 8.6.2.5 @@ -2371,6 +2380,8 @@ JSC_DEFINE_HOST_FUNCTION(objectPrivateFuncInstanceOf, (JSGlobalObject* globalObj return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(globalObject, value, proto))); } +// FIXME: Assert that properties returned by getOwnPropertyNames() are reported enumerable by getOwnPropertySlot(). +// https://bugs.webkit.org/show_bug.cgi?id=219926 void JSObject::getPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, EnumerationMode mode) { VM& vm = globalObject->vm(); diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 158e026326d3a..50ff5953101a2 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -655,8 +655,8 @@ class JSObject : public JSCell { JS_EXPORT_PRIVATE bool hasProperty(JSGlobalObject*, PropertyName) const; JS_EXPORT_PRIVATE bool hasProperty(JSGlobalObject*, unsigned propertyName) const; bool hasProperty(JSGlobalObject*, uint64_t propertyName) const; - bool hasPropertyGeneric(JSGlobalObject*, PropertyName, PropertySlot::InternalMethodType) const; - bool hasPropertyGeneric(JSGlobalObject*, unsigned propertyName, PropertySlot::InternalMethodType) const; + bool hasEnumerableProperty(JSGlobalObject*, PropertyName) const; + bool hasEnumerableProperty(JSGlobalObject*, unsigned propertyName) const; bool hasOwnProperty(JSGlobalObject*, PropertyName, PropertySlot&) const; bool hasOwnProperty(JSGlobalObject*, PropertyName) const; bool hasOwnProperty(JSGlobalObject*, unsigned) const; diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index 05ecfd5fd230a..8696c0eb5a68a 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,18 @@ +2020-12-15 Alexey Shvayka + + Non-enumerable property fails to shadow inherited enumerable property from for-in + https://bugs.webkit.org/show_bug.cgi?id=38970 + + Reviewed by Keith Miller. + + Report RuntimeArray indices as [[Enumerable]]. + + Test: platform/mac/fast/dom/wrapper-classes-objc.html + + * bridge/runtime_array.cpp: + (JSC::RuntimeArray::getOwnPropertySlot): + (JSC::RuntimeArray::getOwnPropertySlotByIndex): + 2020-12-15 Jer Noble [Cocoa] Adopt -externalContentProtectionStatus diff --git a/Source/WebCore/bridge/runtime_array.cpp b/Source/WebCore/bridge/runtime_array.cpp index 802dca0b123b7..f565370313900 100644 --- a/Source/WebCore/bridge/runtime_array.cpp +++ b/Source/WebCore/bridge/runtime_array.cpp @@ -100,7 +100,7 @@ bool RuntimeArray::getOwnPropertySlot(JSObject* object, JSGlobalObject* lexicalG Optional index = parseIndex(propertyName); if (index && index.value() < thisObject->getLength()) { - slot.setValue(thisObject, PropertyAttribute::DontDelete | PropertyAttribute::DontEnum, + slot.setValue(thisObject, static_cast(PropertyAttribute::DontDelete), thisObject->getConcreteArray()->valueAt(lexicalGlobalObject, index.value())); return true; } @@ -112,7 +112,7 @@ bool RuntimeArray::getOwnPropertySlotByIndex(JSObject* object, JSGlobalObject* l { RuntimeArray* thisObject = jsCast(object); if (index < thisObject->getLength()) { - slot.setValue(thisObject, PropertyAttribute::DontDelete | PropertyAttribute::DontEnum, + slot.setValue(thisObject, static_cast(PropertyAttribute::DontDelete), thisObject->getConcreteArray()->valueAt(lexicalGlobalObject, index)); return true; }