Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix decorator evaluation private environment #16325

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
198e865
enable field-initializers-after-methods-private test
JLHwung Feb 20, 2024
06aea1a
refactor: extract computed key memoisation
JLHwung Feb 16, 2024
c30a189
handle computed static field in decorated class
JLHwung Feb 29, 2024
782505c
handle this and super call when moving computed key assignments
JLHwung Feb 29, 2024
6dd3198
handle other functional context
JLHwung Mar 4, 2024
2d34eeb
fix: insert non-static class in the key of the wrapper
JLHwung Mar 4, 2024
125a837
inject computed keys elements to the last non-comptued elements
JLHwung Mar 4, 2024
efbce42
reorg complex AST factories
JLHwung Mar 4, 2024
7714626
add new test cases
JLHwung Mar 4, 2024
4ce976c
copy test cases to 2023-11-misc--to-es-2015
JLHwung Mar 4, 2024
713ad4c
update test outputs
JLHwung Mar 5, 2024
bf86824
rewrite findLast usage for legacy node support
JLHwung Mar 5, 2024
fff405f
make old node happy
JLHwung Mar 5, 2024
f4314e5
fix: use temp class fields to host decorator expr
JLHwung Mar 5, 2024
2cd5bcc
add class-in-for-loop test
JLHwung Mar 5, 2024
707b91c
fix: skip decorators and key when replacing super within a class method
JLHwung Mar 5, 2024
f62c5c2
copy tests to es2015
JLHwung Mar 5, 2024
6969fd4
address review comments
JLHwung Mar 5, 2024
4c9c493
fix ts errors
JLHwung Mar 5, 2024
bdee20d
enable the lastPublicElement insertion point for other decorator vers…
JLHwung Mar 5, 2024
d33d87b
update test output
JLHwung Mar 5, 2024
7ad03e3
ensure typescript test works in both Babel 8 and Babel 7
JLHwung Mar 5, 2024
ccb441a
make old node happy
JLHwung Mar 5, 2024
9073384
workaround pushContainer AST sync issue
JLHwung Mar 5, 2024
af8caa6
small tweaks
JLHwung Mar 6, 2024
e856f3c
output: inject computed key assignments in the first public element
JLHwung Mar 6, 2024
20426d6
update test output
JLHwung Mar 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
449 changes: 368 additions & 81 deletions packages/babel-helper-create-class-features-plugin/src/decorators.ts

Large diffs are not rendered by default.

86 changes: 55 additions & 31 deletions packages/babel-helper-create-class-features-plugin/src/misc.ts
Expand Up @@ -120,14 +120,60 @@ export function injectInitialization(
}
}

type ComputedKeyAssignmentExpression = t.AssignmentExpression & {
left: t.Identifier;
};

/**
* Try to memoise a computed key.
* It returns undefined when the computed key is an uid reference, otherwise
* an assignment expression `memoiserId = computed key`
* @export
* @param {t.Expression} keyNode Computed key
* @param {Scope} scope The scope where memoiser id should be registered
* @param {string} hint The memoiser id hint
* @returns {(ComputedKeyAssignmentExpression | undefined)}
*/
export function memoiseComputedKey(
keyNode: t.Expression,
scope: Scope,
hint: string,
): ComputedKeyAssignmentExpression | undefined {
const isUidReference = t.isIdentifier(keyNode) && scope.hasUid(keyNode.name);
if (isUidReference) {
return;
}
const isMemoiseAssignment =
t.isAssignmentExpression(keyNode, { operator: "=" }) &&
t.isIdentifier(keyNode.left) &&
scope.hasUid(keyNode.left.name);
if (isMemoiseAssignment) {
return t.cloneNode(keyNode as ComputedKeyAssignmentExpression);
} else {
const ident = t.identifier(hint);
// Declaring in the same block scope
// Ref: https://github.com/babel/babel/pull/10029/files#diff-fbbdd83e7a9c998721c1484529c2ce92
scope.push({
id: ident,
kind: "let",
});
return t.assignmentExpression(
"=",
t.cloneNode(ident),
keyNode,
) as ComputedKeyAssignmentExpression;
}
}

export function extractComputedKeys(
path: NodePath<t.Class>,
computedPaths: NodePath<t.ClassProperty | t.ClassMethod>[],
file: File,
) {
const { scope } = path;
const declarations: t.ExpressionStatement[] = [];
const state = {
classBinding: path.node.id && path.scope.getBinding(path.node.id.name),
classBinding: path.node.id && scope.getBinding(path.node.id.name),
file,
};
for (const computedPath of computedPaths) {
Expand All @@ -142,36 +188,14 @@ export function extractComputedKeys(
// Make sure computed property names are only evaluated once (upon class definition)
// and in the right order in combination with static properties
if (!computedKey.isConstantExpression()) {
const scope = path.scope;
const isUidReference =
t.isIdentifier(computedKey.node) && scope.hasUid(computedKey.node.name);
const isMemoiseAssignment =
computedKey.isAssignmentExpression({ operator: "=" }) &&
t.isIdentifier(computedKey.node.left) &&
scope.hasUid(computedKey.node.left.name);
if (isUidReference) {
continue;
} else if (isMemoiseAssignment) {
declarations.push(t.expressionStatement(t.cloneNode(computedNode.key)));
computedNode.key = t.cloneNode(
(computedNode.key as t.AssignmentExpression).left as t.Identifier,
);
} else {
const ident = path.scope.generateUidIdentifierBasedOnNode(
computedNode.key,
);
// Declaring in the same block scope
// Ref: https://github.com/babel/babel/pull/10029/files#diff-fbbdd83e7a9c998721c1484529c2ce92
scope.push({
id: ident,
kind: "let",
});
declarations.push(
t.expressionStatement(
t.assignmentExpression("=", t.cloneNode(ident), computedNode.key),
),
);
computedNode.key = t.cloneNode(ident);
const assignment = memoiseComputedKey(
computedKey.node,
scope,
scope.generateUidBasedOnNode(computedKey.node),
);
if (assignment) {
declarations.push(t.expressionStatement(assignment));
computedNode.key = t.cloneNode(assignment.left);
}
}
}
Expand Down
17 changes: 15 additions & 2 deletions packages/babel-helper-replace-supers/src/index.ts
Expand Up @@ -419,16 +419,29 @@ export default class ReplaceSupers {
}

replace() {
const { methodPath } = this;
// https://github.com/babel/babel/issues/11994
if (this.opts.refToPreserve) {
this.methodPath.traverse(unshadowSuperBindingVisitor, {
methodPath.traverse(unshadowSuperBindingVisitor, {
refName: this.opts.refToPreserve.name,
});
}

const handler = this.constantSuper ? looseHandlers : specHandlers;

memberExpressionToFunctions<ReplaceState>(this.methodPath, visitor, {
// todo: this should have been handled by the environmentVisitor,
// consider add visitSelf support for the path.traverse
// @ts-expect-error: Refine typings in packages/babel-traverse/src/types.ts
// shouldSkip is accepted in traverseNode
visitor.shouldSkip = (path: NodePath) => {
if (path.parentPath === methodPath) {
if (path.parentKey === "decorators" || path.parentKey === "key") {
return true;
}
}
};

memberExpressionToFunctions<ReplaceState>(methodPath, visitor, {
file: this.file,
scope: this.methodPath.scope,
isDerivedConstructor: this.isDerivedConstructor,
Expand Down
@@ -1,4 +1,5 @@
var _initStatic, _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _init_computedKey2, _init_computedKey3, _init_computedKey4, _init_computedKey5, _init_computedKey6, _computedKey, _init_computedKey7, _Foo;
let _computedKey;
var _initStatic, _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _init_computedKey2, _init_computedKey3, _init_computedKey4, _init_computedKey5, _init_computedKey6, _init_computedKey7, _Foo;
const logs = [];
const dec = (value, context) => {
logs.push(context.name);
Expand Down
@@ -1,4 +1,5 @@
var _initStatic, _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _init_computedKey2, _init_computedKey3, _init_computedKey4, _init_computedKey5, _init_computedKey6, _computedKey, _init_computedKey7;
let _computedKey;
var _initStatic, _init_a, _init_a2, _get_a, _set_a, _init_computedKey, _init_computedKey2, _init_computedKey3, _init_computedKey4, _init_computedKey5, _init_computedKey6, _init_computedKey7;
const logs = [];
const dec = (value, context) => {
logs.push(context.name);
Expand All @@ -9,7 +10,6 @@ const f = () => {
[Symbol.toPrimitive]: () => (logs.push("calling toPrimitive"), "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 () {
Expand Down Expand Up @@ -76,7 +76,7 @@ class Foo {
this.#H = v;
}
static #I = _init_computedKey7(this);
static get [_computedKey]() {
static get [_computedKey = babelHelpers.toPropertyKey(f())]() {
return this.#I;
}
static set [_computedKey](v) {
Expand Down
@@ -1,73 +1,51 @@
var _initClass, _A, _temp, _initClass2, _C, _temp2, _initClass3, _D, _temp3, _initClass4, _decorated_class, _temp4, _Class2, _initClass5, _G, _temp5, _initClass6, _decorated_class2, _temp6, _Class3, _initClass7, _H, _temp7, _initClass8, _K, _temp8;
let _A2, _C2, _D2, _ref, _G2, _ref2, _H2, _K2;
var _initClass, _A, _Class, _A3, _initClass2, _C, _Class2, _C3, _initClass3, _D, _Class3, _D3, _initClass4, _decorated_class, _Class4, _Class5, _initClass5, _G, _Class6, _G3, _initClass6, _decorated_class2, _Class7, _Class8, _initClass7, _H, _Class9, _H3, _initClass8, _K, _Class10, _K3;
const dec = () => {};
const A = (new (_temp = class extends babelHelpers.identity {
const A = (new (_A2 = (_A3 = class A {}, [_A, _initClass] = babelHelpers.applyDecs(_A3, [], [dec]), _A3), (_Class = class extends babelHelpers.identity {
constructor() {
super(_A), (() => {})(), _initClass();
}
}, (_A2 => {
class A {}
_A2 = A;
[_A, _initClass] = babelHelpers.applyDecs(_A2, [], [dec]);
})(), _temp)(), _A);
const B = (new (_temp2 = class extends babelHelpers.identity {
}, babelHelpers.defineProperty(_Class, _A2, void 0), _Class))(), _A);
const B = (new (_C2 = (_C3 = class C {}, [_C, _initClass2] = babelHelpers.applyDecs(_C3, [], [dec]), _C3), (_Class2 = class extends babelHelpers.identity {
constructor() {
super(_C), (() => {})(), _initClass2();
}
}, (_C2 => {
class C {}
_C2 = C;
[_C, _initClass2] = babelHelpers.applyDecs(_C2, [], [dec]);
})(), _temp2)(), _C);
const D = (new (_temp3 = class extends babelHelpers.identity {
}, babelHelpers.defineProperty(_Class2, _C2, void 0), _Class2))(), _C);
const D = (new (_D2 = (_D3 = class D {}, [_D, _initClass3] = babelHelpers.applyDecs(_D3, [], [dec]), _D3), (_Class3 = class extends babelHelpers.identity {
constructor() {
super(_D), (() => {})(), _initClass3();
}
}, (_D2 => {
class D {}
_D2 = D;
[_D, _initClass3] = babelHelpers.applyDecs(_D2, [], [dec]);
})(), _temp3)(), _D);
const E = ((new (_temp4 = class extends babelHelpers.identity {
}, babelHelpers.defineProperty(_Class3, _D2, void 0), _Class3))(), _D);
const E = ((new (_ref = (_Class5 = class _ref {}, [_decorated_class, _initClass4] = babelHelpers.applyDecs(_Class5, [], [dec]), _Class5), (_Class4 = class extends babelHelpers.identity {
constructor() {
super(_decorated_class), (() => {})(), _initClass4();
}
}, (_Class2 = class {}, [_decorated_class, _initClass4] = babelHelpers.applyDecs(_Class2, [], [dec])), _temp4)(), _decorated_class), 123);
const F = [(new (_temp5 = class extends babelHelpers.identity {
}, babelHelpers.defineProperty(_Class4, _ref, void 0), _Class4))(), _decorated_class), 123);
const F = [(new (_G2 = (_G3 = class G {}, [_G, _initClass5] = babelHelpers.applyDecs(_G3, [], [dec]), _G3), (_Class6 = class extends babelHelpers.identity {
constructor() {
super(_G), (() => {})(), _initClass5();
}
}, (_G2 => {
class G {}
_G2 = G;
[_G, _initClass5] = babelHelpers.applyDecs(_G2, [], [dec]);
})(), _temp5)(), _G), (new (_temp6 = class extends babelHelpers.identity {
}, babelHelpers.defineProperty(_Class6, _G2, void 0), _Class6))(), _G), (new (_ref2 = (_Class8 = class _ref2 {}, [_decorated_class2, _initClass6] = babelHelpers.applyDecs(_Class8, [], [dec]), _Class8), (_Class7 = class extends babelHelpers.identity {
constructor() {
super(_decorated_class2), (() => {})(), _initClass6();
}
}, (_Class3 = class {}, [_decorated_class2, _initClass6] = babelHelpers.applyDecs(_Class3, [], [dec])), _temp6)(), _decorated_class2)];
const H = (new (_temp7 = class extends babelHelpers.identity {
}, babelHelpers.defineProperty(_Class7, _ref2, void 0), _Class7))(), _decorated_class2)];
const H = (new (_H2 = (_H3 = class H extends I {}, [_H, _initClass7] = babelHelpers.applyDecs(_H3, [], [dec]), _H3), (_Class9 = class extends babelHelpers.identity {
constructor() {
super(_H), (() => {})(), _initClass7();
}
}, (_H2 => {
class H extends I {}
_H2 = H;
[_H, _initClass7] = babelHelpers.applyDecs(_H2, [], [dec]);
})(), _temp7)(), _H);
const J = (new (_temp8 = class extends babelHelpers.identity {
}, babelHelpers.defineProperty(_Class9, _H2, void 0), _Class9))(), _H);
const J = (new (_K2 = (_K3 = class K extends L {}, [_K, _initClass8] = babelHelpers.applyDecs(_K3, [], [dec]), _K3), (_Class10 = class extends babelHelpers.identity {
constructor() {
super(_K), (() => {})(), _initClass8();
}
}, (_K2 => {
class K extends L {}
_K2 = K;
[_K, _initClass8] = babelHelpers.applyDecs(_K2, [], [dec]);
})(), _temp8)(), _K);
}, babelHelpers.defineProperty(_Class10, _K2, void 0), _Class10))(), _K);
function classFactory() {
var _initClass9, _decorated_class3, _temp9, _Class5;
return new (_temp9 = class extends babelHelpers.identity {
let _ref3;
var _initClass9, _decorated_class3, _Class11, _Class12;
return new (_ref3 = (_Class12 = class _ref3 {}, [_decorated_class3, _initClass9] = babelHelpers.applyDecs(_Class12, [], [dec]), _Class12), (_Class11 = class extends babelHelpers.identity {
constructor() {
super(_decorated_class3), (() => {})(), _initClass9();
}
}, (_Class5 = class {}, [_decorated_class3, _initClass9] = babelHelpers.applyDecs(_Class5, [], [dec])), _temp9)(), _decorated_class3;
}, babelHelpers.defineProperty(_Class11, _ref3, void 0), _Class11))(), _decorated_class3;
}
@@ -1,24 +1,17 @@
var _initClass, _temp, _initClass2, _temp2;
let _Foo2, _Bar2;
var _initClass, _Class, _Foo3, _initClass2, _Class2, _Bar3;
const dec = () => {};
let _Foo;
new (_temp = class extends babelHelpers.identity {
new (_Foo2 = (_Foo3 = class Foo {}, [_Foo, _initClass] = babelHelpers.applyDecs(_Foo3, [], [dec]), _Foo3), (_Class = class extends babelHelpers.identity {
constructor() {
(super(_Foo), babelHelpers.defineProperty(this, "field", 123)), _initClass();
}
}, (_Foo2 => {
class Foo {}
_Foo2 = Foo;
[_Foo, _initClass] = babelHelpers.applyDecs(_Foo2, [], [dec]);
})(), _temp)();
}, babelHelpers.defineProperty(_Class, _Foo2, void 0), _Class))();
let _Bar;
new (_temp2 = class extends babelHelpers.identity {
new (_Bar2 = (_Bar3 = class Bar extends _Foo {}, [_Bar, _initClass2] = babelHelpers.applyDecs(_Bar3, [], [dec]), _Bar3), (_Class2 = class extends babelHelpers.identity {
constructor() {
(super(_Bar), babelHelpers.defineProperty(this, "field", ((() => {
this.otherField = 456;
})(), 123))), _initClass2();
}
}, (_Bar2 => {
class Bar extends _Foo {}
_Bar2 = Bar;
[_Bar, _initClass2] = babelHelpers.applyDecs(_Bar2, [], [dec]);
})(), _temp2)();
}, babelHelpers.defineProperty(_Class2, _Bar2, void 0), _Class2))();
@@ -1,28 +1,25 @@
var _initClass, _x, _A, _Class_brand, _B, _temp;
let _Foo2;
var _initClass, _Class, _x, _A, _Class_brand, _B, _Foo3;
const dec = () => {};
let hasX, hasA, hasM;
let _Foo;
new (_x = /*#__PURE__*/new WeakMap(), _A = /*#__PURE__*/new WeakMap(), _Class_brand = /*#__PURE__*/new WeakSet(), _B = /*#__PURE__*/new WeakMap(), (_temp = class extends babelHelpers.identity {
new (_x = /*#__PURE__*/new WeakMap(), _A = /*#__PURE__*/new WeakMap(), _Class_brand = /*#__PURE__*/new WeakSet(), _B = /*#__PURE__*/new WeakMap(), _Foo2 = (_Foo3 = class Foo {
static get a() {
return babelHelpers.classPrivateFieldGet2(_B, this);
}
static set a(v) {
babelHelpers.classPrivateFieldSet2(_B, this, v);
}
static m() {}
}, [_Foo, _initClass] = babelHelpers.applyDecs(_Foo3, [], [dec]), _Foo3), (_Class = class extends babelHelpers.identity {
constructor() {
(super(_Foo), babelHelpers.classPrivateMethodInitSpec(this, _Class_brand), babelHelpers.classPrivateFieldInitSpec(this, _x, void 0), babelHelpers.classPrivateFieldInitSpec(this, _A, void 0), babelHelpers.defineProperty(this, "x", void 0), babelHelpers.classPrivateFieldInitSpec(this, _B, void 0), this), (() => {
hasX = o => _x.has(babelHelpers.checkInRHS(o));
hasA = o => _Class_brand.has(babelHelpers.checkInRHS(o));
hasM = o => _Class_brand.has(babelHelpers.checkInRHS(o));
})(), _initClass();
}
}, (_Foo2 => {
class Foo {
static get a() {
return babelHelpers.classPrivateFieldGet2(_B, this);
}
static set a(v) {
babelHelpers.classPrivateFieldSet2(_B, this, v);
}
static m() {}
}
_Foo2 = Foo;
[_Foo, _initClass] = babelHelpers.applyDecs(_Foo2, [], [dec]);
})(), _temp))();
}, babelHelpers.defineProperty(_Class, _Foo2, void 0), _Class))();
function _get_a(_this) {
return babelHelpers.classPrivateFieldGet2(_A, _this);
}
Expand Down
@@ -1,16 +1,13 @@
var _initClass, _temp;
let _Foo2;
var _initClass, _Class, _Foo3;
const dec = () => {};
let _Foo;
new (_temp = class extends babelHelpers.identity {
new (_Foo2 = (_Foo3 = class Foo {}, [_Foo, _initClass] = babelHelpers.applyDecs(_Foo3, [], [dec]), _Foo3), (_Class = class extends babelHelpers.identity {
constructor() {
(super(_Foo), babelHelpers.defineProperty(this, "field", ((() => {
this;
})(), this))), (() => {
this;
})(), _initClass();
}
}, (_Foo2 => {
class Foo {}
_Foo2 = Foo;
[_Foo, _initClass] = babelHelpers.applyDecs(_Foo2, [], [dec]);
})(), _temp)();
}, babelHelpers.defineProperty(_Class, _Foo2, void 0), _Class))();
@@ -1,13 +1,10 @@
var _initClass, _temp;
let _Foo2;
var _initClass, _Class, _Foo3;
const dec = () => {};
let _Foo;
new (_temp = class extends babelHelpers.identity {
new (_Foo2 = (_Foo3 = class Foo {}, [_Foo, _initClass] = babelHelpers.applyDecs(_Foo3, [], [dec]), _Foo3), (_Class = class extends babelHelpers.identity {
constructor() {
(super(_Foo), babelHelpers.defineProperty(this, "foo", new _Foo())), _initClass();
}
}, (_Foo2 => {
class Foo {}
_Foo2 = Foo;
[_Foo, _initClass] = babelHelpers.applyDecs(_Foo2, [], [dec]);
})(), _temp)();
}, babelHelpers.defineProperty(_Class, _Foo2, void 0), _Class))();
const foo = new _Foo();