Skip to content

Commit

Permalink
Ensure the [[@@toPrimitive]] call of a decorated class member key i…
Browse files Browse the repository at this point in the history
…s invoked once (#16161)

* refactor: unify computed key memoise criteria

* fix: memoise the toPropertyKey results of computed key

* update test outputs

* remove debug test case
  • Loading branch information
JLHwung committed Dec 8, 2023
1 parent d677b3c commit a1da072
Show file tree
Hide file tree
Showing 234 changed files with 921 additions and 1,147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,10 @@ function createSetFunctionNameCall(
]);
}

function createToPropertyKeyCall(state: PluginPass, propertyKey: t.Expression) {
return t.callExpression(state.addHelper("toPropertyKey"), [propertyKey]);
}

function transformClass(
path: NodePath<t.ClassExpression | t.ClassDeclaration>,
state: PluginPass,
Expand Down Expand Up @@ -590,18 +594,17 @@ function transformClass(

const newId = generateClassPrivateUid();
const newField = generateClassProperty(newId, value, isStatic);

const keyPath = element.get("key");
const [newPath] = element.replaceWith(newField);
const keyType = key.type;

addProxyAccessorsFor(
path.node.id,
newPath,
computed &&
!scopeParent.isStatic(key) &&
keyType !== "StringLiteral" &&
keyType !== "NumericLiteral" &&
keyType !== "BigIntLiteral"
? memoiseExpression(key as t.Expression, "computedKey")
computed && !keyPath.isConstantExpression()
? memoiseExpression(
createToPropertyKeyCall(state, key as t.Expression),
"computedKey",
)
: key,
newId,
version,
Expand Down Expand Up @@ -703,8 +706,11 @@ function transformClass(
const isComputed =
"computed" in element.node && element.node.computed === true;
if (isComputed) {
if (!scopeParent.isStatic(node.key)) {
node.key = memoiseExpression(node.key as t.Expression, "computedKey");
if (!element.get("key").isConstantExpression()) {
node.key = memoiseExpression(
createToPropertyKeyCall(state, node.key as t.Expression),
"computedKey",
);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const logs = [];
const dec = (value, context) => { logs.push(context.name) };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => "f()" }; };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => (logs.push("calling toPrimitive"), "f()") }; };
class Foo {
@dec static accessor a;
@dec static accessor #a;
Expand All @@ -17,4 +17,4 @@ class Foo {
@dec static accessor [f()];
}

expect(logs).toStrictEqual(["computing f", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
expect(logs).toStrictEqual(["computing f", "calling toPrimitive", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const logs = [];
const dec = (value, context) => { logs.push(context.name) };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => "f()" }; };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => (logs.push("calling toPrimitive"), "f()") }; };
class Foo {
@dec static accessor a;
@dec static accessor #a;
Expand All @@ -17,4 +17,4 @@ class Foo {
@dec static accessor [f()];
}

expect(logs).toStrictEqual(["computing f", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
expect(logs).toStrictEqual(["computing f", "calling toPrimitive", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
var _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _computedKey, _init_computedKey2, _init_computedKey3, _computedKey2, _init_computedKey4, _init_computedKey5, _computedKey3, _init_computedKey6, _computedKey4, _init_computedKey7, _initStatic, _class;
var _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _init_computedKey2, _init_computedKey3, _init_computedKey4, _init_computedKey5, _init_computedKey6, _computedKey, _init_computedKey7, _initStatic, _class;
const logs = [];
const dec = (value, context) => {
logs.push(context.name);
};
const f = () => {
logs.push("computing f");
return {
[Symbol.toPrimitive]: () => "f()"
[Symbol.toPrimitive]: () => (logs.push("calling toPrimitive"), "f()")
};
};
_computedKey = "c";
_computedKey2 = 1;
_computedKey3 = 3n;
_computedKey4 = f();
_computedKey = babelHelpers.toPropertyKey(f());
var _a = /*#__PURE__*/new WeakMap();
class Foo {
constructor() {
Expand All @@ -33,10 +30,10 @@ class Foo {
static set "b"(v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _C, v);
}
static get [_computedKey]() {
static get ["c"]() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _D);
}
static set [_computedKey](v) {
static set ["c"](v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _D, v);
}
static get 0() {
Expand All @@ -45,10 +42,10 @@ class Foo {
static set 0(v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _E, v);
}
static get [_computedKey2]() {
static get [1]() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _F);
}
static set [_computedKey2](v) {
static set [1](v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _F, v);
}
static get 2n() {
Expand All @@ -57,16 +54,16 @@ class Foo {
static set 2n(v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _G, v);
}
static get [_computedKey3]() {
static get [3n]() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _H);
}
static set [_computedKey3](v) {
static set [3n](v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _H, v);
}
static get [_computedKey4]() {
static get [_computedKey]() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _I);
}
static set [_computedKey4](v) {
static set [_computedKey](v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _I, v);
}
}
Expand All @@ -82,7 +79,7 @@ function _get_a2() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, _class, _B);
}, function (value) {
babelHelpers.classStaticPrivateFieldSpecSet(this, _class, _B, value);
}], [dec, 6, "b"], [dec, 6, _computedKey], [dec, 6, 0], [dec, 6, _computedKey2], [dec, 6, 2n], [dec, 6, _computedKey3], [dec, 6, _computedKey4]], []);
}], [dec, 6, "b"], [dec, 6, "c"], [dec, 6, 0], [dec, 6, 1], [dec, 6, 2n], [dec, 6, 3n], [dec, 6, _computedKey]], []);
_initStatic(_class);
})();
var _A = {
Expand Down Expand Up @@ -121,4 +118,4 @@ var _I = {
writable: true,
value: _init_computedKey7(_class)
};
expect(logs).toStrictEqual(["computing f", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
expect(logs).toStrictEqual(["computing f", "calling toPrimitive", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
var _init_a, _init_b, _computedKey, _init_computedKey, _initProto, _class;
var _init_a, _init_b, _init_computedKey, _initProto, _class;
const dec = () => {};
_computedKey = 'c';
var _A = /*#__PURE__*/new WeakMap();
var _B = /*#__PURE__*/new WeakMap();
var _C = /*#__PURE__*/new WeakMap();
Expand Down Expand Up @@ -31,12 +30,12 @@ class Foo {
set b(v) {
babelHelpers.classPrivateFieldSet(this, _B, v);
}
get [_computedKey]() {
get ['c']() {
return babelHelpers.classPrivateFieldGet(this, _C);
}
set [_computedKey](v) {
set ['c'](v) {
babelHelpers.classPrivateFieldSet(this, _C, v);
}
}
_class = Foo;
[_init_a, _init_b, _init_computedKey, _initProto] = babelHelpers.applyDecs(_class, [[dec, 1, "a"], [dec, 1, "b"], [dec, 1, _computedKey]], []);
[_init_a, _init_b, _init_computedKey, _initProto] = babelHelpers.applyDecs(_class, [[dec, 1, "a"], [dec, 1, "b"], [dec, 1, 'c']], []);
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
var _init_a, _init_b, _computedKey, _init_computedKey, _initStatic, _class;
var _init_a, _init_b, _init_computedKey, _initStatic, _class;
const dec = () => {};
_computedKey = 'c';
class Foo {
static get a() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _A);
Expand All @@ -14,16 +13,16 @@ class Foo {
static set b(v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _B, v);
}
static get [_computedKey]() {
static get ['c']() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _C);
}
static set [_computedKey](v) {
static set ['c'](v) {
babelHelpers.classStaticPrivateFieldSpecSet(this, Foo, _C, v);
}
}
_class = Foo;
(() => {
[_init_a, _init_b, _init_computedKey, _initStatic] = babelHelpers.applyDecs(_class, [[dec, 6, "a"], [dec, 6, "b"], [dec, 6, _computedKey]], []);
[_init_a, _init_b, _init_computedKey, _initStatic] = babelHelpers.applyDecs(_class, [[dec, 6, "a"], [dec, 6, "b"], [dec, 6, 'c']], []);
_initStatic(_class);
})();
var _A = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const logs = [];
const dec = (value, context) => { logs.push(context.name) };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => "f()" }; };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => (logs.push("calling toPrimitive"), "f()") }; };
class Foo {
@dec static accessor a;
@dec static accessor #a;
Expand All @@ -17,4 +17,4 @@ class Foo {
@dec static accessor [f()];
}

expect(logs).toStrictEqual(["computing f", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
expect(logs).toStrictEqual(["computing f", "calling toPrimitive", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const logs = [];
const dec = (value, context) => { logs.push(context.name) };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => "f()" }; };
const f = () => { logs.push("computing f"); return { [Symbol.toPrimitive]: () => (logs.push("calling toPrimitive"), "f()") }; };
class Foo {
@dec static accessor a;
@dec static accessor #a;
Expand All @@ -17,4 +17,4 @@ class Foo {
@dec static accessor [f()];
}

expect(logs).toStrictEqual(["computing f", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
expect(logs).toStrictEqual(["computing f", "calling toPrimitive", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
var _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _computedKey, _init_computedKey2, _init_computedKey3, _computedKey2, _init_computedKey4, _init_computedKey5, _computedKey3, _init_computedKey6, _computedKey4, _init_computedKey7, _initStatic;
var _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _init_computedKey2, _init_computedKey3, _init_computedKey4, _init_computedKey5, _init_computedKey6, _computedKey, _init_computedKey7, _initStatic;
const logs = [];
const dec = (value, context) => {
logs.push(context.name);
};
const f = () => {
logs.push("computing f");
return {
[Symbol.toPrimitive]: () => "f()"
[Symbol.toPrimitive]: () => (logs.push("calling toPrimitive"), "f()")
};
};
_computedKey = "c";
_computedKey2 = 1;
_computedKey3 = 3n;
_computedKey4 = f();
_computedKey = babelHelpers.toPropertyKey(f());
class Foo {
static {
[_init_a, _init_a2, _get_a, _set_a, _init_computedKey, _init_computedKey2, _init_computedKey3, _init_computedKey4, _init_computedKey5, _init_computedKey6, _init_computedKey7, _initStatic] = babelHelpers.applyDecs(this, [[dec, 6, "a"], [dec, 6, "a", function () {
return this.#B;
}, function (value) {
this.#B = value;
}], [dec, 6, "b"], [dec, 6, _computedKey], [dec, 6, 0], [dec, 6, _computedKey2], [dec, 6, 2n], [dec, 6, _computedKey3], [dec, 6, _computedKey4]], []);
}], [dec, 6, "b"], [dec, 6, "c"], [dec, 6, 0], [dec, 6, 1], [dec, 6, 2n], [dec, 6, 3n], [dec, 6, _computedKey]], []);
_initStatic(this);
}
static #A = _init_a(this);
Expand All @@ -44,10 +41,10 @@ class Foo {
this.#C = v;
}
static #D = _init_computedKey2(this);
static get [_computedKey]() {
static get ["c"]() {
return this.#D;
}
static set [_computedKey](v) {
static set ["c"](v) {
this.#D = v;
}
static #E = _init_computedKey3(this);
Expand All @@ -58,10 +55,10 @@ class Foo {
this.#E = v;
}
static #F = _init_computedKey4(this);
static get [_computedKey2]() {
static get [1]() {
return this.#F;
}
static set [_computedKey2](v) {
static set [1](v) {
this.#F = v;
}
static #G = _init_computedKey5(this);
Expand All @@ -72,18 +69,18 @@ class Foo {
this.#G = v;
}
static #H = _init_computedKey6(this);
static get [_computedKey3]() {
static get [3n]() {
return this.#H;
}
static set [_computedKey3](v) {
static set [3n](v) {
this.#H = v;
}
static #I = _init_computedKey7(this);
static get [_computedKey4]() {
static get [_computedKey]() {
return this.#I;
}
static set [_computedKey4](v) {
static set [_computedKey](v) {
this.#I = v;
}
}
expect(logs).toStrictEqual(["computing f", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
expect(logs).toStrictEqual(["computing f", "calling toPrimitive", "a", "#a", "b", "c", "0", "1", "2", "3", "f()"]);
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
var _init_a, _init_b, _computedKey, _init_computedKey, _initProto;
var _init_a, _init_b, _init_computedKey, _initProto;
const dec = () => {};
_computedKey = 'c';
class Foo {
static {
[_init_a, _init_b, _init_computedKey, _initProto] = babelHelpers.applyDecs(this, [[dec, 1, "a"], [dec, 1, "b"], [dec, 1, _computedKey]], []);
[_init_a, _init_b, _init_computedKey, _initProto] = babelHelpers.applyDecs(this, [[dec, 1, "a"], [dec, 1, "b"], [dec, 1, 'c']], []);
}
#A = (_initProto(this), _init_a(this));
get a() {
Expand All @@ -20,10 +19,10 @@ class Foo {
this.#B = v;
}
#C = _init_computedKey(this, 456);
get [_computedKey]() {
get ['c']() {
return this.#C;
}
set [_computedKey](v) {
set ['c'](v) {
this.#C = v;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
var _init_a, _init_b, _computedKey, _init_computedKey, _initStatic;
var _init_a, _init_b, _init_computedKey, _initStatic;
const dec = () => {};
_computedKey = 'c';
class Foo {
static {
[_init_a, _init_b, _init_computedKey, _initStatic] = babelHelpers.applyDecs(this, [[dec, 6, "a"], [dec, 6, "b"], [dec, 6, _computedKey]], []);
[_init_a, _init_b, _init_computedKey, _initStatic] = babelHelpers.applyDecs(this, [[dec, 6, "a"], [dec, 6, "b"], [dec, 6, 'c']], []);
_initStatic(this);
}
static #A = _init_a(this);
Expand All @@ -21,10 +20,10 @@ class Foo {
this.#B = v;
}
static #C = _init_computedKey(this, 456);
static get [_computedKey]() {
static get ['c']() {
return this.#C;
}
static set [_computedKey](v) {
static set ['c'](v) {
this.#C = v;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var _computedKey, _computedKey2, _initProto, _class;
const dec = () => {};
_computedKey = getKey();
_computedKey2 = getKey();
_computedKey = babelHelpers.toPropertyKey(getKey());
_computedKey2 = babelHelpers.toPropertyKey(getKey());
class Foo {
constructor(...args) {
_initProto(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var _computedKey, _computedKey2, _initProto, _class;
const dec = () => {};
_computedKey = getKeyI();
_computedKey2 = getKeyJ();
_computedKey = babelHelpers.toPropertyKey(getKeyI());
_computedKey2 = babelHelpers.toPropertyKey(getKeyJ());
class Foo {
constructor(...args) {
_initProto(this);
Expand Down

0 comments on commit a1da072

Please sign in to comment.