Skip to content

Commit

Permalink
fix: register decorator memoisers as let binding
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Mar 6, 2024
1 parent 206ebf3 commit 8c456e3
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 35 deletions.
Expand Up @@ -129,26 +129,28 @@ function replaceClassWithVar(
id: t.Identifier;
path: NodePath<t.ClassDeclaration | t.ClassExpression>;
} {
const id = path.node.id;
const scope = path.scope;
if (path.type === "ClassDeclaration") {
const id = path.node.id;
const className = id.name;
const varId = path.scope.generateUidIdentifierBasedOnNode(id);
const varId = scope.generateUidIdentifierBasedOnNode(id);
const classId = t.identifier(className);

path.scope.rename(className, varId.name);
scope.rename(className, varId.name);

path.get("id").replaceWith(classId);

return { id: t.cloneNode(varId), path };
} else {
let varId: t.Identifier;

if (path.node.id) {
className = path.node.id.name;
varId = path.scope.parent.generateDeclaredUidIdentifier(className);
path.scope.rename(className, varId.name);
if (id) {
className = id.name;
varId = generateLetUidIdentifier(scope.parent, className);
scope.rename(className, varId.name);
} else {
varId = path.scope.parent.generateDeclaredUidIdentifier(
varId = generateLetUidIdentifier(
scope.parent,
typeof className === "string" ? className : "decorated_class",
);
}
Expand Down Expand Up @@ -1005,7 +1007,7 @@ function transformClass(
hint: string,
assignments: t.AssignmentExpression[],
) => {
const localEvaluatedId = scopeParent.generateDeclaredUidIdentifier(hint);
const localEvaluatedId = generateLetUidIdentifier(scopeParent, hint);
assignments.push(t.assignmentExpression("=", localEvaluatedId, expression));
return t.cloneNode(localEvaluatedId);
};
Expand Down Expand Up @@ -1048,11 +1050,15 @@ function transformClass(
/* fallthrough */
default:
if (element.node.static) {
staticInitLocal ??=
scopeParent.generateDeclaredUidIdentifier("initStatic");
staticInitLocal ??= generateLetUidIdentifier(
scopeParent,
"initStatic",
);
} else {
protoInitLocal ??=
scopeParent.generateDeclaredUidIdentifier("initProto");
protoInitLocal ??= generateLetUidIdentifier(
scopeParent,
"initProto",
);
}
break;
}
Expand Down Expand Up @@ -1142,8 +1148,7 @@ function transformClass(
} else if (scopeParent.isStatic(expression.object)) {
object = t.cloneNode(expression.object);
} else {
decoratorReceiverId ??=
scopeParent.generateDeclaredUidIdentifier("obj");
decoratorReceiverId ??= generateLetUidIdentifier(scopeParent, "obj");
object = t.assignmentExpression(
"=",
t.cloneNode(decoratorReceiverId),
Expand All @@ -1170,7 +1175,7 @@ function transformClass(
let classDecorations: t.Expression[] = [];
let classDecorationsId: t.Identifier;
if (classDecorators) {
classInitLocal = scopeParent.generateDeclaredUidIdentifier("initClass");
classInitLocal = generateLetUidIdentifier(scopeParent, "initClass");
needsDeclaraionForClassBinding = path.isClassDeclaration();
({ id: classIdLocal, path } = replaceClassWithVar(path, className));

Expand Down Expand Up @@ -1346,8 +1351,10 @@ function transformClass(
}

const newId = generateClassPrivateUid();
const newFieldInitId =
element.scope.parent.generateDeclaredUidIdentifier(`init_${name}`);
const newFieldInitId = generateLetUidIdentifier(
scopeParent,
`init_${name}`,
);
const newValue = t.callExpression(
t.cloneNode(newFieldInitId),
params,
Expand All @@ -1359,12 +1366,8 @@ function transformClass(
if (isPrivate) {
privateMethods = extractProxyAccessorsFor(newId, version);

const getId = newPath.scope.parent.generateDeclaredUidIdentifier(
`get_${name}`,
);
const setId = newPath.scope.parent.generateDeclaredUidIdentifier(
`set_${name}`,
);
const getId = generateLetUidIdentifier(scopeParent, `get_${name}`);
const setId = generateLetUidIdentifier(scopeParent, `set_${name}`);

addCallAccessorsFor(version, newPath, key, getId, setId, isStatic);

Expand All @@ -1385,9 +1388,7 @@ function transformClass(
locals = [newFieldInitId];
}
} else if (kind === FIELD) {
const initId = element.scope.parent.generateDeclaredUidIdentifier(
`init_${name}`,
);
const initId = generateLetUidIdentifier(scopeParent, `init_${name}`);
const valuePath = (
element as NodePath<t.ClassProperty | t.ClassPrivateProperty>
).get("value");
Expand All @@ -1406,7 +1407,8 @@ function transformClass(
privateMethods = extractProxyAccessorsFor(key, version);
}
} else if (isPrivate) {
const callId = element.scope.parent.generateDeclaredUidIdentifier(
const callId = generateLetUidIdentifier(
element.scope.parent,
`call_${name}`,
);
locals = [callId];
Expand Down Expand Up @@ -1508,7 +1510,8 @@ function transformClass(

if (hasDecorators && version === "2023-11") {
if (kind === FIELD || kind === ACCESSOR) {
const initExtraId = scopeParent.generateDeclaredUidIdentifier(
const initExtraId = generateLetUidIdentifier(
scopeParent,
`init_extra_${name}`,
);
locals.push(initExtraId);
Expand Down Expand Up @@ -2140,6 +2143,12 @@ function isDecoratedAnonymousClassExpression(path: NodePath) {
);
}

function generateLetUidIdentifier(scope: Scope, name: string) {
const id = scope.generateUidIdentifier(name);
scope.push({ id, kind: "let" });
return t.cloneNode(id);
}

export default function (
{ assertVersion, assumption }: PluginAPI,
{ loose }: Options,
Expand Down
@@ -1,7 +1,7 @@
{
const logs = [];
const classes = [];
function noop() {}
const noop = () => {}

for (let i = 0; i < 2; i++) {
classes.push(class C {
Expand All @@ -19,7 +19,7 @@
{
const logs = [];
const classes = [];
function noop() {}
const noop = () => {}

for (let i = 0; i < 2; i++) {
classes.push(@noop class C {
Expand All @@ -37,7 +37,7 @@
{
const logs = [];
const classes = [];
function noop() {}
const noop = () => {}

for (let i = 0; i < 2; i++) {
classes.push(@noop class C {
Expand All @@ -51,3 +51,44 @@

expect(logs.join()).toBe("0,1");
}

{
const logs = [];
const classes = [];
const setValueTo = i => () => () => i

for (let i = 0; i < 2; i++) {
classes.push(class C {
@setValueTo(i) [i];
})
}

for (const clazz of classes) {
const c = new clazz();
const key = Reflect.ownKeys(c)[0];
logs.push([key, c[key]].join(":"));
}

expect(logs.join()).toBe("0:0,1:1");
}

{
const logs = [];
const classes = [];
const noop = () => {}
const setValueTo = i => (_, { access, addInitializer }) => addInitializer(function() { access.set(this, i) })

for (let i = 0; i < 2; i++) {
classes.push(@noop class C {
@setValueTo(i) [i];
})
}

for (const clazz of classes) {
const c = new clazz();
const key = Reflect.ownKeys(c)[0];
logs.push([key, c[key]].join(":"));
}

expect(logs.join()).toBe("0:0,1:1");
}
@@ -1,7 +1,7 @@
{
const logs = [];
const classes = [];
function noop() {}
const noop = () => {}

for (let i = 0; i < 2; i++) {
classes.push(class C {
Expand All @@ -19,7 +19,7 @@
{
const logs = [];
const classes = [];
function noop() {}
const noop = () => {}

for (let i = 0; i < 2; i++) {
classes.push(@noop class C {
Expand All @@ -37,7 +37,7 @@
{
const logs = [];
const classes = [];
function noop() {}
const noop = () => {}

for (let i = 0; i < 2; i++) {
classes.push(@noop class C {
Expand All @@ -51,3 +51,44 @@

expect(logs.join()).toBe("0,1");
}

{
const logs = [];
const classes = [];
const setValueTo = i => () => () => i

for (let i = 0; i < 2; i++) {
classes.push(class C {
@setValueTo(i) [i];
})
}

for (const clazz of classes) {
const c = new clazz();
const key = Reflect.ownKeys(c)[0];
logs.push([key, c[key]].join(":"));
}

expect(logs.join()).toBe("0:0,1:1");
}

{
const logs = [];
const classes = [];
const noop = () => {}
const setValueTo = i => (_, { access, addInitializer }) => addInitializer(function() { access.set(this, i) })

for (let i = 0; i < 2; i++) {
classes.push(@noop class C {
@setValueTo(i) [i];
})
}

for (const clazz of classes) {
const c = new clazz();
const key = Reflect.ownKeys(c)[0];
logs.push([key, c[key]].join(":"));
}

expect(logs.join()).toBe("0:0,1:1");
}

0 comments on commit 8c456e3

Please sign in to comment.