Diff
Deleted: trunk/JSTests/ChakraCore/test/es5/EnumeratingWithES5.baseline-jsc (264573 => 264574)
--- trunk/JSTests/ChakraCore/test/es5/EnumeratingWithES5.baseline-jsc 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/JSTests/ChakraCore/test/es5/EnumeratingWithES5.baseline-jsc 2020-07-18 23:09:56 UTC (rev 264574)
@@ -1,382 +0,0 @@
-Test 1: Non-writable, simple type handler
-Testing for-in enumeration
-d: dvalue
-Setting value to 1
-d: 1
-Testing getOwnPropertyNames enumeration
-d: 1
-Setting value to 2
-d: 2
-Setting writability of d to false
-Testing for-in enumeration
-d: 2
-Setting value to 3
-d: 2
-Testing getOwnPropertyNames enumeration
-d: 2
-Setting value to 4
-d: 2
-Setting writability of d to true
-Testing for-in enumeration
-d: 2
-Setting value to 5
-d: 5
-Testing getOwnPropertyNames enumeration
-d: 5
-Setting value to 6
-d: 6
-Changing writability during enumeration...
-Testing for-in enumeration
-Setting writability of d to false
-d: 6
-Setting value to 7
-d: 6
-Testing getOwnPropertyNames enumeration
-Setting writability of d to false
-d: 6
-Setting value to 8
-d: 6
-Testing for-in enumeration
-Setting writability of d to true
-d: 6
-Setting value to 9
-d: 9
-Testing getOwnPropertyNames enumeration
-Setting writability of d to true
-d: 9
-Setting value to 10
-d: 10
-Freezing object
-Testing for-in enumeration
-d: 10
-Setting value to 11
-d: 10
-Testing getOwnPropertyNames enumeration
-d: 10
-Setting value to 12
-d: 10
-
-Test 2: Non-writable, simple dictionary type handler
-Testing for-in enumeration
-d: dvalue
-Setting value to 1
-d: 1
-Testing getOwnPropertyNames enumeration
-d: 1
-Setting value to 2
-d: 2
-Setting writability of d to false
-Testing for-in enumeration
-d: 2
-Setting value to 3
-d: 2
-Testing getOwnPropertyNames enumeration
-d: 2
-Setting value to 4
-d: 2
-Setting writability of d to true
-Testing for-in enumeration
-d: 2
-Setting value to 5
-d: 5
-Testing getOwnPropertyNames enumeration
-d: 5
-Setting value to 6
-d: 6
-Changing writability during enumeration...
-Testing for-in enumeration
-Setting writability of d to false
-d: 6
-Setting value to 7
-d: 6
-Testing getOwnPropertyNames enumeration
-Setting writability of d to false
-d: 6
-Setting value to 8
-d: 6
-Testing for-in enumeration
-Setting writability of d to true
-d: 6
-Setting value to 9
-d: 9
-Testing getOwnPropertyNames enumeration
-Setting writability of d to true
-d: 9
-Setting value to 10
-d: 10
-Freezing object
-Testing for-in enumeration
-d: 10
-Setting value to 11
-d: 10
-Testing getOwnPropertyNames enumeration
-d: 10
-Setting value to 12
-d: 10
-
-Test 3: Non-writable, dictionary type handler
-Testing for-in enumeration
-d: dvalue
-Setting value to 1
-d: 1
-Testing getOwnPropertyNames enumeration
-d: 1
-Setting value to 2
-d: 2
-Setting writability of d to false
-Testing for-in enumeration
-d: 2
-Setting value to 3
-d: 2
-Testing getOwnPropertyNames enumeration
-d: 2
-Setting value to 4
-d: 2
-Setting writability of d to true
-Testing for-in enumeration
-d: 2
-Setting value to 5
-d: 5
-Testing getOwnPropertyNames enumeration
-d: 5
-Setting value to 6
-d: 6
-Changing writability during enumeration...
-Testing for-in enumeration
-Setting writability of d to false
-d: 6
-Setting value to 7
-d: 6
-Testing getOwnPropertyNames enumeration
-Setting writability of d to false
-d: 6
-Setting value to 8
-d: 6
-Testing for-in enumeration
-Setting writability of d to true
-d: 6
-Setting value to 9
-d: 9
-Testing getOwnPropertyNames enumeration
-Setting writability of d to true
-d: 9
-Setting value to 10
-d: 10
-Freezing object
-Testing for-in enumeration
-d: 10
-Setting value to 11
-d: 10
-Testing getOwnPropertyNames enumeration
-d: 10
-Setting value to 12
-d: 10
-
-Test 4: Accessors
-Defining accessors for b
-Defining accessors for f
-Testing for-in enumeration
-a: aValue
-Setting value to 1
-a: 1
-b: GETTER FOR b
-Setting value to 2
-SETTER FOR b
-b: GETTER FOR b
-d: dValue
-Setting value to 3
-d: 3
-f: GETTER FOR f
-Setting value to 4
-SETTER FOR f
-f: GETTER FOR f
-g: gValue
-Setting value to 5
-g: 5
-Testing getOwnPropertyNames enumeration
-a: 1
-Setting value to 6
-a: 6
-b: GETTER FOR b
-Setting value to 7
-SETTER FOR b
-b: GETTER FOR b
-d: 3
-Setting value to 8
-d: 8
-e: eValue
-Setting value to 9
-e: 9
-f: GETTER FOR f
-Setting value to 10
-SETTER FOR f
-f: GETTER FOR f
-g: 5
-Setting value to 11
-g: 11
-Defining accessors for d
-Testing for-in enumeration
-a: 6
-Setting value to 12
-a: 12
-b: GETTER FOR b
-Setting value to 13
-SETTER FOR b
-b: GETTER FOR b
-f: GETTER FOR f
-Setting value to 14
-SETTER FOR f
-f: GETTER FOR f
-g: 11
-Setting value to 15
-g: 15
-d: GETTER
-Setting value to 16
-SETTER
-d: GETTER
-Testing getOwnPropertyNames enumeration
-a: 12
-Setting value to 17
-a: 17
-b: GETTER FOR b
-Setting value to 18
-SETTER FOR b
-b: GETTER FOR b
-e: 9
-Setting value to 19
-e: 19
-f: GETTER FOR f
-Setting value to 20
-SETTER FOR f
-f: GETTER FOR f
-g: 15
-Setting value to 21
-g: 21
-d: GETTER
-Setting value to 22
-SETTER
-d: GETTER
-Defining data property d with value 23
-Testing for-in enumeration
-a: 17
-Setting value to 24
-a: 24
-b: GETTER FOR b
-Setting value to 25
-SETTER FOR b
-b: GETTER FOR b
-f: GETTER FOR f
-Setting value to 26
-SETTER FOR f
-f: GETTER FOR f
-g: 21
-Setting value to 27
-g: 27
-d: 23
-Setting value to 28
-d: 28
-Testing getOwnPropertyNames enumeration
-a: 24
-Setting value to 29
-a: 29
-b: GETTER FOR b
-Setting value to 30
-SETTER FOR b
-b: GETTER FOR b
-e: 19
-Setting value to 31
-e: 31
-f: GETTER FOR f
-Setting value to 32
-SETTER FOR f
-f: GETTER FOR f
-g: 27
-Setting value to 33
-g: 33
-d: 28
-Setting value to 34
-d: 34
-Defining accessor property during enumeration...
-Testing for-in enumeration
-a: 29
-Setting value to 35
-a: 35
-b: GETTER FOR b
-Setting value to 36
-SETTER FOR b
-b: GETTER FOR b
-f: GETTER FOR f
-Setting value to 37
-SETTER FOR f
-f: GETTER FOR f
-g: 33
-Setting value to 38
-g: 38
-Defining accessors for d
-d: GETTER
-Setting value to 39
-SETTER
-d: GETTER
-Testing getOwnPropertyNames enumeration
-a: 35
-Setting value to 40
-a: 40
-b: GETTER FOR b
-Setting value to 41
-SETTER FOR b
-b: GETTER FOR b
-e: 31
-Setting value to 42
-e: 42
-f: GETTER FOR f
-Setting value to 43
-SETTER FOR f
-f: GETTER FOR f
-g: 38
-Setting value to 44
-g: 44
-d: GETTER
-Setting value to 45
-SETTER
-d: GETTER
-Defining data property during enumeration...
-Testing for-in enumeration
-a: 40
-Setting value to 47
-a: 47
-b: GETTER FOR b
-Setting value to 49
-SETTER FOR b
-b: GETTER FOR b
-f: GETTER FOR f
-Setting value to 51
-SETTER FOR f
-f: GETTER FOR f
-g: 44
-Setting value to 53
-g: 53
-Defining data property d with value 54
-d: 54
-Setting value to 55
-d: 55
-Testing getOwnPropertyNames enumeration
-a: 47
-Setting value to 57
-a: 57
-b: GETTER FOR b
-Setting value to 59
-SETTER FOR b
-b: GETTER FOR b
-e: 42
-Setting value to 61
-e: 61
-f: GETTER FOR f
-Setting value to 63
-SETTER FOR f
-f: GETTER FOR f
-g: 53
-Setting value to 65
-g: 65
-d: 55
-Setting value to 67
-d: 67
Modified: trunk/JSTests/ChakraCore.yaml (264573 => 264574)
--- trunk/JSTests/ChakraCore.yaml 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/JSTests/ChakraCore.yaml 2020-07-18 23:09:56 UTC (rev 264574)
@@ -1474,7 +1474,7 @@
- path: ChakraCore/test/es5/regexSpace.js
cmd: runChakra :baseline, "NoException", "regexSpace.baseline", []
- path: ChakraCore/test/es5/EnumeratingWithES5.js
- cmd: runChakra :baseline, "NoException", "EnumeratingWithES5.baseline-jsc", []
+ cmd: runChakra :baseline, "NoException", "EnumeratingWithES5.baseline", []
- path: ChakraCore/test/es5/InsufficientArguments.js
cmd: runChakra :baseline, "NoException", "InsufficientArguments.baseline", []
- path: ChakraCore/test/es5/recursivesetter.js
Modified: trunk/JSTests/ChangeLog (264573 => 264574)
--- trunk/JSTests/ChangeLog 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/JSTests/ChangeLog 2020-07-18 23:09:56 UTC (rev 264574)
@@ -1,3 +1,24 @@
+2020-07-18 Alexey Shvayka <[email protected]>
+
+ Redefining a property should not change its insertion index (Object.keys order)
+ https://bugs.webkit.org/show_bug.cgi?id=142933
+
+ Reviewed by Saam Barati.
+
+ * ChakraCore.yaml:
+ * ChakraCore/test/es5/EnumeratingWithES5.baseline-jsc: Removed.
+ * microbenchmarks/redefine-property-accessor-dictionary.js: Added.
+ * microbenchmarks/redefine-property-accessor.js: Added.
+ * microbenchmarks/redefine-property-data-dictionary.js: Added.
+ * microbenchmarks/redefine-property-data.js: Added.
+ * stress/define-own-indexed-property-fast-path.js:
+ * stress/redefine-property-enumerable.js: Added.
+ * stress/redefine-property-get.js: Added.
+ * stress/redefine-property-set.js: Added.
+ * stress/redefine-property-value.js: Added.
+ * stress/redefine-property-writable.js: Added.
+ * test262/expectations.yaml: Mark 12 test cases as passing.
+
2020-07-17 Xan Lopez <[email protected]>
Math.max() can yield the wrong result for max(0, -0).
Added: trunk/JSTests/microbenchmarks/redefine-property-accessor-dictionary.js (0 => 264574)
--- trunk/JSTests/microbenchmarks/redefine-property-accessor-dictionary.js (rev 0)
+++ trunk/JSTests/microbenchmarks/redefine-property-accessor-dictionary.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,24 @@
+//@ skip if $memoryLimited and ["arm", "mips"].include?($architecture)
+const obj = {};
+for (let i = 0; i < 100; ++i)
+ obj["k" + i] = i;
+
+function getter1() {}
+function getter2() {}
+function setter1(v) {}
+function setter2(v) {}
+
+const descGet1 = {get: getter1, configurable: true};
+const descGet1Set1 = {get: getter1, set: setter1};
+const descGet2Set1 = {get: getter2, set: setter1};
+const descGet1Set2 = {get: getter1, set: setter2};
+const descGet2Set2 = {get: getter2, set: setter2};
+
+for (let i = 0; i < 1e4; ++i) {
+ const key = "k" + (i % 100);
+ Object.defineProperty(obj, key, descGet1);
+ Object.defineProperty(obj, key, descGet1Set1);
+ Object.defineProperty(obj, key, descGet2Set1);
+ Object.defineProperty(obj, key, descGet1Set2);
+ Object.defineProperty(obj, key, descGet2Set2);
+}
Added: trunk/JSTests/microbenchmarks/redefine-property-accessor.js (0 => 264574)
--- trunk/JSTests/microbenchmarks/redefine-property-accessor.js (rev 0)
+++ trunk/JSTests/microbenchmarks/redefine-property-accessor.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,19 @@
+function getter1() {}
+function getter2() {}
+function setter1(v) {}
+function setter2(v) {}
+
+const obj = {a: 0, b: 1, get foo() {}, c: 3, d: 4};
+const descGet1 = {get: getter1, configurable: true};
+const descGet1Set1 = {get: getter1, set: setter1};
+const descGet2Set1 = {get: getter2, set: setter1};
+const descGet1Set2 = {get: getter1, set: setter2};
+const descGet2Set2 = {get: getter2, set: setter2};
+
+for (let i = 0; i < 1e4; ++i) {
+ Object.defineProperty(obj, "foo", descGet1);
+ Object.defineProperty(obj, "foo", descGet1Set1);
+ Object.defineProperty(obj, "foo", descGet2Set1);
+ Object.defineProperty(obj, "foo", descGet1Set2);
+ Object.defineProperty(obj, "foo", descGet2Set2);
+}
Added: trunk/JSTests/microbenchmarks/redefine-property-data-dictionary.js (0 => 264574)
--- trunk/JSTests/microbenchmarks/redefine-property-data-dictionary.js (rev 0)
+++ trunk/JSTests/microbenchmarks/redefine-property-data-dictionary.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,18 @@
+//@ skip if $memoryLimited and ["arm", "mips"].include?($architecture)
+const obj = {};
+for (let i = 0; i < 100; ++i)
+ obj["k" + i] = i;
+
+const descNonWritable = {writable: false};
+const descNonEnumerable = {enumerable: false};
+const descWritable = {writable: true};
+const descEnumerable = {enumerable: true};
+
+for (let i = 0; i < 1e4; ++i) {
+ const key = "k" + (i % 100);
+ Object.defineProperty(obj, key, descNonWritable);
+ Object.defineProperty(obj, key, descNonEnumerable);
+ Object.defineProperty(obj, key, descWritable);
+ Object.defineProperty(obj, key, descEnumerable);
+ Object.defineProperty(obj, key, {value: i});
+}
Added: trunk/JSTests/microbenchmarks/redefine-property-data.js (0 => 264574)
--- trunk/JSTests/microbenchmarks/redefine-property-data.js (rev 0)
+++ trunk/JSTests/microbenchmarks/redefine-property-data.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,13 @@
+const obj = {a: 0, b: 1, foo: 2, c: 3, d: 4};
+const descNonWritable = {writable: false};
+const descNonEnumerable = {enumerable: false};
+const descWritable = {writable: true};
+const descEnumerable = {enumerable: true};
+
+for (let i = 0; i < 1e4; ++i) {
+ Object.defineProperty(obj, "foo", descNonWritable);
+ Object.defineProperty(obj, "foo", descNonEnumerable);
+ Object.defineProperty(obj, "foo", descWritable);
+ Object.defineProperty(obj, "foo", descEnumerable);
+ Object.defineProperty(obj, "foo", {value: i});
+}
Modified: trunk/JSTests/stress/define-own-indexed-property-fast-path.js (264573 => 264574)
--- trunk/JSTests/stress/define-own-indexed-property-fast-path.js 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/JSTests/stress/define-own-indexed-property-fast-path.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -1,19 +1,11 @@
function assertSameDescriptor(a, b) {
- const aString = stringifyDescriptor(a);
- const bString = stringifyDescriptor(b);
+ const aString = JSON.stringify(a, null, 2);
+ const bString = JSON.stringify(b, null, 2);
if (aString !== bString)
throw new Error(`Bad descriptor!\nActual: ${aString}\nExpected: ${bString}`);
}
-function stringifyDescriptor(descriptor) {
- const sortedDescriptor = {};
- for (const key of Object.keys(descriptor).sort())
- sortedDescriptor[key] = descriptor[key];
-
- return JSON.stringify(sortedDescriptor, null, 2);
-}
-
function getCombinations(options) {
const optionKeys = Object.keys(options);
const results = [];
Added: trunk/JSTests/stress/redefine-property-enumerable.js (0 => 264574)
--- trunk/JSTests/stress/redefine-property-enumerable.js (rev 0)
+++ trunk/JSTests/stress/redefine-property-enumerable.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,73 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error("bad value: " + actual);
+}
+
+function makeDictionary(dict) {
+ for (let i = 0; i < 1000; ++i)
+ dict["k" + i] = i;
+ return dict;
+}
+
+function makePolyProtoObject() {
+ function foo() {
+ class C {
+ constructor() {
+ this._field = 42;
+ }
+ }
+ return new C();
+ }
+ for (let i = 0; i < 15; ++i)
+ foo();
+ return foo();
+}
+
+function test(proto, key1, key2) {
+ proto[key1] = 1;
+ proto[key2] = 2;
+ Object.defineProperty(proto, key2, {enumerable: false});
+
+ const child = Object.create(proto);
+
+ function isKey1Enumerable(obj) {
+ for (let key in obj) {
+ if (key == key1) return true;
+ }
+ return false;
+ }
+
+ function isKey2Enumerable(obj) {
+ for (let key in obj) {
+ if (key == key2) return true;
+ }
+ return false;
+ }
+
+ noInline(isKey1Enumerable);
+ noInline(isKey2Enumerable);
+
+ for (let i = 0; i < 20; ++i) {
+ shouldBe(isKey1Enumerable(child), true);
+ shouldBe(isKey1Enumerable(proto), true);
+ shouldBe(isKey2Enumerable(child), false);
+ shouldBe(isKey2Enumerable(proto), false);
+ }
+
+ Object.defineProperty(proto, key1, {enumerable: false});
+ shouldBe(isKey1Enumerable(child), false);
+ shouldBe(isKey1Enumerable(proto), false);
+
+ Object.defineProperty(proto, key2, {enumerable: true});
+ shouldBe(isKey2Enumerable(child), true);
+ shouldBe(isKey2Enumerable(proto), true);
+}
+
+test({}, 0, "bar");
+test(Object.create(null), "__proto__", 1);
+
+test(makeDictionary({}), "foo", 2);
+test(makeDictionary(Object.create(null)), 3, "__proto__");
+
+test(makePolyProtoObject(), "foo", "bar");
+test(makePolyProtoObject(), 4, 5);
Added: trunk/JSTests/stress/redefine-property-get.js (0 => 264574)
--- trunk/JSTests/stress/redefine-property-get.js (rev 0)
+++ trunk/JSTests/stress/redefine-property-get.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,73 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error("bad value: " + actual);
+}
+
+function makeDictionary(dict) {
+ for (let i = 0; i < 1000; ++i)
+ dict["k" + i] = i;
+ return dict;
+}
+
+function makePolyProtoObject() {
+ function foo() {
+ class C {
+ constructor() {
+ this._field = 42;
+ }
+ }
+ return new C();
+ }
+ for (let i = 0; i < 15; ++i)
+ foo();
+ return foo();
+}
+
+function test(proto, key1, key2) {
+ Object.defineProperty(proto, key1, {
+ get: () => 1,
+ configurable: true,
+ });
+
+ Object.defineProperty(proto, key2, {
+ get: () => 2,
+ configurable: true,
+ });
+
+ const child = Object.create(proto);
+
+ function getKey1(obj) {
+ return obj[key1];
+ }
+
+ function getKey2(obj) {
+ return obj[key2];
+ }
+
+ noInline(getKey1);
+ noInline(getKey2);
+
+ for (let i = 0; i < 1e4; ++i) {
+ shouldBe(getKey1(child), 1);
+ shouldBe(getKey1(proto), 1);
+ shouldBe(getKey2(child), 2);
+ shouldBe(getKey2(proto), 2);
+ }
+
+ Object.defineProperty(proto, key1, {get() {}});
+ shouldBe(getKey1(child), undefined);
+ shouldBe(getKey1(proto), undefined);
+
+ Object.defineProperty(proto, key2, {get: undefined});
+ shouldBe(getKey2(child), undefined);
+ shouldBe(getKey2(proto), undefined);
+}
+
+test({}, 0, "bar");
+test(Object.create(null), "__proto__", 1);
+
+test(makeDictionary({}), "foo", 2);
+test(makeDictionary(Object.create(null)), 3, "__proto__");
+
+test(makePolyProtoObject(), "foo", "bar");
+test(makePolyProtoObject(), 4, 5);
Added: trunk/JSTests/stress/redefine-property-set.js (0 => 264574)
--- trunk/JSTests/stress/redefine-property-set.js (rev 0)
+++ trunk/JSTests/stress/redefine-property-set.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,79 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error("bad value: " + actual);
+}
+
+function makeDictionary(dict) {
+ for (let i = 0; i < 1000; ++i)
+ dict["k" + i] = i;
+ return dict;
+}
+
+function makePolyProtoObject() {
+ function foo() {
+ class C {
+ constructor() {
+ this._field = 42;
+ }
+ }
+ return new C();
+ }
+ for (let i = 0; i < 15; ++i)
+ foo();
+ return foo();
+}
+
+function test(proto, key1, key2) {
+ let nextValue = 10;
+
+ let value1;
+ Object.defineProperty(proto, key1, {
+ set(val) { value1 = val; },
+ configurable: true,
+ });
+
+ let value2;
+ Object.defineProperty(proto, key2, {
+ set(val) { value2 = val; },
+ configurable: true,
+ });
+
+ const child = Object.create(proto);
+
+ function setKey1(obj) {
+ obj[key1] = ++nextValue;
+ return value1 === nextValue;
+ }
+
+ function setKey2(obj) {
+ obj[key2] = ++nextValue;
+ return value2 === nextValue;
+ }
+
+ noInline(setKey1);
+ noInline(setKey2);
+
+ for (let i = 0; i < 1e4; ++i) {
+ shouldBe(setKey1(child), true);
+ shouldBe(setKey1(proto), true);
+ shouldBe(setKey2(child), true);
+ shouldBe(setKey2(proto), true);
+ }
+
+ Object.defineProperty(proto, key1, {set: undefined});
+ shouldBe(setKey1(child), false);
+ shouldBe(setKey1(proto), false);
+
+ Object.defineProperty(proto, key2, {set() {}});
+ shouldBe(setKey2(child), false);
+ shouldBe(setKey2(proto), false);
+}
+
+test({}, 0, "bar");
+test(Object.create(null), "__proto__", 1);
+
+test(makeDictionary({}), "foo", 2);
+test(makeDictionary(Object.create(null)), 3, "__proto__");
+
+test(makePolyProtoObject(), "foo", "bar");
+test(makePolyProtoObject(), 4, 5);
Added: trunk/JSTests/stress/redefine-property-value.js (0 => 264574)
--- trunk/JSTests/stress/redefine-property-value.js (rev 0)
+++ trunk/JSTests/stress/redefine-property-value.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,66 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error("bad value: " + actual);
+}
+
+function makeDictionary(dict) {
+ for (let i = 0; i < 1000; ++i)
+ dict["k" + i] = i;
+ return dict;
+}
+
+function makePolyProtoObject() {
+ function foo() {
+ class C {
+ constructor() {
+ this._field = 42;
+ }
+ }
+ return new C();
+ }
+ for (let i = 0; i < 15; ++i)
+ foo();
+ return foo();
+}
+
+function test(proto, key1, key2) {
+ proto[key1] = 1;
+ proto[key2] = 2;
+
+ const child = Object.create(proto);
+
+ function getKey1(obj) {
+ return obj[key1];
+ }
+
+ function getKey2(obj) {
+ return obj[key2];
+ }
+
+ noInline(getKey1);
+ noInline(getKey2);
+
+ for (let i = 0; i < 1e4; ++i) {
+ shouldBe(getKey1(child), 1);
+ shouldBe(getKey1(proto), 1);
+ shouldBe(getKey2(child), 2);
+ shouldBe(getKey2(proto), 2);
+ }
+
+ Object.defineProperty(proto, key1, {value: 3});
+ shouldBe(getKey1(child), 3);
+ shouldBe(getKey1(proto), 3);
+
+ Object.defineProperty(proto, key2, {set() {}});
+ shouldBe(getKey2(child), undefined);
+ shouldBe(getKey2(proto), undefined);
+}
+
+test({}, 0, "bar");
+test(Object.create(null), "__proto__", 1);
+
+test(makeDictionary({}), "foo", 2);
+test(makeDictionary(Object.create(null)), 3, "__proto__");
+
+test(makePolyProtoObject(), "foo", "bar");
+test(makePolyProtoObject(), 4, 5);
Added: trunk/JSTests/stress/redefine-property-writable.js (0 => 264574)
--- trunk/JSTests/stress/redefine-property-writable.js (rev 0)
+++ trunk/JSTests/stress/redefine-property-writable.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -0,0 +1,95 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error("bad value: " + actual);
+}
+
+function makeDictionary(dict) {
+ for (let i = 0; i < 1000; ++i)
+ dict["k" + i] = i;
+ return dict;
+}
+
+function makePolyProtoObject() {
+ function foo() {
+ class C {
+ constructor() {
+ this._field = 42;
+ }
+ }
+ return new C();
+ }
+ for (let i = 0; i < 15; ++i)
+ foo();
+ return foo();
+}
+
+function test(proto, key1, key2) {
+ proto[key1] = 1;
+ proto[key2] = 2;
+ Object.defineProperty(proto, key2, {writable: false});
+
+ const child = Object.create(proto);
+ let nextValue = 10;
+
+ function isKey1WritableStrict(obj) {
+ "use strict";
+ try {
+ obj[key1] = ++nextValue;
+ } catch {
+ return false;
+ }
+ return true;
+ }
+
+ function isKey1Writable(obj) {
+ obj[key1] = ++nextValue;
+ return obj[key1] === nextValue;
+ }
+
+ function isKey2WritableStrict(obj) {
+ "use strict";
+ try {
+ obj[key2] = ++nextValue;
+ } catch {
+ return false;
+ }
+ return true;
+ }
+
+ function isKey2Writable(obj) {
+ obj[key2] = ++nextValue;
+ return obj[key2] === nextValue;
+ }
+
+ noInline(isKey1WritableStrict);
+ noInline(isKey1Writable);
+ noInline(isKey2WritableStrict);
+ noInline(isKey2Writable);
+
+ for (let i = 0; i < 1e4; ++i) {
+ shouldBe(isKey1WritableStrict(child), true);
+ shouldBe(isKey1Writable(proto), true);
+ shouldBe(isKey2WritableStrict(child), false);
+ shouldBe(isKey2Writable(proto), false);
+
+ delete child[key1];
+ delete child[key2];
+ }
+
+ Object.defineProperty(proto, key1, {writable: false});
+ shouldBe(isKey1WritableStrict(child), false);
+ shouldBe(isKey1Writable(proto), false);
+
+ Object.defineProperty(proto, key2, {writable: true});
+ shouldBe(isKey2WritableStrict(child), true);
+ shouldBe(isKey2Writable(proto), true);
+}
+
+test({}, 0, "bar");
+test(Object.create(null), "__proto__", 1);
+
+test(makeDictionary({}), "foo", 2);
+test(makeDictionary(Object.create(null)), 3, "__proto__");
+
+test(makePolyProtoObject(), "foo", "bar");
+test(makePolyProtoObject(), 4, 5);
Modified: trunk/JSTests/test262/expectations.yaml (264573 => 264574)
--- trunk/JSTests/test262/expectations.yaml 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/JSTests/test262/expectations.yaml 2020-07-18 23:09:56 UTC (rev 264574)
@@ -910,23 +910,14 @@
default: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
strict mode: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
test/built-ins/Object/entries/order-after-define-property.js:
- default: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
- strict mode: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
+ default: 'Test262Error: Expected [a, name] and [name, a] to have the same contents. '
+ strict mode: 'Test262Error: Expected [a, name] and [name, a] to have the same contents. '
test/built-ins/Object/freeze/proxy-no-ownkeys-returned-keys-order.js:
default: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
strict mode: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
-test/built-ins/Object/getOwnPropertyDescriptors/order-after-define-property.js:
- default: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '
- strict mode: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '
test/built-ins/Object/getOwnPropertyDescriptors/proxy-no-ownkeys-returned-keys-order.js:
default: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
strict mode: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
-test/built-ins/Object/getOwnPropertyNames/order-after-define-property.js:
- default: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
- strict mode: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
-test/built-ins/Object/getOwnPropertySymbols/order-after-define-property.js:
- default: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '
- strict mode: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '
test/built-ins/Object/internals/DefineOwnProperty/consistent-value-function-arguments.js:
default: 'Test262Error: Expected SameValue(«null», «[object Arguments]») to be true'
test/built-ins/Object/internals/DefineOwnProperty/consistent-value-function-caller.js:
@@ -941,14 +932,11 @@
default: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
strict mode: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
test/built-ins/Object/keys/order-after-define-property.js:
- default: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
- strict mode: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
+ default: 'Test262Error: Expected [a, length] and [length, a] to have the same contents. '
+ strict mode: 'Test262Error: Expected [a, length] and [length, a] to have the same contents. '
test/built-ins/Object/seal/proxy-no-ownkeys-returned-keys-order.js:
default: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
strict mode: 'Test262Error: Expected [0, Symbol(), foo] and [0, foo, Symbol()] to have the same contents. '
-test/built-ins/Object/values/order-after-define-property.js:
- default: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
- strict mode: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
test/built-ins/Proxy/apply/arguments-realm.js:
default: 'Test262Error: Expected SameValue(«function Array() {'
strict mode: 'Test262Error: Expected SameValue(«function Array() {'
@@ -988,9 +976,6 @@
test/built-ins/Proxy/ownKeys/trap-is-undefined-target-is-proxy.js:
default: 'Test262Error: Expected [length, foo, 0, Symbol()] and [Symbol(), length, foo, 0] to have the same contents. '
strict mode: 'Test262Error: Expected [length, foo, 0, Symbol()] and [Symbol(), length, foo, 0] to have the same contents. '
-test/built-ins/Reflect/ownKeys/order-after-define-property.js:
- default: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '
- strict mode: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '
test/built-ins/RegExp/property-escapes/generated/Alphabetic.js:
default: 'Test262Error: `\p{Alphabetic}` should match U+001CFA (`ᳺ`)'
strict mode: 'Test262Error: `\p{Alphabetic}` should match U+001CFA (`ᳺ`)'
@@ -3204,9 +3189,6 @@
default: "SyntaxError: Cannot use the keyword 'in' as a lexical variable name."
test/language/statements/for-in/let-array-with-newline.js:
default: 'Test262: This statement should not be evaluated.'
-test/language/statements/for-in/order-after-define-property.js:
- default: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
- strict mode: 'Test262Error: Expected [b, a] and [a, b] to have the same contents. '
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. '
Modified: trunk/LayoutTests/ChangeLog (264573 => 264574)
--- trunk/LayoutTests/ChangeLog 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/LayoutTests/ChangeLog 2020-07-18 23:09:56 UTC (rev 264574)
@@ -1,3 +1,13 @@
+2020-07-18 Alexey Shvayka <[email protected]>
+
+ Redefining a property should not change its insertion index (Object.keys order)
+ https://bugs.webkit.org/show_bug.cgi?id=142933
+
+ Reviewed by Saam Barati.
+
+ * js/object-literal-duplicate-properties-expected.txt:
+ * js/script-tests/object-literal-duplicate-properties.js:
+
2020-07-18 Chris Dumez <[email protected]>
Unreviewed, skip a few css WPT tests that are crashing in Debug since the recent WPT resync.
Modified: trunk/LayoutTests/js/object-literal-duplicate-properties-expected.txt (264573 => 264574)
--- trunk/LayoutTests/js/object-literal-duplicate-properties-expected.txt 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/LayoutTests/js/object-literal-duplicate-properties-expected.txt 2020-07-18 23:09:56 UTC (rev 264574)
@@ -14,7 +14,7 @@
PASS x is 4
PASS o.foo is 3
PASS o.bar is undefined
-FAIL Object.keys(o).join() should be foo,test1,bar,test2,test3,nest. Was test1,test2,test3,foo,bar,nest.
+PASS Object.keys(o).join() is 'foo,test1,bar,test2,test3,nest'
Basic
PASS o = {foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
Modified: trunk/LayoutTests/js/script-tests/object-literal-duplicate-properties.js (264573 => 264574)
--- trunk/LayoutTests/js/script-tests/object-literal-duplicate-properties.js 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/LayoutTests/js/script-tests/object-literal-duplicate-properties.js 2020-07-18 23:09:56 UTC (rev 264574)
@@ -9,7 +9,7 @@
shouldBe("x", "1");
shouldBe("o.foo", "'getter'");
-// Ensure getters/setters get eliminated by computed properties.
+// Ensure getters/setters get eliminated by basic properties.
x = 0;
o = {
get foo() {return "getter"},
@@ -54,7 +54,6 @@
shouldBe("x", "4");
shouldBe("o.foo", "3");
shouldBe("o.bar", "undefined");
-// FIXME: <https://webkit.org/b/142933> Redefining a property should not change its insertion index (Object.keys order)
shouldBe("Object.keys(o).join()", "'foo,test1,bar,test2,test3,nest'");
Modified: trunk/Source/_javascript_Core/ChangeLog (264573 => 264574)
--- trunk/Source/_javascript_Core/ChangeLog 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/Source/_javascript_Core/ChangeLog 2020-07-18 23:09:56 UTC (rev 264574)
@@ -1,3 +1,39 @@
+2020-07-18 Alexey Shvayka <[email protected]>
+
+ Redefining a property should not change its insertion index (Object.keys order)
+ https://bugs.webkit.org/show_bug.cgi?id=142933
+
+ Reviewed by Saam Barati.
+
+ Before this change, JSC used to delete & put back a non-indexed property just to
+ update attributes, which was less efficient and corrupted observable property order.
+
+ This patch:
+ 1. Rewrites validateAndApplyPropertyDescriptor() to closely resemble the spec [1].
+ 2. Drops property deletion, inlines putDescriptor(), and sets necessary Structure
+ flags in attributeChangeTransition().
+ 3. Simplifies validateAndApplyPropertyDescriptor() a bit by obtaining GetterSetter
+ instance from current descriptor rather then calling getDirect().
+
+ This change aligns property order with V8 and SpiderMonkey, advancing provided
+ microbenchmarks by 5-85% (especially for objects in dictionary mode).
+ SixSpeed, SunSpider, and ARES-6 are all neutral.
+
+ [1]: https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
+
+ * runtime/JSObject.cpp:
+ (JSC::validateAndApplyPropertyDescriptor):
+ (JSC::JSObject::defineOwnNonIndexProperty):
+ (JSC::putDescriptor): Deleted.
+ * runtime/JSObjectInlines.h:
+ (JSC::JSObject::putDirectInternal):
+ * runtime/PropertyDescriptor.cpp:
+ (JSC::PropertyDescriptor::slowGetterSetter const):
+ (JSC::PropertyDescriptor::slowGetterSetter): Deleted.
+ * runtime/PropertyDescriptor.h:
+ * runtime/Structure.cpp:
+ (JSC::Structure::attributeChangeTransition):
+
2020-07-17 Yusuke Suzuki <[email protected]>
[JSC] Clean up resolveLocale
Modified: trunk/Source/_javascript_Core/runtime/JSObject.cpp (264573 => 264574)
--- trunk/Source/_javascript_Core/runtime/JSObject.cpp 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/Source/_javascript_Core/runtime/JSObject.cpp 2020-07-18 23:09:56 UTC (rev 264574)
@@ -3551,41 +3551,6 @@
return true;
}
-static bool putDescriptor(JSGlobalObject* globalObject, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
-{
- VM& vm = globalObject->vm();
- if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
- if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
- JSObject* getter = oldDescriptor.getterPresent() ? oldDescriptor.getterObject() : nullptr;
- JSObject* setter = oldDescriptor.setterPresent() ? oldDescriptor.setterObject() : nullptr;
- GetterSetter* accessor = GetterSetter::create(vm, globalObject, getter, setter);
- target->putDirectAccessor(globalObject, propertyName, accessor, attributes | PropertyAttribute::Accessor);
- return true;
- }
- JSValue newValue = jsUndefined();
- if (descriptor.value())
- newValue = descriptor.value();
- else if (oldDescriptor.value())
- newValue = oldDescriptor.value();
- target->putDirect(vm, propertyName, newValue, attributes & ~PropertyAttribute::Accessor);
- if (attributes & PropertyAttribute::ReadOnly)
- target->structure(vm)->setContainsReadOnlyProperties();
- return true;
- }
- attributes &= ~PropertyAttribute::ReadOnly;
-
- JSObject* getter = descriptor.getterPresent()
- ? descriptor.getterObject() : oldDescriptor.getterPresent()
- ? oldDescriptor.getterObject() : nullptr;
- JSObject* setter = descriptor.setterPresent()
- ? descriptor.setterObject() : oldDescriptor.setterPresent()
- ? oldDescriptor.setterObject() : nullptr;
- GetterSetter* accessor = GetterSetter::create(vm, globalObject, getter, setter);
-
- target->putDirectAccessor(globalObject, propertyName, accessor, attributes | PropertyAttribute::Accessor);
- return true;
-}
-
bool JSObject::putDirectMayBeIndex(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value)
{
if (Optional<uint32_t> index = parseIndex(propertyName))
@@ -3593,8 +3558,7 @@
return putDirect(globalObject->vm(), propertyName, value);
}
-// 9.1.6.3 of the spec
-// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-validateandapplypropertydescriptor
+// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
bool validateAndApplyPropertyDescriptor(JSGlobalObject* globalObject, JSObject* object, PropertyName propertyName, bool isExtensible,
const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException)
{
@@ -3608,25 +3572,30 @@
// Step 2.a
if (!isExtensible)
return typeError(globalObject, scope, throwException, NonExtensibleObjectPropertyDefineError);
- if (!object)
- return true;
- // Step 2.c/d
- PropertyDescriptor oldDescriptor;
- oldDescriptor.setValue(jsUndefined());
- // FIXME: spec says to always return true here.
- return putDescriptor(globalObject, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
+
+ if (object) {
+ if (descriptor.isAccessorDescriptor()) {
+ unsigned attributes = (descriptor.attributes() | PropertyAttribute::Accessor) & ~PropertyAttribute::ReadOnly;
+ object->putDirectAccessor(globalObject, propertyName, descriptor.slowGetterSetter(globalObject), attributes);
+ } else {
+ ASSERT(descriptor.isGenericDescriptor() || descriptor.isDataDescriptor());
+ JSValue value = descriptor.value() ? descriptor.value() : jsUndefined();
+ object->putDirect(vm, propertyName, value, descriptor.attributes() & ~PropertyAttribute::Accessor);
+ }
+ }
+
+ return true;
}
// Step 3.
if (descriptor.isEmpty())
return true;
- // Step 4.
+
bool isEqual = current.equalTo(globalObject, descriptor);
RETURN_IF_EXCEPTION(scope, false);
if (isEqual)
return true;
- // Step 5.
- // Filter out invalid changes
+ // Step 4.
if (!current.configurable()) {
if (descriptor.configurable())
return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
@@ -3633,108 +3602,58 @@
if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable())
return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
}
-
- // Step 6.
- // A generic descriptor is simply changing the attributes of an existing property
+
if (descriptor.isGenericDescriptor()) {
- if (!current.attributesEqual(descriptor) && object) {
- JSCell::deleteProperty(object, globalObject, propertyName);
- RETURN_IF_EXCEPTION(scope, false);
- return putDescriptor(globalObject, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
- }
- return true;
- }
-
- // Step 7.
- // Changing between a normal property or an accessor property
- if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
+ // Step 5.
+ // Changing [[Enumerable]] and [[Configurable]] attributes of an existing property
+ } else if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
+ // Step 6.
+ // Changing between a data property and accessor property
if (!current.configurable())
return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
+ } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
+ // Step 7.
+ // Changing the value and attributes of an existing data property
+ if (!current.configurable() && !current.writable()) {
+ if (descriptor.writable())
+ return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
+ if (descriptor.value() && !sameValue(globalObject, current.value(), descriptor.value()))
+ return typeError(globalObject, scope, throwException, ReadonlyPropertyChangeError);
- if (!object)
return true;
+ }
+ } else {
+ // Step 8.
+ // Changing the accessor functions and attributes of an existing accessor property
+ ASSERT(descriptor.isAccessorDescriptor());
+ if (!current.configurable()) {
+ if (descriptor.setterPresent() && descriptor.setter() != current.setter())
+ return typeError(globalObject, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
+ if (descriptor.getterPresent() && descriptor.getter() != current.getter())
+ return typeError(globalObject, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
+ if (current.attributes() & PropertyAttribute::CustomAccessor)
+ return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
- JSCell::deleteProperty(object, globalObject, propertyName);
- RETURN_IF_EXCEPTION(scope, false);
- return putDescriptor(globalObject, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
- }
-
- // Step 8.
- // Changing the value and attributes of an existing property
- if (descriptor.isDataDescriptor()) {
- if (!current.configurable()) {
- if (!current.writable() && descriptor.writable())
- return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
- if (!current.writable()) {
- if (descriptor.value()) {
- bool isSame = sameValue(globalObject, current.value(), descriptor.value());
- RETURN_IF_EXCEPTION(scope, false);
- if (!isSame)
- return typeError(globalObject, scope, throwException, ReadonlyPropertyChangeError);
- }
- }
+ return true;
}
- if (current.attributesEqual(descriptor) && !descriptor.value())
- return true;
- if (!object)
- return true;
- JSCell::deleteProperty(object, globalObject, propertyName);
- RETURN_IF_EXCEPTION(scope, false);
- return putDescriptor(globalObject, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
}
- // Step 9.
- // Changing the accessor functions of an existing accessor property
- ASSERT(descriptor.isAccessorDescriptor());
- if (!current.configurable()) {
- if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(globalObject, current.setter(), descriptor.setter())))
- return typeError(globalObject, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
- if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(globalObject, current.getter(), descriptor.getter())))
- return typeError(globalObject, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
- if (current.attributes() & PropertyAttribute::CustomAccessor)
- return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
- }
-
- // Step 10/11.
if (!object)
return true;
- JSValue accessor = object->getDirect(vm, propertyName);
- if (!accessor)
- return false;
- JSObject* getter = nullptr;
- JSObject* setter = nullptr;
- bool getterSetterChanged = false;
-
- if (accessor.isCustomGetterSetter()) {
- auto* customGetterSetter = jsCast<CustomGetterSetter*>(accessor);
- if (customGetterSetter->setter())
- setter = getCustomGetterSetterFunctionForGetterSetter(globalObject, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Setter);
- if (customGetterSetter->getter())
- getter = getCustomGetterSetterFunctionForGetterSetter(globalObject, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Getter);
+ // Step 9.
+ unsigned attributes = descriptor.attributesOverridingCurrent(current);
+ if (descriptor.isAccessorDescriptor() || (current.isAccessorDescriptor() && !descriptor.isDataDescriptor())) {
+ ASSERT(attributes & PropertyAttribute::Accessor);
+ JSObject* getter = descriptor.getterPresent() ? descriptor.getterObject() : (current.getterPresent() ? current.getterObject() : nullptr);
+ JSObject* setter = descriptor.setterPresent() ? descriptor.setterObject() : (current.setterPresent() ? current.setterObject() : nullptr);
+ GetterSetter* getterSetter = GetterSetter::create(vm, globalObject, getter, setter);
+ object->putDirectAccessor(globalObject, propertyName, getterSetter, attributes & ~PropertyAttribute::ReadOnly);
} else {
- ASSERT(accessor.isGetterSetter());
- auto* getterSetter = jsCast<GetterSetter*>(accessor);
- getter = getterSetter->getter();
- setter = getterSetter->setter();
+ ASSERT(descriptor.isGenericDescriptor() || descriptor.isDataDescriptor());
+ JSValue value = descriptor.value() ? descriptor.value() : (current.value() ? current.value() : jsUndefined());
+ object->putDirect(vm, propertyName, value, attributes & ~PropertyAttribute::Accessor);
}
- if (descriptor.setterPresent()) {
- setter = descriptor.setterObject();
- getterSetterChanged = true;
- }
- if (descriptor.getterPresent()) {
- getter = descriptor.getterObject();
- getterSetterChanged = true;
- }
- if (current.attributesEqual(descriptor) && !getterSetterChanged)
- return true;
-
- GetterSetter* getterSetter = GetterSetter::create(vm, globalObject, getter, setter);
-
- JSCell::deleteProperty(object, globalObject, propertyName);
- RETURN_IF_EXCEPTION(scope, false);
- unsigned attrs = descriptor.attributesOverridingCurrent(current);
- object->putDirectAccessor(globalObject, propertyName, getterSetter, attrs | PropertyAttribute::Accessor);
return true;
}
@@ -3743,11 +3662,6 @@
VM& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
- // Track on the globaldata that we're in define property.
- // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
- // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
- // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
- VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
PropertyDescriptor current;
bool isCurrentDefined = getOwnPropertyDescriptor(globalObject, propertyName, current);
RETURN_IF_EXCEPTION(throwScope, false);
Modified: trunk/Source/_javascript_Core/runtime/JSObjectInlines.h (264573 => 264574)
--- trunk/Source/_javascript_Core/runtime/JSObjectInlines.h 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/Source/_javascript_Core/runtime/JSObjectInlines.h 2020-07-18 23:09:56 UTC (rev 264574)
@@ -314,10 +314,11 @@
putDirect(vm, offset, value);
structure->didReplaceProperty(offset);
- if ((attributes & PropertyAttribute::Accessor) != (currentAttributes & PropertyAttribute::Accessor) || (attributes & PropertyAttribute::CustomAccessorOrValue) != (currentAttributes & PropertyAttribute::CustomAccessorOrValue)) {
- ASSERT(!(attributes & PropertyAttribute::ReadOnly));
+ // FIXME: Check attributes against PropertyAttribute::CustomAccessorOrValue. Changing GetterSetter should work w/o transition.
+ // https://bugs.webkit.org/show_bug.cgi?id=214342
+ if (mode == PutModeDefineOwnProperty && (attributes != currentAttributes || (attributes & PropertyAttribute::AccessorOrCustomAccessorOrValue)))
setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
- } else
+ else
slot.setExistingProperty(this, offset);
return true;
@@ -368,10 +369,11 @@
structure->didReplaceProperty(offset);
putDirect(vm, offset, value);
- if ((attributes & PropertyAttribute::Accessor) != (currentAttributes & PropertyAttribute::Accessor) || (attributes & PropertyAttribute::CustomAccessorOrValue) != (currentAttributes & PropertyAttribute::CustomAccessorOrValue)) {
- ASSERT(!(attributes & PropertyAttribute::ReadOnly));
+ // FIXME: Check attributes against PropertyAttribute::CustomAccessorOrValue. Changing GetterSetter should work w/o transition.
+ // https://bugs.webkit.org/show_bug.cgi?id=214342
+ if (mode == PutModeDefineOwnProperty && (attributes != currentAttributes || (attributes & PropertyAttribute::AccessorOrCustomAccessorOrValue)))
setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
- } else
+ else
slot.setExistingProperty(this, offset);
return true;
Modified: trunk/Source/_javascript_Core/runtime/PropertyDescriptor.cpp (264573 => 264574)
--- trunk/Source/_javascript_Core/runtime/PropertyDescriptor.cpp 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/Source/_javascript_Core/runtime/PropertyDescriptor.cpp 2020-07-18 23:09:56 UTC (rev 264574)
@@ -69,7 +69,7 @@
m_attributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete | PropertyAttribute::DontEnum;
}
-GetterSetter* PropertyDescriptor::slowGetterSetter(JSGlobalObject* globalObject)
+GetterSetter* PropertyDescriptor::slowGetterSetter(JSGlobalObject* globalObject) const
{
VM& vm = globalObject->vm();
JSValue getter = m_getter && !m_getter.isUndefined() ? jsCast<JSObject*>(m_getter) : jsUndefined();
Modified: trunk/Source/_javascript_Core/runtime/PropertyDescriptor.h (264573 => 264574)
--- trunk/Source/_javascript_Core/runtime/PropertyDescriptor.h 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/Source/_javascript_Core/runtime/PropertyDescriptor.h 2020-07-18 23:09:56 UTC (rev 264574)
@@ -57,7 +57,7 @@
JS_EXPORT_PRIVATE bool isAccessorDescriptor() const;
unsigned attributes() const { return m_attributes; }
JSValue value() const { return m_value; }
- GetterSetter* slowGetterSetter(JSGlobalObject*); // Be aware that this will lazily allocate a GetterSetter object. It's much better to use getter() and setter() individually if possible.
+ GetterSetter* slowGetterSetter(JSGlobalObject*) const; // Be aware that this will lazily allocate a GetterSetter object. It's much better to use getter() and setter() individually if possible.
JS_EXPORT_PRIVATE JSValue getter() const;
JS_EXPORT_PRIVATE JSValue setter() const;
JSObject* getterObject() const;
Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (264573 => 264574)
--- trunk/Source/_javascript_Core/runtime/Structure.cpp 2020-07-18 22:53:42 UTC (rev 264573)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp 2020-07-18 23:09:56 UTC (rev 264574)
@@ -723,6 +723,12 @@
ASSERT(entry);
entry->attributes = attributes;
+ if (attributes & PropertyAttribute::ReadOnly)
+ structure->setContainsReadOnlyProperties();
+
+ if (attributes & PropertyAttribute::DontEnum)
+ structure->setIsQuickPropertyAccessAllowedForEnumeration(false);
+
structure->checkOffsetConsistency();
return structure;
}