Diff
Modified: trunk/JSTests/ChangeLog (272882 => 272883)
--- trunk/JSTests/ChangeLog 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/JSTests/ChangeLog 2021-02-15 22:40:26 UTC (rev 272883)
@@ -1,3 +1,23 @@
+2021-02-15 Caio Lima <ticaiol...@gmail.com>
+
+ [ESNext] Implement private accessors
+ https://bugs.webkit.org/show_bug.cgi?id=194435
+
+ Reviewed by Yusuke Suzuki.
+
+ * stress/private-accesor-duplicate-name-early-errors.js: Added.
+ * stress/private-getter-brand-check.js: Added.
+ * stress/private-getter-inner-class.js: Added.
+ * stress/private-members-get-and-set.js: Added.
+ * stress/private-methods-and-accessors-postfix-node.js: Added.
+ * stress/private-methods-and-accessors-prefix-node.js: Added.
+ * stress/private-names-available-on-direct-eval.js:
+ * stress/private-names-available-on-eval-during-field-initialization.js: Copied from JSTests/stress/private-names-available-on-direct-eval.js.
+ * stress/private-setter-brand-check.js: Added.
+ * stress/private-setter-inner-class.js: Added.
+ * test262/config.yaml:
+ * test262/expectations.yaml:
+
2021-02-14 Michael Catanzaro <mcatanz...@gnome.org>
JSC stress test stress/copy-data-properties-fast-path.js.default fails on s390x and ppc64le
Added: trunk/JSTests/stress/private-accesor-duplicate-name-early-errors.js (0 => 272883)
--- trunk/JSTests/stress/private-accesor-duplicate-name-early-errors.js (rev 0)
+++ trunk/JSTests/stress/private-accesor-duplicate-name-early-errors.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,92 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+}
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ set #m(v) { this._v = v; }
+ set #m(u) {}
+ }
+ `);
+});
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ #m;
+ set #m(v) { this._v = v; }
+ }
+ `);
+});
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ set #m(v) { this._v = v; }
+ #m;
+ }
+ `);
+});
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ set #m(v) { this._v = v; }
+ #m() {}
+ }
+ `);
+});
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ #m() {}
+ get #m() {}
+ }
+ `);
+});
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ get #m() {}
+ get #m() {}
+ }
+ `);
+});
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ get #m() {}
+ set #m() {}
+ #m;
+ }
+ `);
+});
+
+assert.throws(SyntaxError, function() {
+ eval(`
+ class C {
+ get #m() {}
+ set #m() {}
+ #m() {}
+ }
+ `);
+});
+
Added: trunk/JSTests/stress/private-getter-brand-check.js (0 => 272883)
--- trunk/JSTests/stress/private-getter-brand-check.js (rev 0)
+++ trunk/JSTests/stress/private-getter-brand-check.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,91 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+};
+
+(function () {
+ let createAndInstantiateClass = function () {
+ class C {
+ get #m() { return 'test'; }
+
+ access(o) {
+ return o.#m;
+ }
+ }
+
+ return new C();
+ }
+
+ let c1 = createAndInstantiateClass();
+ let c2 = createAndInstantiateClass();
+
+ assert.sameValue(c1.access(c1), 'test');
+ assert.sameValue(c2.access(c2), 'test');
+
+ assert.throws(TypeError, function() {
+ c1.access(c2);
+ });
+
+ assert.throws(TypeError, function() {
+ c2.access(c1);
+ });
+})();
+
+(function () {
+ class S {
+ get #m() { return 'super class'; }
+
+ superAccess() { return this.#m; }
+ }
+
+ class C extends S {
+ get #m() { return 'subclass'; }
+
+ access() {
+ return this.#m;
+ }
+ }
+
+ let c = new C();
+
+ assert.sameValue(c.access(), 'subclass');
+ assert.sameValue(c.superAccess(), 'super class');
+
+ let s = new S();
+ assert.sameValue(s.superAccess(), 'super class');
+ assert.throws(TypeError, function() {
+ c.access.call(s);
+ });
+})();
+
+(function () {
+ class C {
+ get #m() { return 'test'; }
+
+ access(o) {
+ return o.#m;
+ }
+ }
+
+ let c = new C();
+ assert.sameValue(c.access(c), 'test');
+
+ let o = {};
+ assert.throws(TypeError, function() {
+ c.access(o);
+ });
+})();
+
Added: trunk/JSTests/stress/private-getter-inner-class.js (0 => 272883)
--- trunk/JSTests/stress/private-getter-inner-class.js (rev 0)
+++ trunk/JSTests/stress/private-getter-inner-class.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,159 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+};
+
+(function () {
+ class C {
+ get #m() { return 'test'; }
+
+ B = class {
+ method(o) {
+ return o.#m;
+ }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+ assert.sameValue(innerB.method(c), 'test');
+})();
+
+(function () {
+ class C {
+ get #m() { return 'outer class'; }
+
+ method() { return this.#m; }
+
+ B = class {
+ method(o) {
+ return o.#m;
+ }
+
+ #m = 'test';
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+ assert.sameValue(innerB.method(innerB), 'test');
+ assert.sameValue(c.method(), 'outer class');
+ assert.throws(TypeError, function() {
+ innerB.method(c);
+ });
+})();
+
+(function () {
+ class C {
+ get #m() { return 'outer class'; }
+
+ method() { return this.#m; }
+
+ B = class {
+ method(o) {
+ return o.#m;
+ }
+
+ get #m() { return 'test'; }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+ assert.sameValue(innerB.method(innerB), 'test');
+ assert.sameValue(c.method(), 'outer class');
+ assert.throws(TypeError, function() {
+ innerB.method(c);
+ });
+})();
+
+(function () {
+ class C {
+ get #m() { throw new Error('Should never execute'); }
+
+ B = class {
+ method(o) {
+ return o.#m();
+ }
+
+ #m() { return 'test'; }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+ assert.sameValue(innerB.method(innerB), 'test');
+ assert.throws(TypeError, function() {
+ innerB.method(c);
+ });
+})();
+
+(function () {
+ class C {
+ get #m() { return 'outer class'; }
+
+ method() { return this.#m; }
+
+ B = class {
+ method(o) {
+ return o.#m;
+ }
+
+ set #m(v) { this._v = v; }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+
+ assert.throws(TypeError, function() {
+ innerB.method(innerB);
+ });
+
+ assert.sameValue(c.method(), 'outer class');
+
+ assert.throws(TypeError, function() {
+ innerB.method(c);
+ });
+})();
+
+(function () {
+ class C {
+ #m() { return 'outer class'; }
+
+ method() { return this.#m(); }
+
+ B = class {
+ method(o) {
+ return o.#m;
+ }
+
+ get #m() { return 'test262'; }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+ assert.sameValue(innerB.method(innerB), 'test262');
+ assert.sameValue(c.method(), 'outer class');
+ assert.throws(TypeError, function() {
+ innerB.method(c);
+ }, 'accessed inner class getter from an object of outer class');
+ assert.throws(TypeError, function() {
+ C.prototype.method.call(innerB);
+ });
+})();
+
Added: trunk/JSTests/stress/private-members-get-and-set.js (0 => 272883)
--- trunk/JSTests/stress/private-members-get-and-set.js (rev 0)
+++ trunk/JSTests/stress/private-members-get-and-set.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,63 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+};
+
+(function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ accessPrivateMember() {
+ return this.#m;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function () {
+ c.accessPrivateMember();
+ });
+})();
+
+(function () {
+ class C {
+ get #m() { return 'test'; }
+
+ accessPrivateMember(v) {
+ this.#m = v;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function () {
+ c.accessPrivateMember('test');
+ });
+})();
+
+(function () {
+ class C {
+ #m() { return 'test'; }
+
+ accessPrivateMember(v) {
+ this.#m = v;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function () {
+ c.accessPrivateMember('test');
+ });
+})();
+
Added: trunk/JSTests/stress/private-methods-and-accessors-postfix-node.js (0 => 272883)
--- trunk/JSTests/stress/private-methods-and-accessors-postfix-node.js (rev 0)
+++ trunk/JSTests/stress/private-methods-and-accessors-postfix-node.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,166 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+};
+
+(function() {
+ class C {
+ #method() {
+ throw new Error("Should never be called");
+ }
+
+ access() {
+ return this.#method++;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ let executedGetter = false;
+ class C {
+ get #m() {
+ executedGetter = true;
+ }
+
+ access() {
+ return this.#m++;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+
+ assert.sameValue(true, executedGetter);
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ throw new Error("Should never be executed");
+ }
+
+ access() {
+ return this.#m++;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ assert.sameValue(5, v);
+ }
+
+ get #m() {
+ return 4;
+ }
+
+ access() {
+ return this.#m++;
+ }
+ }
+
+ let c = new C();
+ assert.sameValue(4, c.access());
+})();
+
+// Ignored result
+
+(function() {
+ class C {
+ #method() {
+ throw new Error("Should never be called");
+ }
+
+ access() {
+ this.#method--;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ let executedGetter = false;
+ class C {
+ get #m() {
+ executedGetter = true;
+ }
+
+ access() {
+ this.#m--;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+
+ assert.sameValue(true, executedGetter);
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ throw new Error("Should never be executed");
+ }
+
+ access() {
+ this.#m--;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ assert.sameValue(3, v);
+ }
+
+ get #m() {
+ return 4;
+ }
+
+ access() {
+ this.#m--;
+ }
+ }
+
+ let c = new C();
+ c.access();
+})();
+
Added: trunk/JSTests/stress/private-methods-and-accessors-prefix-node.js (0 => 272883)
--- trunk/JSTests/stress/private-methods-and-accessors-prefix-node.js (rev 0)
+++ trunk/JSTests/stress/private-methods-and-accessors-prefix-node.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,166 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+};
+
+(function() {
+ class C {
+ #method() {
+ throw new Error("Should never be called");
+ }
+
+ access() {
+ return ++this.#method;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ let executedGetter = false;
+ class C {
+ get #m() {
+ executedGetter = true;
+ }
+
+ access() {
+ return ++this.#m;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+
+ assert.sameValue(true, executedGetter);
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ throw new Error("Should never be executed");
+ }
+
+ access() {
+ return ++this.#m;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ assert.sameValue(5, v);
+ }
+
+ get #m() {
+ return 4;
+ }
+
+ access() {
+ return ++this.#m;
+ }
+ }
+
+ let c = new C();
+ assert.sameValue(5, c.access());
+})();
+
+// Ignored result
+
+(function() {
+ class C {
+ #method() {
+ throw new Error("Should never be called");
+ }
+
+ access() {
+ --this.#method;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ let executedGetter = false;
+ class C {
+ get #m() {
+ executedGetter = true;
+ }
+
+ access() {
+ --this.#m;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+
+ assert.sameValue(true, executedGetter);
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ throw new Error("Should never be executed");
+ }
+
+ access() {
+ --this.#m;
+ }
+ }
+
+ let c = new C();
+ assert.throws(TypeError, function() {
+ c.access();
+ });
+})();
+
+(function() {
+ class C {
+ set #m(v) {
+ assert.sameValue(3, v);
+ }
+
+ get #m() {
+ return 4;
+ }
+
+ access() {
+ --this.#m;
+ }
+ }
+
+ let c = new C();
+ c.access();
+})();
+
Modified: trunk/JSTests/stress/private-names-available-on-direct-eval.js (272882 => 272883)
--- trunk/JSTests/stress/private-names-available-on-direct-eval.js 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/JSTests/stress/private-names-available-on-direct-eval.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -21,3 +21,36 @@
assert.sameValue(c.callMethodFromEval(), 'test');
})();
+(function () {
+ class C {
+ get #m() {
+ return 'test';
+ }
+
+ callGetterFromEval() {
+ let self = this;
+ return eval('self.#m');
+ }
+ }
+
+ let c = new C();
+ assert.sameValue(c.callGetterFromEval(), 'test');
+})();
+
+(function () {
+ class C {
+ set #m(v) {
+ this._v = v;
+ }
+
+ callSetterFromEval(v) {
+ let self = this;
+ eval('self.#m = v');
+ }
+ }
+
+ let c = new C();
+ c.callSetterFromEval('test')
+ assert.sameValue(c._v, 'test');
+})();
+
Copied: trunk/JSTests/stress/private-names-available-on-eval-during-field-initialization.js (from rev 272882, trunk/JSTests/stress/private-names-available-on-direct-eval.js) (0 => 272883)
--- trunk/JSTests/stress/private-names-available-on-eval-during-field-initialization.js (rev 0)
+++ trunk/JSTests/stress/private-names-available-on-eval-during-field-initialization.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,31 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + rhs + " bug got: " + lhs);
+ }
+};
+
+(function () {
+ class C {
+ #m() { return 'test'; }
+
+ field = eval('this.#m()');
+ }
+
+ let c = new C();
+ assert.sameValue(c.field, 'test');
+})();
+
+(function () {
+ class C {
+ get #m() { return 'test'; }
+
+ field = eval('this.#m');
+ }
+
+ let c = new C();
+ assert.sameValue(c.field, 'test');
+})();
+
Added: trunk/JSTests/stress/private-setter-brand-check.js (0 => 272883)
--- trunk/JSTests/stress/private-setter-brand-check.js (rev 0)
+++ trunk/JSTests/stress/private-setter-brand-check.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,100 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+};
+
+(function () {
+ let createAndInstantiateClass = function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ access(o, v) {
+ o.#m = v;
+ }
+ }
+
+ let c = new C();
+ return c;
+ };
+
+ let c1 = createAndInstantiateClass();
+ let c2 = createAndInstantiateClass();
+
+ c1.access(c1, 'test');
+ assert.sameValue(c1._v, 'test');
+ c2.access(c2, 'test');
+ assert.sameValue(c2._v, 'test');
+
+ assert.throws(TypeError, function() {
+ c1.access(c2, 'foo');
+ });
+
+ assert.throws(TypeError, function() {
+ c2.access(c1, 'foo');
+ });
+})();
+
+(function () {
+ class S {
+ set #m(v) { this._v = v }
+
+ superAccess(v) { this.#m = v; }
+ }
+
+ class C extends S {
+ set #m(v) { this._u = v; }
+
+ access(v) {
+ return this.#m = v;
+ }
+ }
+
+ let c = new C();
+
+ c.access('test');
+ assert.sameValue(c._u, 'test');
+
+ c.superAccess('super class');
+ assert.sameValue(c._v, 'super class');
+
+ let s = new S();
+ s.superAccess('super class')
+ assert.sameValue(s._v, 'super class');
+
+ assert.throws(TypeError, function() {
+ c.access.call(s, 'foo');
+ });
+})();
+
+(function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ access(o, v) {
+ return o.#m = v;
+ }
+ }
+
+ let c = new C();
+ c.access(c, 'test');
+ assert.sameValue(c._v, 'test');
+
+ let o = {};
+ assert.throws(TypeError, function() {
+ c.access(o, 'foo');
+ });
+})();
+
Added: trunk/JSTests/stress/private-setter-inner-class.js (0 => 272883)
--- trunk/JSTests/stress/private-setter-inner-class.js (rev 0)
+++ trunk/JSTests/stress/private-setter-inner-class.js 2021-02-15 22:40:26 UTC (rev 272883)
@@ -0,0 +1,155 @@
+//@ requireOptions("--usePrivateMethods=true")
+
+let assert = {
+ sameValue: function (lhs, rhs) {
+ if (lhs !== rhs)
+ throw new Error("Expected: " + lhs + " bug got: " + rhs);
+ },
+
+ throws: function (expectedError, op) {
+ try {
+ op();
+ } catch(e) {
+ if (!(e instanceof expectedError))
+ throw new Error("Expected to throw: " + expectedError + " but threw: " + e);
+ }
+ }
+};
+
+(function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ B = class {
+ method(o, v) {
+ o.#m = v;
+ }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+ innerB.method(c, 'test');
+ assert.sameValue(c._v, 'test');
+})();
+
+(function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ method(v) { this.#m = v; }
+
+ B = class {
+ method(o, v) {
+ o.#m = v;
+ }
+
+ get m() { return this.#m; }
+
+ #m;
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+
+ innerB.method(innerB, 'test');
+ assert.sameValue(innerB.m, 'test');
+
+ c.method('outer class');
+ assert.sameValue(c._v, 'outer class');
+
+ assert.throws(TypeError, function() {
+ innerB.method(c, 'foo');
+ });
+})();
+
+(function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ method(v) { this.#m = v; }
+
+ B = class {
+ method(o, v) {
+ o.#m = v;
+ }
+
+ get #m() { return 'test'; }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+
+ assert.throws(TypeError, function() {
+ innerB.method(innerB);
+ });
+
+ c.method('outer class');
+ assert.sameValue(c._v, 'outer class');
+
+ assert.throws(TypeError, function() {
+ innerB.method(c);
+ });
+})();
+
+(function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ method(v) { this.#m = v; }
+
+ B = class {
+ method(o, v) {
+ o.#m = v;
+ }
+
+ #m() { return 'test'; }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+
+ assert.throws(TypeError, function() {
+ innerB.method(innerB, 'foo');
+ });
+
+ c.method('outer class');
+ assert.sameValue(c._v, 'outer class');
+
+ assert.throws(TypeError, function() {
+ innerB.method(c);
+ });
+})();
+
+(function () {
+ class C {
+ set #m(v) { this._v = v; }
+
+ method(v) { this.#m = v; }
+
+ B = class {
+ method(o, v) {
+ o.#m = v;
+ }
+
+ set #m(v) { this._v = v; }
+ }
+ }
+
+ let c = new C();
+ let innerB = new c.B();
+
+ innerB.method(innerB, 'test262');
+ assert.sameValue(innerB._v, 'test262');
+
+ c.method('outer class');
+ assert.sameValue(c._v, 'outer class');
+
+ assert.throws(TypeError, function() {
+ innerB.method(c, 'foo');
+ });
+})();
+
Modified: trunk/JSTests/test262/config.yaml (272882 => 272883)
--- trunk/JSTests/test262/config.yaml 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/JSTests/test262/config.yaml 2021-02-15 22:40:26 UTC (rev 272883)
@@ -4,6 +4,7 @@
WeakRef: useWeakRefs
FinalizationRegistry: useWeakRefs
class-fields-private: usePrivateClassFields
+ class-methods-private: usePrivateMethods
class-static-fields-public: usePublicStaticClassFields
class-static-fields-private: usePrivateStaticClassFields
Intl.DateTimeFormat-dayPeriod: useIntlDateTimeFormatDayPeriod
@@ -19,7 +20,6 @@
- regexp-lookbehind
- legacy-regexp
- - class-methods-private
- class-static-methods-private
- cleanupSome
- host-gc-required
Modified: trunk/JSTests/test262/expectations.yaml (272882 => 272883)
--- trunk/JSTests/test262/expectations.yaml 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/JSTests/test262/expectations.yaml 2021-02-15 22:40:26 UTC (rev 272883)
@@ -741,6 +741,12 @@
test/built-ins/Function/prototype/toString/method-object.js:
default: 'Test262Error: Conforms to NativeFunction Syntax: "function f( /* b */ ) /* c */ { /* d */ }" (f /* a */ ( /* b */ ) /* c */ { /* d */ })'
strict mode: 'Test262Error: Conforms to NativeFunction Syntax: "function f( /* b */ ) /* c */ { /* d */ }" (f /* a */ ( /* b */ ) /* c */ { /* d */ })'
+test/built-ins/Function/prototype/toString/private-method-class-_expression_.js:
+ default: 'Test262Error: Conforms to NativeFunction Syntax: "function #f( /* b */ ) /* c */ { /* d */ }" (#f /* a */ ( /* b */ ) /* c */ { /* d */ })'
+ strict mode: 'Test262Error: Conforms to NativeFunction Syntax: "function #f( /* b */ ) /* c */ { /* d */ }" (#f /* a */ ( /* b */ ) /* c */ { /* d */ })'
+test/built-ins/Function/prototype/toString/private-method-class-statement.js:
+ default: 'Test262Error: Conforms to NativeFunction Syntax: "function #f( /* b */ ) /* c */ { /* d */ }" (#f /* a */ ( /* b */ ) /* c */ { /* d */ })'
+ strict mode: 'Test262Error: Conforms to NativeFunction Syntax: "function #f( /* b */ ) /* c */ { /* d */ }" (#f /* a */ ( /* b */ ) /* c */ { /* d */ })'
test/built-ins/Function/prototype/toString/setter-class-_expression_-static.js:
default: 'Test262Error: Conforms to NativeFunction Syntax: "function ( /* c */ a /* d */ ) /* e */ { /* f */ }" (set /* a */ f /* b */ ( /* c */ a /* d */ ) /* e */ { /* f */ })'
strict mode: 'Test262Error: Conforms to NativeFunction Syntax: "function ( /* c */ a /* d */ ) /* e */ { /* f */ }" (set /* a */ f /* b */ ( /* c */ a /* d */ ) /* e */ { /* f */ })'
Modified: trunk/Source/_javascript_Core/ChangeLog (272882 => 272883)
--- trunk/Source/_javascript_Core/ChangeLog 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-02-15 22:40:26 UTC (rev 272883)
@@ -1,3 +1,101 @@
+2021-02-15 Caio Lima <ticaiol...@gmail.com>
+
+ [ESNext] Implement private accessors
+ https://bugs.webkit.org/show_bug.cgi?id=194435
+
+ Reviewed by Yusuke Suzuki.
+
+ This patch is implementing support for instance private getters and
+ setters following the proposal on https://tc39.es/proposal-private-methods.
+ Private accessors also use the private brand check mechanism of
+ private methods, which means that we are using both
+ `op_set_private_brand` and `op_check_private_brand` to perform brand
+ checks. Accessors are also stored on class lexical scope as a pair of
+ `getter` and `setter`. This is done creating a new `JSObject` and
+ storing the `getter` on `get` property, and `setter` on `set`
+ property. This is designed in such way that we can always hit IC fast
+ path on `get_by_id_direct` to access the property, and also to allow
+ constant folding of accessors on DFG/FTL, since acessors objects are
+ going to be constant once created.
+
+ For reference, we have the following bytecode for a private getter
+ access:
+
+ ```
+ class C {
+ get #m() {...}
+ access() {
+ return this.#m;
+ }
+ }
+ ```
+
+ Bytecode for class declaration:
+
+ ```
+ ...
+ new_object dst:loc12, inlineCapacity:2 // this is the object to store getter and setter pair
+ new_func_exp dst:loc13, scope:loc4, functionDecl:"get #m() {...}"
+ put_by_id base:loc13, property:@homeObject, value:loc11, flags:Strict
+ put_by_id base:loc12, property:@get, value:loc13, flags:IsDirect|Strict
+ put_to_scope scope:loc4, var:#m, value:loc12 // loc4 is the class lexical scope
+ ...
+
+ ```
+
+ Bytecode for `access()`:
+
+ ```
+ ...
+ resolve_scope dst:loc7, scope:loc4, var:"#m", resolveType:GlobalProperty, localScopeDepth:0
+ get_from_scope dst:loc8, scope:loc7, var:@privateBrand
+ check_private_brand base:this, brand:loc8
+ get_from_scope dst:loc8, scope:loc7, var:"#m"
+ get_by_id_direct dst:loc9, base:loc8, property:@get
+ mov dst:loc10, src:this
+ call dst:loc6, callee:loc9, argc:1, argv:16
+ ...
+
+ ```
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::instantiateLexicalVariables):
+ (JSC::BytecodeGenerator::getPrivateTraits):
+ (JSC::BytecodeGenerator::getAvailablePrivateAccessNames):
+ (JSC::BytecodeGenerator::isPrivateMethod): Deleted.
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::PropertyListNode::emitBytecode):
+ (JSC::PropertyListNode::emitPutConstantProperty):
+ (JSC::BaseDotNode::emitGetPropertyValue):
+ (JSC::BaseDotNode::emitPutProperty):
+ (JSC::PostfixNode::emitDot):
+ (JSC::PrefixNode::emitDot):
+ * parser/Nodes.h:
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseClass):
+ (JSC::Parser<LexerType>::parseGetterSetter):
+ * parser/Parser.h:
+ (JSC::Scope::declarePrivateSetter):
+ (JSC::Scope::declarePrivateGetter):
+ * parser/VariableEnvironment.cpp:
+ (JSC::VariableEnvironment::declarePrivateAccessor):
+ (JSC::VariableEnvironment::declarePrivateSetter):
+ (JSC::VariableEnvironment::declarePrivateGetter):
+ * parser/VariableEnvironment.h:
+ (JSC::VariableEnvironmentEntry::isPrivateSetter const):
+ (JSC::VariableEnvironmentEntry::isPrivateGetter const):
+ (JSC::VariableEnvironmentEntry::setIsPrivateSetter):
+ (JSC::VariableEnvironmentEntry::setIsPrivateGetter):
+ (JSC::PrivateNameEntry::isSetter const):
+ (JSC::PrivateNameEntry::isGetter const):
+ (JSC::PrivateNameEntry::isField const):
+ (JSC::PrivateNameEntry::isPrivateMethodOrAcessor const):
+ (JSC::VariableEnvironment::declarePrivateSetter):
+ (JSC::VariableEnvironment::declarePrivateGetter):
+ * runtime/ExceptionHelpers.cpp:
+ (JSC::createPrivateMethodAccessError):
+
2021-02-15 Commit Queue <commit-qu...@webkit.org>
Unreviewed, reverting r272831.
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (272882 => 272883)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2021-02-15 22:40:26 UTC (rev 272883)
@@ -56,8 +56,8 @@
#include "UnlinkedModuleProgramCodeBlock.h"
#include "UnlinkedProgramCodeBlock.h"
#include <wtf/BitVector.h>
+#include <wtf/HashSet.h>
#include <wtf/Optional.h>
-#include <wtf/SmallPtrSet.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/WTFString.h>
@@ -1900,10 +1900,17 @@
// FIXME: only do this if there is an eval() within a nested scope --- otherwise it isn't needed.
// https://bugs.webkit.org/show_bug.cgi?id=206663
- if (entry.value.isPrivateField())
- symbolTable->addPrivateName(entry.key.get(), PrivateNameEntry(PrivateNameEntry::Traits::IsDeclared));
- else if (entry.value.isPrivateMethod())
- symbolTable->addPrivateName(entry.key.get(), PrivateNameEntry(PrivateNameEntry::Traits::IsDeclared | PrivateNameEntry::Traits::IsMethod));
+
+ const PrivateNameEnvironment* privateEnvironment = lexicalVariables.privateNameEnvironment();
+ if (!privateEnvironment)
+ continue;
+
+ auto findResult = privateEnvironment->find(entry.key.get());
+
+ if (findResult == privateEnvironment->end())
+ continue;
+
+ symbolTable->addPrivateName(findResult->key.get(), findResult->value);
}
}
return hasCapturedVariables;
@@ -2931,16 +2938,18 @@
}
}
-bool BytecodeGenerator::isPrivateMethod(const Identifier& ident)
+// This should be called only with PrivateNames available.
+PrivateNameEntry BytecodeGenerator::getPrivateTraits(const Identifier& ident)
{
for (unsigned i = m_privateNamesStack.size(); i--; ) {
auto& map = m_privateNamesStack[i];
auto it = map.find(ident.impl());
if (it != map.end())
- return it->value.isMethod();
+ return it->value;
}
- return false;
+ RELEASE_ASSERT_NOT_REACHED();
+ return PrivateNameEntry();
}
void BytecodeGenerator::pushPrivateAccessNames(const PrivateNameEnvironment* environment)
@@ -2981,17 +2990,13 @@
Optional<PrivateNameEnvironment> BytecodeGenerator::getAvailablePrivateAccessNames()
{
PrivateNameEnvironment result;
- SmallPtrSet<UniquedStringImpl*, 16> excludedNames;
+ HashSet<UniquedStringImpl*> excludedNames;
for (unsigned i = m_privateNamesStack.size(); i--; ) {
auto& map = m_privateNamesStack[i];
for (auto& entry : map) {
- if (entry.value.isPrivateMethodOrAcessor()) {
- if (!excludedNames.contains(entry.key.get())) {
- result.add(entry.key, entry.value);
- excludedNames.add(entry.key.get());
- }
- } else
- excludedNames.add(entry.key.get());
+ auto addResult = excludedNames.add(entry.key.get());
+ if (addResult.isNewEntry)
+ result.add(entry.key, entry.value);
}
}
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (272882 => 272883)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2021-02-15 22:40:26 UTC (rev 272883)
@@ -1263,7 +1263,7 @@
m_lastInstruction = prevLastInstruction;
}
- bool isPrivateMethod(const Identifier&);
+ PrivateNameEntry getPrivateTraits(const Identifier&);
void pushPrivateAccessNames(const PrivateNameEnvironment*);
void popPrivateAccessNames();
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (272882 => 272883)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2021-02-15 22:40:26 UTC (rev 272883)
@@ -579,6 +579,57 @@
RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype, Vector<JSTextPosition>* instanceFieldLocations, Vector<JSTextPosition>* staticFieldLocations)
{
+ using GetterSetterPair = std::pair<PropertyNode*, PropertyNode*>;
+ using GetterSetterMap = HashMap<UniquedStringImpl*, GetterSetterPair, IdentifierRepHash>;
+
+ if (hasPrivateAccessors()) {
+ GetterSetterMap privateAccessorMap;
+
+ for (PropertyListNode* propertyList = this; propertyList; propertyList = propertyList->m_next) {
+ if (!(propertyList->m_node->type() & (PropertyNode::PrivateGetter | PropertyNode::PrivateSetter)))
+ continue;
+
+ // We group private getters and setters to store them in a object
+ GetterSetterPair pair(propertyList->m_node, static_cast<PropertyNode*>(nullptr));
+ GetterSetterMap::AddResult result = privateAccessorMap.add(propertyList->m_node->name()->impl(), pair);
+ auto& resultPair = result.iterator->value;
+ // If the map already contains an element with node->name(),
+ // we need to store this node in the second part.
+ if (!result.isNewEntry)
+ resultPair.second = propertyList->m_node;
+ continue;
+ }
+
+ // Then we declare private accessors
+ for (auto& it : privateAccessorMap) {
+ // FIXME: Use GetterSetter to store private accessors
+ // https://bugs.webkit.org/show_bug.cgi?id=221915
+ RefPtr<RegisterID> getterSetterObj = generator.emitNewObject(generator.newTemporary());
+ GetterSetterPair pair = it.value;
+
+ auto emitPutAccessor = [&] (PropertyNode* propertyNode) {
+ RegisterID* base = propertyNode->isInstanceClassProperty() ? prototype : dstOrConstructor;
+
+ RefPtr<RegisterID> value = generator.emitNode(propertyNode->m_assign);
+ if (propertyNode->needsSuperBinding())
+ emitPutHomeObject(generator, value.get(), base);
+ auto setterOrGetterIdent = propertyNode->m_type & PropertyNode::PrivateGetter
+ ? generator.propertyNames().builtinNames().getPrivateName()
+ : generator.propertyNames().builtinNames().setPrivateName();
+ generator.emitDirectPutById(getterSetterObj.get(), setterOrGetterIdent, value.get());
+ };
+
+ if (pair.first)
+ emitPutAccessor(pair.first);
+
+ if (pair.second)
+ emitPutAccessor(pair.second);
+
+ Variable var = generator.variable(*pair.first->name());
+ generator.emitPutToScope(generator.scopeRegister(), var, getterSetterObj.get(), DoNotThrowIfNotFound, InitializationMode::ConstInitialization);
+ }
+ }
+
PropertyListNode* p = this;
RegisterID* dst = nullptr;
@@ -586,6 +637,9 @@
for (; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next) {
dst = p->m_node->isInstanceClassProperty() ? prototype : dstOrConstructor;
+ if (p->m_node->type() & (PropertyNode::PrivateGetter | PropertyNode::PrivateSetter))
+ continue;
+
if (p->isComputedClassField())
emitSaveComputedFieldName(generator, *p->m_node);
@@ -610,8 +664,6 @@
// a computed property or a spread, just emit everything as that may override previous values.
bool canOverrideProperties = false;
- typedef std::pair<PropertyNode*, PropertyNode*> GetterSetterPair;
- typedef HashMap<UniquedStringImpl*, GetterSetterPair, IdentifierRepHash> GetterSetterMap;
GetterSetterMap instanceMap;
GetterSetterMap staticMap;
@@ -651,6 +703,9 @@
if (p->isComputedClassField())
emitSaveComputedFieldName(generator, *p->m_node);
+ if (p->m_node->type() & (PropertyNode::PrivateGetter | PropertyNode::PrivateSetter))
+ continue;
+
if (p->isInstanceClassField()) {
ASSERT(instanceFieldLocations);
ASSERT(node->m_type & PropertyNode::Constant);
@@ -782,6 +837,8 @@
if (node.isClassProperty()) {
ASSERT(node.needsSuperBinding());
+ ASSERT(!(node.type() & PropertyNode::PrivateSetter));
+ ASSERT(!(node.type() & PropertyNode::PrivateGetter));
if (node.type() & PropertyNode::PrivateMethod) {
Variable var = generator.variable(*node.name());
@@ -919,16 +976,44 @@
RegisterID* BaseDotNode::emitGetPropertyValue(BytecodeGenerator& generator, RegisterID* dst, RegisterID* base, RefPtr<RegisterID>& thisValue)
{
if (isPrivateMember()) {
- if (generator.isPrivateMethod(identifier())) {
- Variable var = generator.variable(identifier());
+ auto identifierName = identifier();
+ auto privateTraits = generator.getPrivateTraits(identifierName);
+ if (privateTraits.isMethod()) {
+ Variable var = generator.variable(identifierName);
RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
- RegisterID* privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
- generator.emitCheckPrivateBrand(base, privateBrandSymbol);
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base, privateBrandSymbol.get());
return generator.emitGetFromScope(dst, scope.get(), var, ThrowIfNotFound);
}
+ if (privateTraits.isGetter()) {
+ Variable var = generator.variable(identifierName);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base, privateBrandSymbol.get());
+
+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+ RefPtr<RegisterID> getterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().getPrivateName());
+ CallArguments args(generator, nullptr);
+ generator.move(args.thisRegister(), base);
+ return generator.emitCall(dst, getterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes);
+ }
+
+ if (privateTraits.isSetter()) {
+ // We need to perform brand check to follow the spec
+ Variable var = generator.variable(identifierName);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base, privateBrandSymbol.get());
+ generator.emitThrowTypeError("Trying to access an undefined private getter");
+ return dst;
+ }
+
+ ASSERT(privateTraits.isField());
Variable var = generator.variable(m_ident);
ASSERT_WITH_MESSAGE(!var.local(), "Private Field names must be stored in captured variables");
@@ -957,16 +1042,36 @@
{
if (isPrivateMember()) {
auto identifierName = identifier();
- if (generator.isPrivateMethod(identifierName)) {
+ auto privateTraits = generator.getPrivateTraits(identifierName);
+ if (privateTraits.isSetter()) {
Variable var = generator.variable(identifierName);
RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
- RegisterID* privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
- generator.emitCheckPrivateBrand(base, privateBrandSymbol);
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base, privateBrandSymbol.get());
- generator.emitThrowTypeError("Trying to access a not defined private setter");
+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+ RefPtr<RegisterID> setterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().setPrivateName());
+ CallArguments args(generator, nullptr, 1);
+ generator.move(args.thisRegister(), base);
+ generator.move(args.argumentRegister(0), value);
+ generator.emitCall(generator.newTemporary(), setterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes);
+
+ return value;
}
+ if (privateTraits.isGetter() || privateTraits.isMethod()) {
+ Variable var = generator.variable(identifierName);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base, privateBrandSymbol.get());
+
+ generator.emitThrowTypeError("Trying to access an undefined private setter");
+ return value;
+ }
+
+ ASSERT(privateTraits.isField());
Variable var = generator.variable(m_ident);
ASSERT_WITH_MESSAGE(!var.local(), "Private Field names must be stored in captured variables");
@@ -2287,16 +2392,67 @@
if (dotAccessor->isPrivateMember()) {
ASSERT(!baseIsSuper);
+ auto privateTraits = generator.getPrivateTraits(ident);
+
+ if (privateTraits.isField()) {
+ Variable var = generator.variable(ident);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+ RefPtr<RegisterID> privateName = generator.newTemporary();
+ generator.emitGetFromScope(privateName.get(), scope.get(), var, DoNotThrowIfNotFound);
+
+ RefPtr<RegisterID> value = generator.emitGetPrivateName(generator.newTemporary(), base.get(), privateName.get());
+ RefPtr<RegisterID> oldValue = emitPostIncOrDec(generator, generator.tempDestination(dst), value.get(), m_operator);
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitPrivateFieldPut(base.get(), privateName.get(), value.get());
+ generator.emitProfileType(value.get(), divotStart(), divotEnd());
+ return generator.move(dst, oldValue.get());
+ }
+
+ if (privateTraits.isMethod()) {
+ Variable var = generator.variable(ident);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base.get(), privateBrandSymbol.get());
+
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitThrowTypeError("Trying to access an undefined private setter");
+ return generator.tempDestination(dst);
+ }
+
Variable var = generator.variable(ident);
RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
- RefPtr<RegisterID> privateName = generator.newTemporary();
- generator.emitGetFromScope(privateName.get(), scope.get(), var, DoNotThrowIfNotFound);
- RefPtr<RegisterID> value = generator.emitGetPrivateName(generator.newTemporary(), base.get(), privateName.get());
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base.get(), privateBrandSymbol.get());
+
+ RefPtr<RegisterID> value;
+ if (privateTraits.isGetter()) {
+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+ RefPtr<RegisterID> getterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().getPrivateName());
+ CallArguments args(generator, nullptr);
+ generator.move(args.thisRegister(), base.get());
+ value = generator.emitCall(generator.newTemporary(), getterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes);
+ } else {
+ generator.emitThrowTypeError("Trying to access an undefined private getter");
+ return generator.tempDestination(dst);
+ }
+
RefPtr<RegisterID> oldValue = emitPostIncOrDec(generator, generator.tempDestination(dst), value.get(), m_operator);
generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
- generator.emitPrivateFieldPut(base.get(), privateName.get(), value.get());
- generator.emitProfileType(value.get(), divotStart(), divotEnd());
+
+ if (privateTraits.isSetter()) {
+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+ RefPtr<RegisterID> setterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().setPrivateName());
+ CallArguments args(generator, nullptr, 1);
+ generator.move(args.thisRegister(), base.get());
+ generator.move(args.argumentRegister(0), value.get());
+ generator.emitCall(generator.newTemporary(), setterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes);
+ generator.emitProfileType(value.get(), divotStart(), divotEnd());
+ return generator.move(dst, oldValue.get());
+ }
+
+ generator.emitThrowTypeError("Trying to access an undefined private getter");
return generator.move(dst, oldValue.get());
}
@@ -2525,17 +2681,66 @@
generator.emitExpressionInfo(dotAccessor->divot(), dotAccessor->divotStart(), dotAccessor->divotEnd());
RegisterID* value;
if (dotAccessor->isPrivateMember()) {
- ASSERT(!baseNode->isSuperNode());
+ auto privateTraits = generator.getPrivateTraits(ident);
+ if (privateTraits.isField()) {
+ ASSERT(!baseNode->isSuperNode());
+ Variable var = generator.variable(ident);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+ RefPtr<RegisterID> privateName = generator.newTemporary();
+ generator.emitGetFromScope(privateName.get(), scope.get(), var, DoNotThrowIfNotFound);
+
+ value = generator.emitGetPrivateName(propDst.get(), base.get(), privateName.get());
+ emitIncOrDec(generator, value, m_operator);
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitPrivateFieldPut(base.get(), privateName.get(), value);
+ generator.emitProfileType(value, divotStart(), divotEnd());
+ return generator.move(dst, propDst.get());
+ }
+
+ if (privateTraits.isMethod()) {
+ Variable var = generator.variable(ident);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base.get(), privateBrandSymbol.get());
+
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitThrowTypeError("Trying to access an undefined private setter");
+ return generator.move(dst, propDst.get());
+ }
+
Variable var = generator.variable(ident);
RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
- RefPtr<RegisterID> privateName = generator.newTemporary();
- generator.emitGetFromScope(privateName.get(), scope.get(), var, DoNotThrowIfNotFound);
- value = generator.emitGetPrivateName(propDst.get(), base.get(), privateName.get());
+ RefPtr<RegisterID> privateBrandSymbol = generator.emitGetPrivateBrand(generator.newTemporary(), scope.get());
+ generator.emitCheckPrivateBrand(base.get(), privateBrandSymbol.get());
+
+ if (privateTraits.isGetter()) {
+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+ RefPtr<RegisterID> getterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().getPrivateName());
+ CallArguments args(generator, nullptr);
+ generator.move(args.thisRegister(), base.get());
+ value = generator.emitCall(propDst.get(), getterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes);
+ } else {
+ generator.emitThrowTypeError("Trying to access an undefined private getter");
+ return generator.move(dst, propDst.get());
+ }
+
emitIncOrDec(generator, value, m_operator);
generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
- generator.emitPrivateFieldPut(base.get(), privateName.get(), value);
- generator.emitProfileType(value, divotStart(), divotEnd());
+
+ if (privateTraits.isSetter()) {
+ RefPtr<RegisterID> getterSetterObj = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+ RefPtr<RegisterID> setterFunction = generator.emitDirectGetById(generator.newTemporary(), getterSetterObj.get(), generator.propertyNames().builtinNames().setPrivateName());
+ CallArguments args(generator, nullptr, 1);
+ generator.move(args.thisRegister(), base.get());
+ generator.move(args.argumentRegister(0), value);
+ generator.emitCall(generator.newTemporary(), setterFunction.get(), NoExpectedFunction, args, m_position, m_position, m_position, DebuggableCall::Yes);
+ generator.emitProfileType(value, divotStart(), divotEnd());
+ return generator.move(dst, propDst.get());
+ }
+
+ generator.emitThrowTypeError("Trying to access an undefined private getter");
return generator.move(dst, propDst.get());
}
Modified: trunk/Source/_javascript_Core/parser/Nodes.h (272882 => 272883)
--- trunk/Source/_javascript_Core/parser/Nodes.h 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/parser/Nodes.h 2021-02-15 22:40:26 UTC (rev 272883)
@@ -721,7 +721,7 @@
enum class ClassElementTag : uint8_t { No, Instance, Static, LastTag };
class PropertyNode final : public ParserArenaFreeable {
public:
- enum Type : uint8_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, PrivateField = 64, PrivateMethod = 128 };
+ enum Type : uint16_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, PrivateField = 64, PrivateMethod = 128, PrivateSetter = 256, PrivateGetter = 512 };
PropertyNode(const Identifier&, ExpressionNode*, Type, SuperBinding, ClassElementTag);
PropertyNode(ExpressionNode*, Type, SuperBinding, ClassElementTag);
@@ -740,7 +740,7 @@
bool isInstanceClassField() const { return isInstanceClassProperty() && !needsSuperBinding(); }
bool isStaticClassField() const { return isStaticClassProperty() && !needsSuperBinding(); }
bool isOverriddenByDuplicate() const { return m_isOverriddenByDuplicate; }
- bool isPrivate() const { return m_type & (PrivateField | PrivateMethod); }
+ bool isPrivate() const { return m_type & (PrivateField | PrivateMethod | PrivateGetter | PrivateSetter); }
bool hasComputedName() const { return m_expression; }
bool isComputedClassField() const { return isClassField() && hasComputedName(); }
void setIsOverriddenByDuplicate() { m_isOverriddenByDuplicate = true; }
@@ -760,7 +760,7 @@
const Identifier* m_name;
ExpressionNode* m_expression;
ExpressionNode* m_assign;
- unsigned m_type;
+ unsigned m_type : 10;
unsigned m_needsSuperBinding : 1;
static_assert(1 << 2 > static_cast<unsigned>(ClassElementTag::LastTag), "ClassElementTag shouldn't use more than two bits");
unsigned m_classElementTag : 2;
@@ -788,6 +788,16 @@
return m_node->isStaticClassField();
}
+ void setHasPrivateAccessors(bool hasPrivateAccessors)
+ {
+ m_hasPrivateAccessors = hasPrivateAccessors;
+ }
+
+ bool hasPrivateAccessors() const
+ {
+ return m_hasPrivateAccessors;
+ }
+
static bool shouldCreateLexicalScopeForClass(PropertyListNode*);
RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*, Vector<JSTextPosition>*, Vector<JSTextPosition>*);
@@ -804,6 +814,7 @@
PropertyNode* m_node;
PropertyListNode* m_next { nullptr };
+ bool m_hasPrivateAccessors { false };
};
class ObjectLiteralNode final : public ExpressionNode {
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (272882 => 272883)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2021-02-15 22:40:26 UTC (rev 272883)
@@ -2880,6 +2880,7 @@
classScope->preventVarDeclarations();
classScope->setStrictMode();
bool declaresPrivateMethod = false;
+ bool declaresPrivateAccessor = false;
next();
ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax.");
@@ -2978,7 +2979,7 @@
bool escaped = m_token.m_data.escaped;
ASSERT(ident);
next();
- if (parseMode == SourceParseMode::MethodMode && !escaped && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(BIGINT) || match(OPENBRACKET))) {
+ if (parseMode == SourceParseMode::MethodMode && !escaped && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(BIGINT) || match(OPENBRACKET) || (Options::usePrivateMethods() && match(PRIVATENAME)))) {
isGetter = *ident == propertyNames.get;
isSetter = *ident == propertyNames.set;
}
@@ -3006,6 +3007,7 @@
ASSERT(ident);
next();
if (Options::usePrivateMethods() && match(OPENPAREN)) {
+ semanticFailIfTrue(tag == ClassElementTag::Static, "Cannot declare a static private method");
semanticFailIfTrue(classScope->declarePrivateMethod(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare private method twice");
declaresPrivateMethod = true;
type = static_cast<PropertyNode::Type>(type | PropertyNode::PrivateMethod);
@@ -3025,8 +3027,21 @@
TreeProperty property;
if (isGetter || isSetter) {
- type = static_cast<PropertyNode::Type>(type & ~PropertyNode::Constant);
- type = static_cast<PropertyNode::Type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter));
+ if (Options::usePrivateMethods() && match(PRIVATENAME)) {
+ ident = m_token.m_data.ident;
+ if (isSetter) {
+ semanticFailIfTrue(classScope->declarePrivateSetter(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Declared private setter with an already used name");
+ declaresPrivateAccessor = true;
+ type = static_cast<PropertyNode::Type>(type | PropertyNode::PrivateSetter);
+ } else {
+ semanticFailIfTrue(classScope->declarePrivateGetter(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Declared private getter with an already used name");
+ declaresPrivateAccessor = true;
+ type = static_cast<PropertyNode::Type>(type | PropertyNode::PrivateGetter);
+ }
+ } else {
+ type = static_cast<PropertyNode::Type>(type & ~PropertyNode::Constant);
+ type = static_cast<PropertyNode::Type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter));
+ }
property = parseGetterSetter(context, type, methodStart, ConstructorKind::None, tag);
failIfFalse(property, "Cannot parse this method");
} else if (!match(OPENPAREN) && (tag == ClassElementTag::Instance || Options::usePublicStaticClassFields()) && parseMode == SourceParseMode::MethodMode) {
@@ -3100,7 +3115,7 @@
info.endOffset = tokenLocation().endOffset - 1;
consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body");
- if (declaresPrivateMethod) {
+ if (declaresPrivateMethod || declaresPrivateAccessor) {
Identifier privateBrandIdentifier = m_vm.propertyNames->builtinNames().privateBrandPrivateName();
DeclarationResultMask declarationResult = classScope->declareLexicalVariable(&privateBrandIdentifier, true);
ASSERT_UNUSED(declarationResult, declarationResult == DeclarationResult::Valid);
@@ -3108,6 +3123,11 @@
classScope->addClosedVariableCandidateUnconditionally(privateBrandIdentifier.impl());
}
+ if constexpr (std::is_same_v<TreeBuilder, ASTBuilder>) {
+ if (classElements)
+ classElements->setHasPrivateAccessors(declaresPrivateAccessor);
+ }
+
if (Options::usePrivateClassFields()) {
// Fail if there are no parent private name scopes and any used-but-undeclared private names.
semanticFailIfFalse(copyUndeclaredPrivateNamesToOuterScope(), "Cannot reference undeclared private names");
@@ -4428,12 +4448,18 @@
JSTokenLocation location(tokenLocation());
- if (matchSpecIdentifier() || match(STRING) || m_token.m_type & KeywordTokenFlag) {
+ bool matchesPrivateName = match(PRIVATENAME);
+ if (matchSpecIdentifier() || match(STRING) || (Options::usePrivateMethods() && matchesPrivateName) || m_token.m_type & KeywordTokenFlag) {
stringPropertyName = m_token.m_data.ident;
semanticFailIfTrue(tag == ClassElementTag::Static && *stringPropertyName == m_vm.propertyNames->prototype,
"Cannot declare a static method named 'prototype'");
semanticFailIfTrue(tag == ClassElementTag::Instance && *stringPropertyName == m_vm.propertyNames->constructor,
"Cannot declare a getter or setter named 'constructor'");
+
+ if (match(PRIVATENAME)) {
+ semanticFailIfTrue(tag == ClassElementTag::No, "Cannot declare a private setter or getter outside a class");
+ semanticFailIfTrue(tag == ClassElementTag::Static, "Cannot declare a private setter or getter as static");
+ }
next();
} else if (match(DOUBLE) || match(INTEGER)) {
numericPropertyName = m_token.m_data.doubleValue;
@@ -4454,10 +4480,18 @@
failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition");
SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::GetterMode);
failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse getter definition");
- } else {
+ } else if (type & PropertyNode::Setter) {
failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition");
SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::SetterMode);
failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse setter definition");
+ } else if (type & PropertyNode::PrivateSetter) {
+ failIfFalse(match(OPENPAREN), "Expected a parameter list for private setter definition");
+ SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::SetterMode);
+ failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse private setter definition");
+ } else if (type & PropertyNode::PrivateGetter) {
+ failIfFalse(match(OPENPAREN), "Expected a parameter list for private getter definition");
+ SetForScope<SourceParseMode> innerParseMode(m_parseMode, SourceParseMode::GetterMode);
+ failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse private getter definition");
}
if (stringPropertyName)
Modified: trunk/Source/_javascript_Core/parser/Parser.h (272882 => 272883)
--- trunk/Source/_javascript_Core/parser/Parser.h 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/parser/Parser.h 2021-02-15 22:40:26 UTC (rev 272883)
@@ -517,6 +517,34 @@
return result;
}
+ DeclarationResultMask declarePrivateSetter(const Identifier& ident)
+ {
+ ASSERT(m_allowsLexicalDeclarations);
+ DeclarationResultMask result = DeclarationResult::Valid;
+ bool addResult = m_lexicalVariables.declarePrivateSetter(ident);
+
+ if (!addResult) {
+ result |= DeclarationResult::InvalidDuplicateDeclaration;
+ return result;
+ }
+
+ return result;
+ }
+
+ DeclarationResultMask declarePrivateGetter(const Identifier& ident)
+ {
+ ASSERT(m_allowsLexicalDeclarations);
+ DeclarationResultMask result = DeclarationResult::Valid;
+ bool addResult = m_lexicalVariables.declarePrivateGetter(ident);
+
+ if (!addResult) {
+ result |= DeclarationResult::InvalidDuplicateDeclaration;
+ return result;
+ }
+
+ return result;
+ }
+
DeclarationResultMask declarePrivateField(const Identifier& ident)
{
ASSERT(m_allowsLexicalDeclarations);
Modified: trunk/Source/_javascript_Core/parser/VariableEnvironment.cpp (272882 => 272883)
--- trunk/Source/_javascript_Core/parser/VariableEnvironment.cpp 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/parser/VariableEnvironment.cpp 2021-02-15 22:40:26 UTC (rev 272883)
@@ -104,6 +104,80 @@
findResult->value.setIsExported();
}
+bool VariableEnvironment::declarePrivateAccessor(const RefPtr<UniquedStringImpl>& identifier, PrivateNameEntry::Traits accessorTraits)
+{
+ if (!m_rareData)
+ m_rareData = WTF::makeUnique<VariableEnvironment::RareData>();
+
+ auto findResult = m_rareData->m_privateNames.find(identifier);
+
+ if (findResult == m_rareData->m_privateNames.end()) {
+ PrivateNameEntry meta(PrivateNameEntry::Traits::IsDeclared | accessorTraits);
+
+ auto entry = VariableEnvironmentEntry();
+ if (accessorTraits == PrivateNameEntry::Traits::IsSetter)
+ entry.setIsPrivateSetter();
+ else {
+ ASSERT(accessorTraits == PrivateNameEntry::Traits::IsGetter);
+ entry.setIsPrivateGetter();
+ }
+ entry.setIsConst();
+ entry.setIsCaptured();
+ m_map.add(identifier, entry);
+
+ auto addResult = m_rareData->m_privateNames.add(identifier, meta);
+ return addResult.isNewEntry;
+ }
+
+ PrivateNameEntry currentEntry = findResult->value;
+ if (currentEntry.isDeclared()) {
+ if ((accessorTraits == PrivateNameEntry::Traits::IsSetter && !currentEntry.isGetter())
+ || (accessorTraits == PrivateNameEntry::Traits::IsGetter && !currentEntry.isSetter()))
+ return false; // Error: declaring a duplicate private accessor.
+
+ PrivateNameEntry meta(currentEntry.bits() | accessorTraits);
+ m_rareData->m_privateNames.set(identifier, meta);
+
+ auto entryIterator = m_map.find(identifier);
+ ASSERT(entryIterator != m_map.end());
+ if (accessorTraits == PrivateNameEntry::Traits::IsSetter)
+ entryIterator->value.setIsPrivateSetter();
+ else {
+ ASSERT(accessorTraits == PrivateNameEntry::Traits::IsGetter);
+ entryIterator->value.setIsPrivateGetter();
+ }
+
+ return true;
+ }
+
+ // it was previously used, mark it as declared.
+ auto entry = VariableEnvironmentEntry();
+ if (accessorTraits == PrivateNameEntry::Traits::IsSetter)
+ entry.setIsPrivateSetter();
+ else {
+ ASSERT(accessorTraits == PrivateNameEntry::Traits::IsGetter);
+ entry.setIsPrivateGetter();
+ }
+ entry.setIsConst();
+ entry.setIsCaptured();
+ m_map.add(identifier, entry);
+
+ PrivateNameEntry newEntry(currentEntry.bits() | PrivateNameEntry::Traits::IsDeclared | accessorTraits);
+ m_rareData->m_privateNames.set(identifier, newEntry);
+ return true;
+
+}
+
+bool VariableEnvironment::declarePrivateSetter(const RefPtr<UniquedStringImpl>& identifier)
+{
+ return declarePrivateAccessor(identifier, PrivateNameEntry::Traits::IsSetter);
+}
+
+bool VariableEnvironment::declarePrivateGetter(const RefPtr<UniquedStringImpl>& identifier)
+{
+ return declarePrivateAccessor(identifier, PrivateNameEntry::Traits::IsGetter);
+}
+
bool VariableEnvironment::declarePrivateMethod(const RefPtr<UniquedStringImpl>& identifier)
{
if (!m_rareData)
Modified: trunk/Source/_javascript_Core/parser/VariableEnvironment.h (272882 => 272883)
--- trunk/Source/_javascript_Core/parser/VariableEnvironment.h 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/parser/VariableEnvironment.h 2021-02-15 22:40:26 UTC (rev 272883)
@@ -47,6 +47,8 @@
ALWAYS_INLINE bool isSloppyModeHoistingCandidate() const { return m_bits & IsSloppyModeHoistingCandidate; }
ALWAYS_INLINE bool isPrivateField() const { return m_bits & IsPrivateField; }
ALWAYS_INLINE bool isPrivateMethod() const { return m_bits & IsPrivateMethod; }
+ ALWAYS_INLINE bool isPrivateSetter() const { return m_bits & IsPrivateSetter; }
+ ALWAYS_INLINE bool isPrivateGetter() const { return m_bits & IsPrivateGetter; }
ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; }
ALWAYS_INLINE void setIsConst() { m_bits |= IsConst; }
@@ -60,6 +62,8 @@
ALWAYS_INLINE void setIsSloppyModeHoistingCandidate() { m_bits |= IsSloppyModeHoistingCandidate; }
ALWAYS_INLINE void setIsPrivateField() { m_bits |= IsPrivateField; }
ALWAYS_INLINE void setIsPrivateMethod() { m_bits |= IsPrivateMethod; }
+ ALWAYS_INLINE void setIsPrivateSetter() { m_bits |= IsPrivateSetter; }
+ ALWAYS_INLINE void setIsPrivateGetter() { m_bits |= IsPrivateGetter; }
ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; }
@@ -84,6 +88,8 @@
IsSloppyModeHoistingCandidate = 1 << 9,
IsPrivateField = 1 << 10,
IsPrivateMethod = 1 << 11,
+ IsPrivateGetter = 1 << 12,
+ IsPrivateSetter = 1 << 13,
};
uint16_t m_bits { 0 };
};
@@ -101,8 +107,11 @@
ALWAYS_INLINE bool isUsed() const { return m_bits & IsUsed; }
ALWAYS_INLINE bool isDeclared() const { return m_bits & IsDeclared; }
ALWAYS_INLINE bool isMethod() const { return m_bits & IsMethod; }
+ ALWAYS_INLINE bool isSetter() const { return m_bits & IsSetter; }
+ ALWAYS_INLINE bool isGetter() const { return m_bits & IsGetter; }
+ ALWAYS_INLINE bool isField() const { return !isPrivateMethodOrAcessor(); }
- bool isPrivateMethodOrAcessor() const { return isMethod(); }
+ bool isPrivateMethodOrAcessor() const { return isMethod() || isSetter() || isGetter(); }
ALWAYS_INLINE void setIsUsed() { m_bits |= IsUsed; }
ALWAYS_INLINE void setIsDeclared() { m_bits |= IsDeclared; }
@@ -119,6 +128,8 @@
IsUsed = 1 << 0,
IsDeclared = 1 << 1,
IsMethod = 1 << 2,
+ IsGetter = 1 << 3,
+ IsSetter = 1 << 4,
};
private:
@@ -195,6 +206,14 @@
bool declarePrivateMethod(const Identifier& identifier) { return declarePrivateMethod(identifier.impl()); }
bool declarePrivateMethod(const RefPtr<UniquedStringImpl>& identifier);
+ bool declarePrivateAccessor(const RefPtr<UniquedStringImpl>&, PrivateNameEntry::Traits);
+
+ bool declarePrivateSetter(const Identifier& identifier) { return declarePrivateSetter(identifier.impl()); }
+ bool declarePrivateSetter(const RefPtr<UniquedStringImpl>& identifier);
+
+ bool declarePrivateGetter(const Identifier& identifier) { return declarePrivateGetter(identifier.impl()); }
+ bool declarePrivateGetter(const RefPtr<UniquedStringImpl>& identifier);
+
Map::AddResult declarePrivateField(const RefPtr<UniquedStringImpl>& identifier)
{
auto& meta = getOrAddPrivateName(identifier.get());
Modified: trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp (272882 => 272883)
--- trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp 2021-02-15 22:28:02 UTC (rev 272882)
+++ trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp 2021-02-15 22:40:26 UTC (rev 272883)
@@ -336,7 +336,7 @@
JSObject* createPrivateMethodAccessError(JSGlobalObject* globalObject)
{
- return createTypeError(globalObject, makeString("Cannot access private method"_s), defaultSourceAppender, TypeNothing);
+ return createTypeError(globalObject, makeString("Cannot access private method or acessor"_s), defaultSourceAppender, TypeNothing);
}
JSObject* createReinstallPrivateMethodError(JSGlobalObject* globalObject)