Skip to content

Commit 576c7e5

Browse files
authored
Ensuring super call is always first statement in constructors (#524)
* made sure super calls in constructors are always first statement * adhering to strictness * a slight refactor
1 parent 27ae2a8 commit 576c7e5

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

src/LuaTransformer.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,10 @@ export class LuaTransformer {
10071007
return undefined;
10081008
}
10091009

1010-
const bodyStatements: tstl.Statement[] = this.transformClassInstanceFields(classDeclaration, instanceFields);
1010+
const bodyWithFieldInitializers: tstl.Statement[] = this.transformClassInstanceFields(
1011+
classDeclaration,
1012+
instanceFields
1013+
);
10111014

10121015
// Check for field declarations in constructor
10131016
const constructorFieldsDeclarations = statement.parameters.filter(p => p.modifiers !== undefined);
@@ -1027,7 +1030,7 @@ export class LuaTransformer {
10271030
tstl.SyntaxKind.OrOperator
10281031
)
10291032
);
1030-
bodyStatements.push(assignement);
1033+
bodyWithFieldInitializers.push(assignement);
10311034
} else {
10321035
// self.declarationName = declarationName
10331036
const assignement = tstl.createAssignmentStatement(
@@ -1037,7 +1040,7 @@ export class LuaTransformer {
10371040
),
10381041
declarationName
10391042
);
1040-
bodyStatements.push(assignement);
1043+
bodyWithFieldInitializers.push(assignement);
10411044
}
10421045
}
10431046

@@ -1049,9 +1052,24 @@ export class LuaTransformer {
10491052
);
10501053

10511054
const [body] = this.transformFunctionBody(statement.parameters, statement.body, restParamName);
1052-
bodyStatements.push(...body);
10531055

1054-
const block: tstl.Block = tstl.createBlock(bodyStatements);
1056+
// If there are field initializers and the first statement is a super call, hoist the super call to the top
1057+
if (bodyWithFieldInitializers.length > 0 && statement.body && statement.body.statements.length > 0) {
1058+
const firstStatement = statement.body.statements[0];
1059+
if (ts.isExpressionStatement(firstStatement)
1060+
&& ts.isCallExpression(firstStatement.expression)
1061+
&& firstStatement.expression.expression.kind === ts.SyntaxKind.SuperKeyword)
1062+
{
1063+
const superCall = body.shift();
1064+
if (superCall) {
1065+
bodyWithFieldInitializers.unshift(superCall);
1066+
}
1067+
}
1068+
}
1069+
1070+
bodyWithFieldInitializers.push(...body);
1071+
1072+
const block: tstl.Block = tstl.createBlock(bodyWithFieldInitializers);
10551073

10561074
const result = tstl.createAssignmentStatement(
10571075
this.createConstructorName(className),

test/unit/class.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,3 +797,29 @@ test("Class annonymous expression name via constructor", () => {
797797
`;
798798
expect(util.transpileAndExecute(code)).toBe("____");
799799
});
800+
801+
test("Class field override in subclass", () => {
802+
const code = `
803+
class Foo {
804+
field = "foo";
805+
}
806+
class Bar extends Foo {
807+
field = "bar";
808+
}
809+
return (new Foo()).field + (new Bar()).field;`;
810+
expect(util.transpileAndExecute(code)).toBe("foobar");
811+
});
812+
813+
test("Class field override in subclass with constructors", () => {
814+
const code = `
815+
class Foo {
816+
field = "foo";
817+
constructor() {}
818+
}
819+
class Bar extends Foo {
820+
field = "bar";
821+
constructor() { super(); }
822+
}
823+
return (new Foo()).field + (new Bar()).field;`;
824+
expect(util.transpileAndExecute(code)).toBe("foobar");
825+
});

0 commit comments

Comments
 (0)