Skip to content

Commit 37d2bd7

Browse files
tomblindlolleko
authored andcommitted
fixed additional issues with exported classes and a few transform tes… (#355)
* fixed additional issues with exported classes and a few transform tests where functions are now declared local * local/global/export logic fix - function and var are global unless inside a module, namespace or function - everything else is local - functions and function expressions properly separate declaration from assignment for recursion now - transform tests updated - also fixed noHeader not being respected when building lualib
1 parent d01cdbf commit 37d2bd7

File tree

6 files changed

+90
-72
lines changed

6 files changed

+90
-72
lines changed

src/CompilerOptions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as ts from "typescript";
22

33
export interface CompilerOptions extends ts.CompilerOptions {
4-
addHeader?: boolean;
4+
noHeader?: boolean;
55
luaTarget?: string;
66
luaLibImport?: string;
77
}
@@ -18,4 +18,4 @@ export enum LuaTarget {
1818
Lua52 = "5.2",
1919
Lua53 = "5.3",
2020
LuaJIT = "jit",
21-
}
21+
}

src/LuaPrinter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class LuaPrinter {
4848
public print(block: tstl.Block, luaLibFeatures?: Set<LuaLibFeature>): string {
4949
let header = "";
5050

51-
if (this.options.addHeader === undefined || this.options.addHeader === true) {
51+
if (this.options.noHeader === undefined || this.options.noHeader === false) {
5252
header += `--[[ Generated with https://github.com/Perryvw/TypescriptToLua ]]\n`;
5353
}
5454

src/LuaTransformer.ts

Lines changed: 79 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,10 @@ export class LuaTransformer {
343343
const fieldName = this.transformPropertyName(field.name);
344344
const value = this.transformExpression(field.initializer);
345345

346-
const classField =
347-
tstl.createTableIndexExpression(this.addExportToIdentifier(className, statement.name.text), fieldName);
346+
const classField = tstl.createTableIndexExpression(
347+
this.addExportToIdentifier(className),
348+
fieldName
349+
);
348350

349351
const fieldAssign = tstl.createAssignmentStatement(
350352
classField,
@@ -400,6 +402,8 @@ export class LuaTransformer {
400402

401403
const result: tstl.Statement[] = [];
402404

405+
const classNameWithExport = this.addExportToIdentifier(className);
406+
403407
// Write class declaration
404408
if (extendsType) {
405409
const baseName = tstl.createIdentifier(extendsType.symbol.escapedName as string);
@@ -412,7 +416,7 @@ export class LuaTransformer {
412416

413417
if (!noClassOr) {
414418
// className or baseName.new()
415-
rhs = tstl.createBinaryExpression(className, rhs, tstl.SyntaxKind.OrOperator);
419+
rhs = tstl.createBinaryExpression(classNameWithExport, rhs, tstl.SyntaxKind.OrOperator);
416420
}
417421

418422
// (local) className = className or baseName.new()
@@ -427,10 +431,7 @@ export class LuaTransformer {
427431

428432
if (!noClassOr) {
429433
// className or {}
430-
rhs = tstl.createBinaryExpression(
431-
this.addExportToIdentifier(className, statement.name.text),
432-
rhs,
433-
tstl.SyntaxKind.OrOperator);
434+
rhs = tstl.createBinaryExpression(classNameWithExport, rhs, tstl.SyntaxKind.OrOperator);
434435
}
435436

436437
// (local) className = className or {}
@@ -442,20 +443,16 @@ export class LuaTransformer {
442443
}
443444

444445
// className.__index
445-
const classIndex = tstl.createTableIndexExpression(
446-
this.addExportToIdentifier(className, statement.name.text),
447-
tstl.createStringLiteral("__index"));
446+
const classIndex = tstl.createTableIndexExpression(classNameWithExport, tstl.createStringLiteral("__index"));
448447
// className.__index = className
449-
const assignClassIndex = tstl.createAssignmentStatement(classIndex, className, undefined, statement);
448+
const assignClassIndex = tstl.createAssignmentStatement(classIndex, classNameWithExport, undefined, statement);
450449

451450
result.push(assignClassIndex);
452451

453452
if (extendsType) {
454453
const baseName = tstl.createIdentifier(extendsType.symbol.escapedName as string);
455454
// className.__base = baseName
456-
const classBase = tstl.createTableIndexExpression(
457-
this.addExportToIdentifier(className, statement.name.text),
458-
tstl.createStringLiteral("__base"));
455+
const classBase = tstl.createTableIndexExpression(classNameWithExport, tstl.createStringLiteral("__base"));
459456

460457
const assignClassBase = tstl.createAssignmentStatement(classBase, baseName, undefined, statement);
461458

@@ -469,7 +466,7 @@ export class LuaTransformer {
469466
this.selfIdentifier,
470467
tstl.createCallExpression(
471468
tstl.createIdentifier("setmetatable"),
472-
[tstl.createTableExpression(), className]
469+
[tstl.createTableExpression(), classNameWithExport]
473470
)
474471
);
475472

@@ -498,11 +495,11 @@ export class LuaTransformer {
498495
const ifConstructor = tstl.createIfStatement(
499496
tstl.createBinaryExpression(
500497
tstl.createIdentifier("construct"),
501-
tstl.createTableIndexExpression(className, tstl.createStringLiteral("constructor")),
498+
tstl.createTableIndexExpression(classNameWithExport, tstl.createStringLiteral("constructor")),
502499
tstl.SyntaxKind.AndOperator),
503500
tstl.createBlock([
504501
tstl.createExpressionStatement(tstl.createCallExpression(
505-
tstl.createTableIndexExpression(className, tstl.createStringLiteral("constructor")),
502+
tstl.createTableIndexExpression(classNameWithExport, tstl.createStringLiteral("constructor")),
506503
[this.selfIdentifier, tstl.createDotsLiteral()])),
507504
]));
508505

@@ -517,7 +514,7 @@ export class LuaTransformer {
517514
// or function export.className.new(construct, ...) ... end
518515
const newFunc = tstl.createAssignmentStatement(
519516
tstl.createTableIndexExpression(
520-
this.addExportToIdentifier(className, statement.name.text),
517+
classNameWithExport,
521518
tstl.createStringLiteral("new")),
522519
tstl.createFunctionExpression(
523520
tstl.createBlock(newFuncStatements),
@@ -593,7 +590,7 @@ export class LuaTransformer {
593590

594591
const result = tstl.createAssignmentStatement(
595592
tstl.createTableIndexExpression(
596-
this.addExportToIdentifier(className, classDeclaration.name.text),
593+
this.addExportToIdentifier(className),
597594
tstl.createStringLiteral("constructor")),
598595
tstl.createFunctionExpression(body, params, dotsLiteral, restParamName, undefined, undefined),
599596
undefined,
@@ -619,7 +616,7 @@ export class LuaTransformer {
619616

620617
return tstl.createAssignmentStatement(
621618
tstl.createTableIndexExpression(
622-
this.addExportToIdentifier(className, classDeclaration.name.text),
619+
this.addExportToIdentifier(className),
623620
tstl.createStringLiteral("get__" + name.text)),
624621
accessorFunction
625622
);
@@ -644,7 +641,7 @@ export class LuaTransformer {
644641

645642
return tstl.createAssignmentStatement(
646643
tstl.createTableIndexExpression(
647-
this.addExportToIdentifier(className, classDeclaration.name.text),
644+
this.addExportToIdentifier(className),
648645
tstl.createStringLiteral("set__" + name.text)),
649646
accessorFunction
650647
);
@@ -678,9 +675,10 @@ export class LuaTransformer {
678675
restParamName
679676
);
680677

678+
const parent = node.parent as ts.ClassLikeDeclaration;
681679
return tstl.createAssignmentStatement(
682680
tstl.createTableIndexExpression(
683-
this.addExportToIdentifier(className, (node.parent as ts.ClassLikeDeclaration).name.text),
681+
this.addExportToIdentifier(className),
684682
methodName),
685683
functionExpression,
686684
undefined,
@@ -944,7 +942,7 @@ export class LuaTransformer {
944942
);
945943
const functionExpression = tstl.createFunctionExpression(body, params, dotsLiteral, restParamName);
946944

947-
return this.createLocalOrExportedDeclaration(name, functionExpression, undefined, functionDeclaration);
945+
return this.createLocalOrExportedOrGlobalDeclaration(name, functionExpression, undefined, functionDeclaration);
948946
}
949947

950948
public transformTypeAliasDeclaration(statement: ts.TypeAliasDeclaration): undefined {
@@ -970,9 +968,14 @@ export class LuaTransformer {
970968
const identifierName = this.transformIdentifier(statement.name);
971969
if (statement.initializer) {
972970
const value = this.transformExpression(statement.initializer);
973-
return this.createLocalOrExportedDeclaration(identifierName, value);
971+
return this.createLocalOrExportedOrGlobalDeclaration(identifierName, value, undefined, statement);
974972
} else {
975-
return this.createLocalOrExportedDeclaration(identifierName, tstl.createNilLiteral());
973+
return this.createLocalOrExportedOrGlobalDeclaration(
974+
identifierName,
975+
tstl.createNilLiteral(),
976+
undefined,
977+
statement
978+
);
976979
}
977980
} else if (ts.isArrayBindingPattern(statement.name)) {
978981
// Destructuring type
@@ -987,17 +990,27 @@ export class LuaTransformer {
987990
// Don't unpack TupleReturn decorated functions
988991
if (statement.initializer) {
989992
if (tsHelper.isTupleReturnCall(statement.initializer, this.checker)) {
990-
return this.createLocalOrExportedDeclaration(vars, this.transformExpression(statement.initializer));
993+
return this.createLocalOrExportedOrGlobalDeclaration(
994+
vars,
995+
this.transformExpression(statement.initializer),
996+
undefined,
997+
statement
998+
);
991999
} else {
9921000
// local vars = this.transpileDestructingAssignmentValue(node.initializer);
9931001
const initializer = this.createUnpackCall(
9941002
this.transformExpression(statement.initializer),
9951003
statement.initializer
9961004
);
997-
return this.createLocalOrExportedDeclaration(vars, initializer);
1005+
return this.createLocalOrExportedOrGlobalDeclaration(vars, initializer, undefined, statement);
9981006
}
9991007
} else {
1000-
return this.createLocalOrExportedDeclaration(vars, tstl.createNilLiteral());
1008+
return this.createLocalOrExportedOrGlobalDeclaration(
1009+
vars,
1010+
tstl.createNilLiteral(),
1011+
undefined,
1012+
statement
1013+
);
10011014
}
10021015
} else {
10031016
throw TSTLErrors.UnsupportedKind("variable declaration", statement.name.kind, statement);
@@ -2955,10 +2968,8 @@ export class LuaTransformer {
29552968
return scopeSymbol.exports.has(identifierName as ts.__String);
29562969
}
29572970

2958-
public addExportToIdentifier(identifier: tstl.Identifier, originalStr?: string)
2959-
: tstl.IdentifierOrTableIndexExpression {
2960-
const testStr = originalStr ? originalStr : identifier.text;
2961-
if (this.isIdentifierExported(testStr)) {
2971+
public addExportToIdentifier(identifier: tstl.Identifier): tstl.IdentifierOrTableIndexExpression {
2972+
if (this.isIdentifierExported(identifier.text)) {
29622973
return this.createExportedIdentifier(identifier);
29632974
}
29642975
return identifier;
@@ -3083,49 +3094,56 @@ export class LuaTransformer {
30833094
return filePath.replace(new RegExp("\\\\|\/", "g"), ".");
30843095
}
30853096

3086-
private createLocalOrExportedOrGlobalDeclaration(
3087-
lhs: tstl.Identifier | tstl.Identifier[],
3088-
rhs: tstl.Expression,
3089-
parent?: tstl.Node,
3090-
tsOriginal?: ts.Node
3091-
): tstl.Statement[]
3092-
{
3093-
const statements: tstl.Statement[] = [];
3094-
if (this.isModule || this.currentNamespace) {
3095-
statements.push(...this.createLocalOrExportedDeclaration(lhs, rhs, parent ,tsOriginal));
3097+
private shouldExportIdentifier(identifier: tstl.Identifier | tstl.Identifier[]): boolean {
3098+
if (!this.isModule && !this.currentNamespace) {
3099+
return false;
3100+
}
3101+
if (Array.isArray(identifier)) {
3102+
return identifier.some(i => this.isIdentifierExported(i.text));
30963103
} else {
3097-
statements.push(tstl.createAssignmentStatement(lhs, rhs, parent, tsOriginal));
3104+
return this.isIdentifierExported(identifier.text);
30983105
}
3099-
return statements;
31003106
}
31013107

3102-
private createLocalOrExportedDeclaration(
3108+
private createLocalOrExportedOrGlobalDeclaration(
31033109
lhs: tstl.Identifier | tstl.Identifier[],
31043110
rhs: tstl.Expression,
31053111
parent?: tstl.Node,
31063112
tsOriginal?: ts.Node
31073113
): tstl.Statement[]
31083114
{
3109-
const statements: tstl.Statement[] = [];
3110-
if (!Array.isArray(lhs)) {
3111-
lhs = [lhs];
3115+
if (this.shouldExportIdentifier(lhs)) {
3116+
// exported
3117+
if (Array.isArray(lhs)) {
3118+
return [tstl.createAssignmentStatement(lhs.map(i => this.createExportedIdentifier(i)), rhs, parent)];
3119+
} else {
3120+
return [tstl.createAssignmentStatement(this.createExportedIdentifier(lhs), rhs, parent)];
3121+
}
31123122
}
3113-
const shouldExport = lhs.some(i => this.isIdentifierExported(i.text));
3114-
if (shouldExport) {
3115-
statements.push(
3116-
tstl.createAssignmentStatement(lhs.map(i => this.createExportedIdentifier(i)), rhs, parent));
3117-
} else {
3118-
// TODO this check probably should be moved out of this function or be improved?
3119-
if (tsOriginal &&
3120-
(ts.isFunctionLike(tsOriginal) || tsHelper.findFirstNodeAbove(tsOriginal, ts.isFunctionLike))) {
3121-
// Separate declaration from assignment to allow for recursion
3122-
statements.push(tstl.createVariableDeclarationStatement(lhs, undefined, parent));
3123-
statements.push(tstl.createAssignmentStatement(lhs, rhs, parent));
3123+
3124+
const insideFunction = this.scopeStack.some(s => s.type === ScopeType.Function);
3125+
const isLetOrConst = tsOriginal && ts.isVariableDeclaration(tsOriginal)
3126+
&& (tsOriginal.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
3127+
if (this.isModule || this.currentNamespace || insideFunction || isLetOrConst) {
3128+
// local
3129+
const isFunction =
3130+
tsOriginal
3131+
&& (ts.isFunctionDeclaration(tsOriginal)
3132+
|| (ts.isVariableDeclaration(tsOriginal) && ts.isFunctionLike(tsOriginal.initializer)));
3133+
if (isFunction) {
3134+
// Separate declaration from assignment for functions to allow recursion
3135+
return [
3136+
tstl.createVariableDeclarationStatement(lhs, undefined, parent, tsOriginal),
3137+
tstl.createAssignmentStatement(lhs, rhs, parent, tsOriginal),
3138+
];
31243139
} else {
3125-
statements.push(tstl.createVariableDeclarationStatement(lhs, rhs, parent));
3140+
return [tstl.createVariableDeclarationStatement(lhs, rhs, parent, tsOriginal)];
31263141
}
3142+
3143+
} else {
3144+
// global
3145+
return [tstl.createAssignmentStatement(lhs, rhs, parent, tsOriginal)];
31273146
}
3128-
return statements;
31293147
}
31303148

31313149
private validateFunctionAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type, toName?: string): void {

test/src/util.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { LuaTransformer } from "../../src/LuaTransformer";
1414

1515
export function transpileString(str: string, options?: CompilerOptions, ignoreDiagnostics = true): string {
1616
if (options) {
17-
if (options.addHeader === undefined) {
18-
options.addHeader = false;
17+
if (options.noHeader === undefined) {
18+
options.noHeader = true;
1919
}
2020
return compilerTranspileString(str, options, ignoreDiagnostics);
2121
} else {
@@ -25,7 +25,7 @@ export function transpileString(str: string, options?: CompilerOptions, ignoreDi
2525
luaLibImport: LuaLibImportKind.Require,
2626
luaTarget: LuaTarget.Lua53,
2727
target: ts.ScriptTarget.ES2015,
28-
addHeader: false,
28+
noHeader: true,
2929
},
3030
ignoreDiagnostics
3131
);

test/translation/ts/getSetAccessors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class MyClass {
88
}
99
}
1010

11-
var instance = new MyClass();
11+
let instance = new MyClass();
1212
instance.field = 4;
1313
const b = instance.field;
14-
const c = (4 + instance.field)*3;
14+
const c = (4 + instance.field)*3;

test/unit/assignments.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class AssignmentTests {
4141
@TestCase("true", "true")
4242
@TestCase("false", "false")
4343
@TestCase(`{a:3,b:"4"}`, `{a = 3, b = "4"}`)
44-
@Test("Const assignment")
44+
@Test("Let assignment")
4545
public letAssignment(inp: string, out: string): void {
4646
const lua = util.transpileString(`let myvar = ${inp};`);
4747
Expect(lua).toBe(`local myvar = ${out};`);
@@ -53,10 +53,10 @@ export class AssignmentTests {
5353
@TestCase("true", "true")
5454
@TestCase("false", "false")
5555
@TestCase(`{a:3,b:"4"}`, `{a = 3, b = "4"}`)
56-
@Test("Const assignment")
56+
@Test("Var assignment")
5757
public varAssignment(inp: string, out: string): void {
5858
const lua = util.transpileString(`var myvar = ${inp};`);
59-
Expect(lua).toBe(`local myvar = ${out};`);
59+
Expect(lua).toBe(`myvar = ${out};`);
6060
}
6161

6262
@TestCase("var myvar;")

0 commit comments

Comments
 (0)