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 2021-12 decorators application order #14244

Merged
merged 15 commits into from Feb 8, 2022
Merged
2 changes: 1 addition & 1 deletion packages/babel-helpers/src/helpers-generated.ts

Large diffs are not rendered by default.

24 changes: 8 additions & 16 deletions packages/babel-helpers/src/helpers/applyDecs.js
Expand Up @@ -18,7 +18,7 @@

function createMetadataMethodsForProperty(metadataMap, kind, property) {
return {
getMetadata(key) {
getMetadata: function (key) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
Expand All @@ -41,7 +41,7 @@ function createMetadataMethodsForProperty(metadataMap, kind, property) {
return metadataForKey.constructor;
}
},
setMetadata(key, value) {
setMetadata: function (key, value) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
Expand Down Expand Up @@ -317,7 +317,7 @@ function applyMemberDec(
}
}
} else {
for (var i = 0; i < decs.length; i++) {
for (var i = decs.length - 1; i >= 0; i--) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Decorators (after evaluated) are applied from right to left.

var dec = decs[i];

newValue = dec(value, ctx);
Expand Down Expand Up @@ -421,8 +421,8 @@ function applyMemberDecs(
staticMetadataMap,
decInfos
) {
var protoInitializers;
var staticInitializers;
var protoInitializers = [];
var staticInitializers = [];

var existingProtoNonFields = new Map();
var existingStaticNonFields = new Map();
Expand All @@ -447,19 +447,11 @@ function applyMemberDecs(
metadataMap = staticMetadataMap;
kind = kind - 5 /* STATIC */;

if (!staticInitializers) {
staticInitializers = [];
}

initializers = staticInitializers;
} else {
base = Class.prototype;
metadataMap = protoMetadataMap;

if (!protoInitializers) {
protoInitializers = [];
}

initializers = protoInitializers;
}

Expand Down Expand Up @@ -499,11 +491,11 @@ function applyMemberDecs(
);
}

if (protoInitializers) {
if (protoInitializers.length > 0) {
pushInitializers(ret, protoInitializers);
}

if (staticInitializers) {
if (staticInitializers.length > 0) {
pushInitializers(ret, staticInitializers);
}
}
Expand Down Expand Up @@ -541,7 +533,7 @@ function applyClassDecs(ret, targetClass, metadataMap, classDecs) {
createMetadataMethodsForProperty(metadataMap, 0 /* CONSTRUCTOR */, name)
);

for (var i = 0; i < classDecs.length; i++) {
for (var i = classDecs.length - 1; i >= 0; i--) {
newClass = classDecs[i](newClass, ctx) || newClass;
}

Expand Down
104 changes: 50 additions & 54 deletions packages/babel-plugin-proposal-decorators/src/transformer-2021-12.ts
@@ -1,4 +1,4 @@
import type { NodePath } from "@babel/traverse";
import type { NodePath, Scope } from "@babel/traverse";
import { types as t, template } from "@babel/core";
import syntaxDecorators from "@babel/plugin-syntax-decorators";
import ReplaceSupers from "@babel/helper-replace-supers";
Expand Down Expand Up @@ -506,13 +506,20 @@ function transformClass(
let constructorPath: NodePath<t.ClassMethod> | undefined;
let requiresProtoInit = false;
let requiresStaticInit = false;
let hasComputedProps = false;
const decoratedPrivateMethods = new Set<string>();

let protoInitLocal: t.Identifier,
staticInitLocal: t.Identifier,
classInitLocal: t.Identifier,
classLocal: t.Identifier;
const assignments: t.AssignmentExpression[] = [];
const scopeParent: Scope = path.scope.parent;

const memoiseExpression = (expression: t.Expression, hint: string) => {
const localEvaluatedId = scopeParent.generateDeclaredUidIdentifier(hint);
assignments.push(t.assignmentExpression("=", localEvaluatedId, expression));
return t.cloneNode(localEvaluatedId);
};

if (classDecorators) {
classInitLocal =
Expand All @@ -523,6 +530,15 @@ function transformClass(
classLocal = localId;

path.node.decorators = null;

for (const classDecorator of classDecorators) {
if (!scopeParent.isStatic(classDecorator.expression)) {
classDecorator.expression = memoiseExpression(
classDecorator.expression,
"dec",
);
}
}
} else {
if (!path.node.id) {
path.node.id = path.scope.generateUidIdentifier("Class");
Expand All @@ -536,43 +552,50 @@ function transformClass(
continue;
}

let { key } = element.node;
const kind = getElementKind(element);
const { node } = element;
const decorators = element.get("decorators");

const isPrivate = key.type === "PrivateName";
const hasDecorators = Array.isArray(decorators) && decorators.length > 0;

if (hasDecorators) {
for (const decoratorPath of decorators) {
if (!scopeParent.isStatic(decoratorPath.node.expression)) {
decoratorPath.node.expression = memoiseExpression(
decoratorPath.node.expression,
"dec",
);
}
}
}

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");
}
}

const kind = getElementKind(element);
const { key } = node;

const isPrivate = key.type === "PrivateName";

const isStatic = !!element.node.static;

let name = "computedKey";

if (isPrivate) {
name = (key as t.PrivateName).id.name;
} else if (key.type === "Identifier") {
} else if (!isComputed && key.type === "Identifier") {
name = key.name;
}

if (element.isClassMethod({ kind: "constructor" })) {
constructorPath = element;
}

if (isComputed) {
const keyPath = element.get("key");
const localComputedNameId =
keyPath.scope.parent.generateDeclaredUidIdentifier(name);
keyPath.replaceWith(localComputedNameId);

elementDecoratorInfo.push({
localComputedNameId: t.cloneNode(localComputedNameId),
keyNode: t.cloneNode(key as t.Expression),
});

key = localComputedNameId;
hasComputedProps = true;
}

if (Array.isArray(decorators) && decorators.length > 0) {
if (hasDecorators) {
let locals: t.Identifier | t.Identifier[];
let privateMethods: t.FunctionExpression | t.FunctionExpression[];

Expand Down Expand Up @@ -730,34 +753,6 @@ function transformClass(
}
}

if (hasComputedProps) {
const assignments: t.AssignmentExpression[] = [];

for (const info of elementDecoratorInfo) {
if (isDecoratorInfo(info)) {
const { decorators } = info;
const newDecorators: t.Identifier[] = [];

for (const decorator of decorators) {
const localComputedNameId =
path.scope.parent.generateDeclaredUidIdentifier("dec");
assignments.push(
t.assignmentExpression("=", localComputedNameId, decorator),
);
newDecorators.push(t.cloneNode(localComputedNameId));
}

info.decorators = newDecorators;
} else {
assignments.push(
t.assignmentExpression("=", info.localComputedNameId, info.keyNode),
);
}
}

path.insertBefore(assignments);
}

const elementDecorations = generateDecorationExprs(elementDecoratorInfo);
const classDecorations = t.arrayExpression(
(classDecorators || []).map(d => d.expression),
Expand All @@ -766,10 +761,6 @@ function transformClass(
const locals: t.Identifier[] =
extractElementLocalAssignments(elementDecoratorInfo);

if (classDecorators) {
locals.push(classLocal, classInitLocal);
}

if (requiresProtoInit) {
protoInitLocal =
path.scope.parent.generateDeclaredUidIdentifier("initProto");
Expand Down Expand Up @@ -879,6 +870,7 @@ function transformClass(
const originalClass = path.node;

if (classDecorators) {
locals.push(classLocal, classInitLocal);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

classLocal and classInitLocal are generated after other functions, see applyClassDecs in applyDecs.

const statics = [];
let staticBlocks: t.StaticBlock[] = [];
path.get("body.body").forEach(element => {
Expand Down Expand Up @@ -982,6 +974,10 @@ function transformClass(
),
);

// When path is a ClassExpression, path.insertBefore will convert `path`
// into a SequenceExpression
path.insertBefore(assignments);

// Recrawl the scope to make sure new identifiers are properly synced
path.scope.crawl();

Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
@dec
accessor #a;
Expand Down
@@ -1,5 +1,7 @@
var _init_a, _get_a, _set_a, _init_b, _get_b, _set_b, _initProto;

const dec = () => {};

var _A = /*#__PURE__*/new WeakMap();

var _a = /*#__PURE__*/new WeakMap();
Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
@dec
accessor a;
Expand Down
@@ -1,9 +1,8 @@
var _init_a, _init_b, _computedKey, _init_computedKey, _dec, _dec2, _dec3, _initProto;
var _init_a, _init_b, _computedKey, _init_computedKey, _initProto;

const dec = () => {};

_dec = dec
_dec2 = dec
_computedKey = 'c'
_dec3 = dec

var _A = /*#__PURE__*/new WeakMap();

Expand Down Expand Up @@ -54,5 +53,5 @@ class Foo {
}

(() => {
[_init_a, _init_b, _init_computedKey, _initProto] = babelHelpers.applyDecs(Foo, [[_dec, 1, "a"], [_dec2, 1, "b"], [_dec3, 1, _computedKey]], []);
[_init_a, _init_b, _init_computedKey, _initProto] = babelHelpers.applyDecs(Foo, [[dec, 1, "a"], [dec, 1, "b"], [dec, 1, _computedKey]], []);
})();
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
@dec
static accessor #a;
Expand Down
@@ -1,5 +1,7 @@
var _init_a, _get_a, _set_a, _init_b, _get_b, _set_b, _initStatic;

const dec = () => {};

var _a = /*#__PURE__*/new WeakMap();

var _b = /*#__PURE__*/new WeakMap();
Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
@dec
static accessor a;
Expand Down
@@ -1,9 +1,8 @@
var _init_a, _init_b, _computedKey, _init_computedKey, _dec, _dec2, _dec3, _initStatic;
var _init_a, _init_b, _computedKey, _init_computedKey, _initStatic;

const dec = () => {};

_dec = dec
_dec2 = dec
_computedKey = 'c'
_dec3 = dec

class Foo {
static get a() {
Expand Down Expand Up @@ -33,7 +32,7 @@ class Foo {
}

(() => {
[_init_a, _init_b, _init_computedKey, _initStatic] = babelHelpers.applyDecs(Foo, [[_dec, 6, "a"], [_dec2, 6, "b"], [_dec3, 6, _computedKey]], []);
[_init_a, _init_b, _init_computedKey, _initStatic] = babelHelpers.applyDecs(Foo, [[dec, 6, "a"], [dec, 6, "b"], [dec, 6, _computedKey]], []);

_initStatic(Foo);
})();
Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
accessor #a;

Expand Down
@@ -1,3 +1,5 @@
const dec = () => {};

var _A = /*#__PURE__*/new WeakMap();

var _a = /*#__PURE__*/new WeakMap();
Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
accessor a;

Expand Down
@@ -1,3 +1,5 @@
const dec = () => {};

var _A = /*#__PURE__*/new WeakMap();

var _B = /*#__PURE__*/new WeakMap();
Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
static accessor #a;

Expand Down
@@ -1,3 +1,5 @@
const dec = () => {};

class Foo {}

function _get_a() {
Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
static accessor a;

Expand Down
@@ -1,3 +1,5 @@
const dec = () => {};

class Foo {
static get a() {
return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _A);
Expand Down
@@ -1,3 +1,4 @@
const dec = () => {};
class Foo {
@dec
accessor #a;
Expand Down
@@ -1,5 +1,7 @@
var _init_a, _get_a, _set_a, _init_b, _get_b, _set_b, _initProto;

const dec = () => {};

class Foo {
static {
[_init_a, _get_a, _set_a, _init_b, _get_b, _set_b, _initProto] = babelHelpers.applyDecs(this, [[dec, 1, "a", function () {
Expand Down