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

More aggressively inline decorators in the static block #16294

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
1 change: 0 additions & 1 deletion babel.config.js
Expand Up @@ -543,7 +543,6 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
if (left.value === false) return res.replace(right.replacement);
if (right.value === false) return res.replace(left.replacement);
if (left.unrelated && right.unrelated) return res.unrelated();
console.log(left, right);
return res.replace(
t.logicalExpression("||", left.replacement, right.replacement)
);
Expand Down
Expand Up @@ -751,6 +751,32 @@ function createPrivateBrandCheckClosure(brandName: t.PrivateName) {
);
}

// Check if the expression does not reference function-specific
// context or the given identifier name.
// `true` means "maybe" and `false` means "no".
function usesFunctionContextOrRef(
expression: t.Node,
refName: string | undefined,
) {
try {
t.traverseFast(expression, node => {
if (
t.isThisExpression(node) ||
t.isSuper(node) ||
t.isIdentifier(node, { name: "arguments" }) ||
(t.isMetaProperty(node) && node.meta.name !== "import") ||
(refName && t.isIdentifier(node, { name: refName }))
) {
// TODO: Add early return support to t.traverseFast
throw null;
}
});
return false;
} catch {
return true;
}
}

function checkPrivateMethodUpdateError(
path: NodePath<t.Class>,
decoratedPrivateMethods: Set<string>,
Expand Down Expand Up @@ -810,6 +836,8 @@ function transformClass(

const classDecorators = path.node.decorators;
let hasElementDecorators = false;
let hasComputedKeysSideEffects = false;
let elemDecsUseFnContext = false;

const generateClassPrivateUid = createLazyPrivateUidGeneratorForClass(path);

Expand Down Expand Up @@ -868,6 +896,9 @@ function transformClass(
break;
}
hasElementDecorators = true;
elemDecsUseFnContext ||= element.node.decorators.some(dec =>
usesFunctionContextOrRef(dec, path.node.id?.name),
);
} else if (element.node.type === "ClassAccessorProperty") {
// @ts-expect-error todo: propertyVisitor.ClassAccessorProperty should be callable. Improve typings.
propertyVisitor.ClassAccessorProperty(
Expand Down Expand Up @@ -896,6 +927,10 @@ function transformClass(
isStatic,
);
}

if ("computed" in element.node && element.node.computed) {
hasComputedKeysSideEffects ||= !scopeParent.isStatic(element.node.key);
}
}

if (!classDecorators && !hasElementDecorators) {
Expand All @@ -922,14 +957,16 @@ function transformClass(
// Memoise the this value `a.b` of decorator member expressions `@a.b.dec`,
type HandleDecoratorExpressionsResult = {
// whether the whole decorator list requires memoisation
needMemoise: boolean;
hasSideEffects: boolean;
usesFnContext: boolean;
// the this value of each decorator if applicable
decoratorsThis: (t.Expression | undefined)[];
};
function handleDecoratorExpressions(
expressions: t.Expression[],
): HandleDecoratorExpressionsResult {
let needMemoise = false;
let hasSideEffects = false;
let usesFnContext = false;
const decoratorsThis: (t.Expression | null)[] = [];
for (const expression of expressions) {
let object;
Expand All @@ -954,11 +991,21 @@ function transformClass(
}
}
decoratorsThis.push(object);
needMemoise ||= !scopeParent.isStatic(expression);
hasSideEffects ||= !scopeParent.isStatic(expression);
usesFnContext ||= usesFunctionContextOrRef(
expression,
path.node.id?.name,
);
}
return { needMemoise, decoratorsThis };
return { hasSideEffects, usesFnContext, decoratorsThis };
}

const willExtractSomeElemDecs =
hasComputedKeysSideEffects ||
(process.env.BABEL_8_BREAKING
? elemDecsUseFnContext
: elemDecsUseFnContext || version !== "2023-11");

let needsDeclaraionForClassBinding = false;
let classDecorationsFlag = 0;
let classDecorations: t.Expression[] = [];
Expand All @@ -971,7 +1018,7 @@ function transformClass(
path.node.decorators = null;

const decoratorExpressions = classDecorators.map(el => el.expression);
const { needMemoise, decoratorsThis } =
const { hasSideEffects, decoratorsThis } =
handleDecoratorExpressions(decoratorExpressions);

const { haveThis, decs } = generateDecorationList(
Expand All @@ -982,7 +1029,7 @@ function transformClass(
classDecorationsFlag = haveThis ? 1 : 0;
classDecorations = decs;

if (needMemoise) {
if (hasSideEffects && willExtractSomeElemDecs) {
classDecorationsId = memoiseExpression(
t.arrayExpression(classDecorations),
"classDecs",
Expand Down Expand Up @@ -1042,16 +1089,22 @@ function transformClass(

if (hasDecorators) {
const decoratorExpressions = decorators.map(d => d.expression);
const { needMemoise, decoratorsThis } =
handleDecoratorExpressions(decoratorExpressions);
const {
hasSideEffects,
usesFnContext: usesFunctionContext,
decoratorsThis,
} = handleDecoratorExpressions(decoratorExpressions);
const { decs, haveThis } = generateDecorationList(
decoratorExpressions,
decoratorsThis,
version,
);
decoratorsHaveThis = haveThis;
decoratorsArray = decs.length === 1 ? decs[0] : t.arrayExpression(decs);
if (needMemoise) {
if (
usesFunctionContext ||
(hasSideEffects && willExtractSomeElemDecs)
) {
decoratorsArray = memoiseExpression(decoratorsArray, name + "Decs");
}
}
Expand Down
@@ -1,4 +1,4 @@
var _initStatic, _initClass, _accessorDecs, _init_accessor, _init_extra_accessor, _getterDecs, _setterDecs, _methodDecs, _propertyDecs, _init_property, _init_extra_property, _A, _temp;
var _initStatic, _initClass, _init_accessor, _init_extra_accessor, _init_property, _init_extra_property, _A, _temp;
let original, replaced, accessorThis, getterThis, setterThis, methodThis, propertyThis, classThis;
function dec(Klass, context) {
original = Klass;
Expand All @@ -15,11 +15,6 @@ function captureInitializerThis(callback) {
});
};
}
_accessorDecs = captureInitializerThis(v => accessorThis = v);
_getterDecs = captureInitializerThis(v => getterThis = v);
_setterDecs = captureInitializerThis(v => setterThis = v);
_methodDecs = captureInitializerThis(v => methodThis = v);
_propertyDecs = captureInitializerThis(v => propertyThis = v);
let _Foo;
new (_A = /*#__PURE__*/new WeakMap(), (_temp = class extends babelHelpers.identity {
constructor() {
Expand All @@ -44,7 +39,7 @@ new (_A = /*#__PURE__*/new WeakMap(), (_temp = class extends babelHelpers.identi
({
e: [_init_accessor, _init_extra_accessor, _init_property, _init_extra_property, _initStatic],
c: [_Foo, _initClass]
} = babelHelpers.applyDecs2311(_Foo2, [dec], [[_accessorDecs, 9, "accessor"], [_getterDecs, 11, "getter"], [_setterDecs, 12, "setter"], [_methodDecs, 10, "method"], [_propertyDecs, 8, "property"]]));
} = babelHelpers.applyDecs2311(_Foo2, [dec], [[captureInitializerThis(v => accessorThis = v), 9, "accessor"], [captureInitializerThis(v => getterThis = v), 11, "getter"], [captureInitializerThis(v => setterThis = v), 12, "setter"], [captureInitializerThis(v => methodThis = v), 10, "method"], [captureInitializerThis(v => propertyThis = v), 8, "property"]]));
_initStatic(_Foo2);
})();
})(), _temp))();
@@ -1,20 +1,18 @@
var _initClass, _classDecs, _initClass2, _classDecs2, _Bar2;
var _initClass, _initClass2, _Bar2;
const dec = () => {};
_classDecs = [dec1];
let _Bar;
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
class Bar {
static {
[_Bar, _initClass] = babelHelpers.applyDecs2311(this, _classDecs, []).c;
[_Bar, _initClass] = babelHelpers.applyDecs2311(this, [dec1], []).c;
}
static {
_initClass();
}
}
_classDecs2 = [dec2];
let _Foo;
class Foo extends (_Bar2 = _Bar) {
static {
[_Foo, _initClass2] = babelHelpers.applyDecs2311(this, _classDecs2, [], 0, void 0, _Bar2).c;
[_Foo, _initClass2] = babelHelpers.applyDecs2311(this, [dec2], [], 0, void 0, _Bar2).c;
}
static {
_initClass2();
Expand Down
@@ -1,4 +1,4 @@
var _initStatic, _initClass, _accessorDecs, _init_accessor, _init_extra_accessor, _getterDecs, _setterDecs, _methodDecs, _propertyDecs, _init_property, _init_extra_property;
var _initStatic, _initClass, _init_accessor, _init_extra_accessor, _init_property, _init_extra_property;
let original, replaced, accessorThis, getterThis, setterThis, methodThis, propertyThis, classThis;
function dec(Klass, context) {
original = Klass;
Expand All @@ -15,11 +15,6 @@ function captureInitializerThis(callback) {
});
};
}
_accessorDecs = captureInitializerThis(v => accessorThis = v);
_getterDecs = captureInitializerThis(v => getterThis = v);
_setterDecs = captureInitializerThis(v => setterThis = v);
_methodDecs = captureInitializerThis(v => methodThis = v);
_propertyDecs = captureInitializerThis(v => propertyThis = v);
let _Foo;
new class extends babelHelpers.identity {
static {
Expand All @@ -28,7 +23,7 @@ new class extends babelHelpers.identity {
({
e: [_init_accessor, _init_extra_accessor, _init_property, _init_extra_property, _initStatic],
c: [_Foo, _initClass]
} = babelHelpers.applyDecs2311(this, [dec], [[_accessorDecs, 9, "accessor"], [_getterDecs, 11, "getter"], [_setterDecs, 12, "setter"], [_methodDecs, 10, "method"], [_propertyDecs, 8, "property"]]));
} = babelHelpers.applyDecs2311(this, [dec], [[captureInitializerThis(v => accessorThis = v), 9, "accessor"], [captureInitializerThis(v => getterThis = v), 11, "getter"], [captureInitializerThis(v => setterThis = v), 12, "setter"], [captureInitializerThis(v => methodThis = v), 10, "method"], [captureInitializerThis(v => propertyThis = v), 8, "property"]]));
_initStatic(this);
}
static get accessor() {
Expand Down
@@ -1,9 +1,8 @@
var _initClass, _classDecs;
_classDecs = [dec];
var _initClass;
let _A;
class A {
static {
[_A, _initClass] = babelHelpers.applyDecs2311(this, _classDecs, []).c;
[_A, _initClass] = babelHelpers.applyDecs2311(this, [dec], []).c;
}
static {
_initClass();
Expand Down
@@ -1,9 +1,8 @@
var _initClass, _classDecs;
_classDecs = [dec];
var _initClass;
let _default2;
class _default {
static {
[_default2, _initClass] = babelHelpers.applyDecs2311(babelHelpers.setFunctionName(this, "default"), _classDecs, []).c;
[_default2, _initClass] = babelHelpers.applyDecs2311(babelHelpers.setFunctionName(this, "default"), [dec], []).c;
}
static {
_initClass();
Expand Down
@@ -1,8 +1,7 @@
var _xDecs, _init_x, _init_extra_x;
_xDecs = dec;
var _init_x, _init_extra_x;
export class A {
static {
[_init_x, _init_extra_x] = babelHelpers.applyDecs2311(this, [], [[_xDecs, 0, "x"]]).e;
[_init_x, _init_extra_x] = babelHelpers.applyDecs2311(this, [], [[dec, 0, "x"]]).e;
}
constructor() {
_init_extra_x(this);
Expand Down
@@ -1,9 +1,8 @@
var _initClass, _classDecs;
_classDecs = [dec];
var _initClass;
let _A;
class A {
static {
[_A, _initClass] = babelHelpers.applyDecs2311(this, _classDecs, []).c;
[_A, _initClass] = babelHelpers.applyDecs2311(this, [dec], []).c;
}
static {
_initClass();
Expand Down
@@ -1,7 +1,4 @@
var _initProto, _aDecs, _call_a, _gDecs, _call_g, _agDecs, _call_ag, _Foo;
_aDecs = dec;
_gDecs = dec;
_agDecs = dec;
var _initProto, _call_a, _call_g, _call_ag, _Foo;
var _ag = /*#__PURE__*/new WeakMap();
var _g = /*#__PURE__*/new WeakMap();
var _a = /*#__PURE__*/new WeakMap();
Expand All @@ -14,4 +11,4 @@ class Foo {
}
}
_Foo = Foo;
[_call_a, _call_g, _call_ag, _initProto] = babelHelpers.applyDecs2311(_Foo, [], [[_aDecs, 2, "a", async function () {}], [_gDecs, 2, "g", function* () {}], [_agDecs, 2, "ag", async function* () {}]], 0, _ => _a.has(babelHelpers.checkInRHS(_))).e;
[_call_a, _call_g, _call_ag, _initProto] = babelHelpers.applyDecs2311(_Foo, [], [[dec, 2, "a", async function () {}], [dec, 2, "g", function* () {}], [dec, 2, "ag", async function* () {}]], 0, _ => _a.has(babelHelpers.checkInRHS(_))).e;
@@ -1,10 +1,7 @@
var _initProto, _aDecs, _call_a, _gDecs, _call_g, _agDecs, _call_ag;
_aDecs = dec;
_gDecs = dec;
_agDecs = dec;
var _initProto, _call_a, _call_g, _call_ag;
class Foo {
static {
[_call_a, _call_g, _call_ag, _initProto] = babelHelpers.applyDecs2311(this, [], [[_aDecs, 2, "a", async function () {}], [_gDecs, 2, "g", function* () {}], [_agDecs, 2, "ag", async function* () {}]], 0, _ => #a in _).e;
[_call_a, _call_g, _call_ag, _initProto] = babelHelpers.applyDecs2311(this, [], [[dec, 2, "a", async function () {}], [dec, 2, "g", function* () {}], [dec, 2, "ag", async function* () {}]], 0, _ => #a in _).e;
}
constructor() {
_initProto(this);
Expand Down
@@ -1,7 +1,4 @@
var _initClass, _obj, _classDecs, _xDecs, _init_x, _init_extra_x, _yDecs, _init_y, _init_extra_y, _A2;
_classDecs = [_obj = o1, _obj.dec, void 0, dec, _obj = o2, _obj.dec];
_xDecs = [_obj = o2, _obj.dec, _obj = o3.o, _obj.dec];
_yDecs = [_obj = o2, _obj.dec, void 0, dec];
var _initClass, _obj, _init_x, _init_extra_x, _init_y, _init_extra_y, _A2;
let _A;
class A {
constructor() {
Expand All @@ -14,5 +11,5 @@ _A2 = A;
({
e: [_init_x, _init_extra_x, _init_y, _init_extra_y],
c: [_A, _initClass]
} = babelHelpers.applyDecs2311(_A2, _classDecs, [[_xDecs, 16, "x"], [_yDecs, 16, "y"]], 1));
} = babelHelpers.applyDecs2311(_A2, [_obj = o1, _obj.dec, void 0, dec, _obj = o2, _obj.dec], [[[_obj = o2, _obj.dec, _obj = o3.o, _obj.dec], 16, "x"], [[_obj = o2, _obj.dec, void 0, dec], 16, "y"]], 1));
_initClass();
@@ -1,7 +1,5 @@
var _initProto, _initClass, _obj, _classDecs, _methodDecs, _Foo2;
var _initProto, _initClass, _obj, _Foo2;
const dec = () => {};
_classDecs = [void 0, dec, void 0, call(), void 0, chain.expr(), void 0, arbitrary + expr, _obj = array, _obj[expr]];
_methodDecs = [void 0, dec, void 0, call(), void 0, chain.expr(), void 0, arbitrary + expr, _obj = array, _obj[expr]];
let _Foo;
var _a = /*#__PURE__*/new WeakMap();
class Foo {
Expand All @@ -23,5 +21,5 @@ _Foo2 = Foo;
({
e: [_initProto],
c: [_Foo, _initClass]
} = babelHelpers.applyDecs2311(_Foo2, _classDecs, [[_methodDecs, 18, "method"]], 1));
} = babelHelpers.applyDecs2311(_Foo2, [void 0, dec, void 0, call(), void 0, chain.expr(), void 0, arbitrary + expr, _obj = array, _obj[expr]], [[[void 0, dec, void 0, call(), void 0, chain.expr(), void 0, arbitrary + expr, _obj = array, _obj[expr]], 18, "method"]], 1));
_initClass();
Expand Up @@ -6,7 +6,7 @@ function dec(fn) {
}

@dec(() => {
expect(() => Foo).toThrow(ReferenceError);
expect(() => Foo.x).toThrow();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes our behavior from a TDZ error to Foo being undefined, because it now runs inside the static block. In practice we are never very good with preserving TDZ errors, so it should be fine.

didRun = true;
}) class Foo {}

Expand Down
@@ -1,9 +1,8 @@
var _initProto, _methodDecs, _B, _initProto2, _methodDecs2, _B2;
var _initProto, _B, _initProto2, _B2;
const dec = () => {};
_methodDecs = deco;
class A extends (_B = B) {
static {
[_initProto] = babelHelpers.applyDecs2311(this, [], [[_methodDecs, 2, "method"]], 0, void 0, _B).e;
[_initProto] = babelHelpers.applyDecs2311(this, [], [[deco, 2, "method"]], 0, void 0, _B).e;
}
constructor() {
if (Math.random() > 0.5) {
Expand All @@ -14,10 +13,9 @@ class A extends (_B = B) {
}
method() {}
}
_methodDecs2 = deco;
class C extends (_B2 = B) {
static {
[_initProto2] = babelHelpers.applyDecs2311(this, [], [[_methodDecs2, 2, "method"]], 0, void 0, _B2).e;
[_initProto2] = babelHelpers.applyDecs2311(this, [], [[deco, 2, "method"]], 0, void 0, _B2).e;
}
constructor() {
try {
Expand Down
@@ -1,14 +1,11 @@
var _initClass, _obj, _classDecs, _xDecs, _init_x, _init_extra_x, _yDecs, _init_y, _init_extra_y;
_classDecs = [_obj = o1, _obj.dec, void 0, dec, _obj = o2, _obj.dec, _obj = o4.o(), _obj.dec];
_xDecs = [_obj = o2, _obj.dec, _obj = o3.o, _obj.dec, _obj = o4.o(), _obj.dec];
_yDecs = [_obj = o2, _obj.dec, void 0, dec];
var _initClass, _obj, _init_x, _init_extra_x, _init_y, _init_extra_y;
let _A;
class A {
static {
({
e: [_init_x, _init_extra_x, _init_y, _init_extra_y],
c: [_A, _initClass]
} = babelHelpers.applyDecs2311(this, _classDecs, [[_xDecs, 16, "x"], [_yDecs, 16, "y"]], 1));
} = babelHelpers.applyDecs2311(this, [_obj = o1, _obj.dec, void 0, dec, _obj = o2, _obj.dec, _obj = o4.o(), _obj.dec], [[[_obj = o2, _obj.dec, _obj = o3.o, _obj.dec, _obj = o4.o(), _obj.dec], 16, "x"], [[_obj = o2, _obj.dec, void 0, dec], 16, "y"]], 1));
}
constructor() {
_init_extra_y(this);
Expand Down