From e0ca0a4bffb4ccf5fa01c7bdba715926a2250906 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 25 Oct 2018 07:50:09 -0600 Subject: [PATCH 01/18] passing nil instead of _G as context for global functions when in ES strict mode --- src/Transpiler.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 06df32d2f..20e83465f 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -74,6 +74,7 @@ export abstract class LuaTranspiler { public namespace: string[]; public importCount: number; public isModule: boolean; + public isStrict: boolean; public sourceFile: ts.SourceFile; public loopStack: number[]; public classStack: string[]; @@ -91,6 +92,8 @@ export abstract class LuaTranspiler { this.importCount = 0; this.sourceFile = sourceFile; this.isModule = tsHelper.isFileModule(sourceFile); + this.isStrict = options.alwaysStrict || options.strict + || (options.target && options.target > ts.ScriptTarget.ES5); this.loopStack = []; this.classStack = []; this.exportStack = []; @@ -1158,7 +1161,7 @@ export abstract class LuaTranspiler { if (!ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { - params = this.transpileArguments(node.arguments, ts.createIdentifier("_G")); + params = this.transpileArguments(node.arguments, ts.createIdentifier(this.isStrict ? "nil" : "_G")); } else { params = this.transpileArguments(node.arguments); } @@ -1197,7 +1200,7 @@ export abstract class LuaTranspiler { if (!ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { - params = this.transpileArguments(node.arguments, ts.createIdentifier("_G")); + params = this.transpileArguments(node.arguments, ts.createIdentifier(this.isStrict ? "nil" : "_G")); } else { params = this.transpileArguments(node.arguments); } @@ -1401,7 +1404,12 @@ export abstract class LuaTranspiler { // Add context as first param if present if (context) { - parameters.push(this.transpileExpression(context)); + if (ts.isIdentifier(context) && context.text === "nil") { + // Avoid "Error: Cannot use Lua keyword nil as identifier." + parameters.push("nil"); + } else { + parameters.push(this.transpileExpression(context)); + } } params.forEach(param => { From dcb71a2803466d548c1a87e2b79bb229e0722e28 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 25 Oct 2018 08:42:27 -0600 Subject: [PATCH 02/18] fixed logic for determining strict mode --- src/Transpiler.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 20e83465f..1967c98fd 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -92,8 +92,9 @@ export abstract class LuaTranspiler { this.importCount = 0; this.sourceFile = sourceFile; this.isModule = tsHelper.isFileModule(sourceFile); - this.isStrict = options.alwaysStrict || options.strict - || (options.target && options.target > ts.ScriptTarget.ES5); + this.isStrict = options.alwaysStrict + || (options.strict && options.alwaysStrict !== false) + || (this.isModule && options.target && options.target >= ts.ScriptTarget.ES2015); this.loopStack = []; this.classStack = []; this.exportStack = []; From a24d775eb69aa6c90db11cdb52ea787b7b5f89d9 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 25 Oct 2018 09:02:04 -0600 Subject: [PATCH 03/18] replaced hack-around when passing nil as a function context with a null keyword --- src/Transpiler.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 1967c98fd..59aaa2d9c 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1162,7 +1162,8 @@ export abstract class LuaTranspiler { if (!ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { - params = this.transpileArguments(node.arguments, ts.createIdentifier(this.isStrict ? "nil" : "_G")); + const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); + params = this.transpileArguments(node.arguments, context); } else { params = this.transpileArguments(node.arguments); } @@ -1201,7 +1202,8 @@ export abstract class LuaTranspiler { if (!ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { - params = this.transpileArguments(node.arguments, ts.createIdentifier(this.isStrict ? "nil" : "_G")); + const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); + params = this.transpileArguments(node.arguments, context); } else { params = this.transpileArguments(node.arguments); } @@ -1405,12 +1407,7 @@ export abstract class LuaTranspiler { // Add context as first param if present if (context) { - if (ts.isIdentifier(context) && context.text === "nil") { - // Avoid "Error: Cannot use Lua keyword nil as identifier." - parameters.push("nil"); - } else { - parameters.push(this.transpileExpression(context)); - } + parameters.push(this.transpileExpression(context)); } params.forEach(param => { From f9a7eb384c103d12db39a0b5167e7b2377e70ed1 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 26 Oct 2018 08:09:03 -0600 Subject: [PATCH 04/18] testing viability of wrapping context/no-context calls on assignment --- src/TSHelper.ts | 6 ++++-- src/Transpiler.ts | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 03db7e577..14bf380a0 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -80,8 +80,10 @@ export class TSHelper { } public static isFunctionType(type: ts.Type, checker: ts.TypeChecker): boolean { - const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); - return typeNode && ts.isFunctionTypeNode(typeNode); + const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); + return sigs.length > 0; + // const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); + // return typeNode && ts.isFunctionTypeNode(typeNode); } public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean { diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 59aaa2d9c..24a1b6f5d 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1144,7 +1144,7 @@ export abstract class LuaTranspiler { public transpileNewExpression(node: ts.NewExpression): string { const name = this.transpileExpression(node.expression); - let params = node.arguments ? this.transpileArguments(node.arguments, ts.createTrue()) : "true"; + let params = node.arguments ? this.transpileArguments(node.arguments, null, ts.createTrue()) : "true"; const type = this.checker.getTypeAtLocation(node); const classDecorators = tsHelper.getCustomDecorators(type, this.checker); @@ -1163,7 +1163,7 @@ export abstract class LuaTranspiler { && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); - params = this.transpileArguments(node.arguments, context); + params = this.transpileArguments(node.arguments, null, context); } else { params = this.transpileArguments(node.arguments); } @@ -1192,20 +1192,21 @@ export abstract class LuaTranspiler { // Handle super calls properly if (node.expression.kind === ts.SyntaxKind.SuperKeyword) { - params = this.transpileArguments(node.arguments, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); + params = this.transpileArguments(node.arguments, null, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); const className = this.classStack[this.classStack.length - 1]; return `${className}.__base.constructor(${params})`; } callPath = this.transpileExpression(node.expression); const type = this.checker.getTypeAtLocation(node.expression); + const sig = (type.symbol.valueDeclaration as ts.SignatureDeclaration); if (!ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); - params = this.transpileArguments(node.arguments, context); + params = this.transpileArguments(node.arguments, sig, context); } else { - params = this.transpileArguments(node.arguments); + params = this.transpileArguments(node.arguments, sig); } return isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment && returnValueIsUsed ? `({ ${callPath}(${params}) })` : `${callPath}(${params})`; @@ -1251,7 +1252,7 @@ export abstract class LuaTranspiler { // Get the type of the function if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) { // Super calls take the format of super.call(self,...) - params = this.transpileArguments(node.arguments, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); + params = this.transpileArguments(node.arguments, null, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); return `${this.transpileExpression(node.expression)}(${params})`; } else { // Replace last . with : here @@ -1402,7 +1403,8 @@ export abstract class LuaTranspiler { } } - public transpileArguments(params: ts.NodeArray, context?: ts.Expression): string { + public transpileArguments(params: ts.NodeArray, sig?: ts.SignatureDeclaration, + context?: ts.Expression): string { const parameters: string[] = []; // Add context as first param if present @@ -1410,9 +1412,32 @@ export abstract class LuaTranspiler { parameters.push(this.transpileExpression(context)); } - params.forEach(param => { - parameters.push(this.transpileExpression(param)); - }); + if (sig) { + for (let i = 0; i < params.length; ++i) { + const param = params[i]; + const paramTypeNode = sig.parameters[i].type; + const paramType = this.checker.getTypeAtLocation(paramTypeNode); + if (tsHelper.isFunctionType(paramType, this.checker)) { + const argType = this.checker.getTypeAtLocation(param); + const argNoContext = tsHelper.getCustomDecorators(argType, this.checker) + .has(DecoratorKind.NoContext); + const paramNoContext = tsHelper.getCustomDecorators(paramType, this.checker) + .has(DecoratorKind.NoContext); + if (argNoContext && !paramNoContext) { + parameters.push(`function(____, ...) return ${this.transpileExpression(param)}(...) end`); + continue; + } else if (!argNoContext && paramNoContext) { + parameters.push(`function(...) return ${this.transpileExpression(param)}(nil, ...) end`); + continue; + } + } + parameters.push(this.transpileExpression(param)); + } + } else { + params.forEach(param => { + parameters.push(this.transpileExpression(param)); + }); + } return parameters.join(","); } From 90b7eb9a6cae3945d4c8637ba456ddc8dc7bb36c Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 26 Oct 2018 08:56:22 -0600 Subject: [PATCH 05/18] working on more function assignment situations --- src/TSHelper.ts | 9 ++++-- src/Transpiler.ts | 75 +++++++++++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 14bf380a0..e4c97b92b 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -79,11 +79,14 @@ export class TSHelper { return typeNode && this.isArrayTypeNode(typeNode); } - public static isFunctionType(type: ts.Type, checker: ts.TypeChecker): boolean { + public static isCallableType(type: ts.Type, checker: ts.TypeChecker): boolean { const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); return sigs.length > 0; - // const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); - // return typeNode && ts.isFunctionTypeNode(typeNode); + } + + public static isFunctionType(type: ts.Type, checker: ts.TypeChecker): boolean { + const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); + return typeNode && ts.isFunctionTypeNode(typeNode); } public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean { diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 24a1b6f5d..252273475 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -908,7 +908,9 @@ export abstract class LuaTranspiler { result = `${lhs}<=${rhs}`; break; case ts.SyntaxKind.EqualsToken: - result = this.transpileAssignment(node, lhs, rhs); + const assignType = this.checker.getTypeAtLocation(node.left); // TOMB: will this work? + const arhs = this.transpileExpressionForAssignment(node.right, assignType); + result = this.transpileAssignment(node, lhs, arhs); break; case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: @@ -1144,7 +1146,9 @@ export abstract class LuaTranspiler { public transpileNewExpression(node: ts.NewExpression): string { const name = this.transpileExpression(node.expression); - let params = node.arguments ? this.transpileArguments(node.arguments, null, ts.createTrue()) : "true"; + const constructorType = this.checker.getTypeAtLocation(node.expression); + const sig = (constructorType.symbol.valueDeclaration as ts.SignatureDeclaration); + let params = node.arguments ? this.transpileArguments(node.arguments, sig, ts.createTrue()) : "true"; const type = this.checker.getTypeAtLocation(node); const classDecorators = tsHelper.getCustomDecorators(type, this.checker); @@ -1163,9 +1167,9 @@ export abstract class LuaTranspiler { && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); - params = this.transpileArguments(node.arguments, null, context); + params = this.transpileArguments(node.arguments, sig, context); } else { - params = this.transpileArguments(node.arguments); + params = this.transpileArguments(node.arguments, sig); } return `${customDecorator.args[0]}(${params})`; } @@ -1190,16 +1194,18 @@ export abstract class LuaTranspiler { ? `({ ${result} })` : result; } + const type = this.checker.getTypeAtLocation(node.expression); + const sig = (type.symbol.valueDeclaration as ts.SignatureDeclaration); + // Handle super calls properly if (node.expression.kind === ts.SyntaxKind.SuperKeyword) { - params = this.transpileArguments(node.arguments, null, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); + params = this.transpileArguments(node.arguments, sig, + ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); const className = this.classStack[this.classStack.length - 1]; return `${className}.__base.constructor(${params})`; } callPath = this.transpileExpression(node.expression); - const type = this.checker.getTypeAtLocation(node.expression); - const sig = (type.symbol.valueDeclaration as ts.SignatureDeclaration); if (!ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { @@ -1249,10 +1255,14 @@ export abstract class LuaTranspiler { return this.transpileFunctionCallExpression(node); } + const type = this.checker.getTypeAtLocation(node.expression); + const sig = (type.symbol.valueDeclaration as ts.SignatureDeclaration); + // Get the type of the function if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) { // Super calls take the format of super.call(self,...) - params = this.transpileArguments(node.arguments, null, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); + params = this.transpileArguments(node.arguments, sig, + ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); return `${this.transpileExpression(node.expression)}(${params})`; } else { // Replace last . with : here @@ -1261,13 +1271,13 @@ export abstract class LuaTranspiler { return `tostring(${this.transpileExpression(node.expression.expression)})`; } else if (name === "hasOwnProperty") { const expr = this.transpileExpression(node.expression.expression); - params = this.transpileArguments(node.arguments); + params = this.transpileArguments(node.arguments, sig); return `(rawget(${expr}, ${params} )~=nil)`; } else { const type = this.checker.getTypeAtLocation(node.expression); const op = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? "." : ":"; callPath = `${this.transpileExpression(node.expression.expression)}${op}${name}`; - params = this.transpileArguments(node.arguments); + params = this.transpileArguments(node.arguments, sig); return `${callPath}(${params})`; } } @@ -1403,6 +1413,23 @@ export abstract class LuaTranspiler { } } + public transpileExpressionForAssignment(node: ts.Expression, assignType: ts.Type): string { + if (tsHelper.isCallableType(assignType, this.checker)) { + const type = this.checker.getTypeAtLocation(node); + const noContext = tsHelper.getCustomDecorators(type, this.checker) + .has(DecoratorKind.NoContext); + const assignNoContext = tsHelper.getCustomDecorators(assignType, this.checker) + .has(DecoratorKind.NoContext); + if (noContext && !assignNoContext) { + return `function(____, ...) return ${this.transpileExpression(node)}(...) end`; + } else if (!noContext && assignNoContext) { + const context = this.isStrict ? "nil" : "_G"; + return `function(...) return ${this.transpileExpression(node)}(${context}, ...) end`; + } + } + return this.transpileExpression(node); + } + public transpileArguments(params: ts.NodeArray, sig?: ts.SignatureDeclaration, context?: ts.Expression): string { const parameters: string[] = []; @@ -1412,26 +1439,11 @@ export abstract class LuaTranspiler { parameters.push(this.transpileExpression(context)); } - if (sig) { + if (sig && sig.parameters.length >= params.length) { for (let i = 0; i < params.length; ++i) { const param = params[i]; - const paramTypeNode = sig.parameters[i].type; - const paramType = this.checker.getTypeAtLocation(paramTypeNode); - if (tsHelper.isFunctionType(paramType, this.checker)) { - const argType = this.checker.getTypeAtLocation(param); - const argNoContext = tsHelper.getCustomDecorators(argType, this.checker) - .has(DecoratorKind.NoContext); - const paramNoContext = tsHelper.getCustomDecorators(paramType, this.checker) - .has(DecoratorKind.NoContext); - if (argNoContext && !paramNoContext) { - parameters.push(`function(____, ...) return ${this.transpileExpression(param)}(...) end`); - continue; - } else if (!argNoContext && paramNoContext) { - parameters.push(`function(...) return ${this.transpileExpression(param)}(nil, ...) end`); - continue; - } - } - parameters.push(this.transpileExpression(param)); + const paramType = this.checker.getTypeAtLocation(sig.parameters[i].type); + parameters.push(this.transpileExpressionForAssignment(param, paramType)); } } else { params.forEach(param => { @@ -1621,7 +1633,8 @@ export abstract class LuaTranspiler { // Find variable identifier const identifierName = this.transpileIdentifier(node.name); if (node.initializer) { - const value = this.transpileExpression(node.initializer); + const type = this.checker.getTypeAtLocation(node.type); // TOMB: will this work? + const value = this.transpileExpressionForAssignment(node.initializer, type); if (ts.isFunctionExpression(node.initializer) || ts.isArrowFunction(node.initializer)) { // Separate declaration and assignment for functions to allow recursion return `local ${identifierName}; ${identifierName} = ${value}`; @@ -1643,9 +1656,9 @@ export abstract class LuaTranspiler { // Don't unpack TupleReturn decorated functions if (tsHelper.isTupleReturnCall(node.initializer, this.checker)) { - return `local ${vars}=${this.transpileExpression(node.initializer)}`; + return `local ${vars}=${this.transpileExpression(node.initializer)}`; // TOMB: todo } else { - return `local ${vars}=${this.transpileDestructingAssignmentValue(node.initializer)}`; + return `local ${vars}=${this.transpileDestructingAssignmentValue(node.initializer)}`; // TOMB: todo } } else { throw TSTLErrors.UnsupportedKind("variable declaration", node.name.kind, node); From 10be9634560321f6ec4ab2651fd7a24fdc28a75f Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 29 Oct 2018 07:13:34 -0600 Subject: [PATCH 06/18] fixed getting constructor signature and refactored things a bit --- src/TSHelper.ts | 5 +++++ src/Transpiler.ts | 49 ++++++++++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index e4c97b92b..576b9a1e1 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -253,4 +253,9 @@ export class TSHelper { } return [false, null, null]; } + + public static getFunctionSignature(declarations: ts.Declaration[]): ts.SignatureDeclaration { + return declarations + && declarations.find(d => (d as ts.FunctionLikeDeclaration).body !== undefined) as ts.SignatureDeclaration; + } } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 252273475..0befc74c1 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -846,7 +846,7 @@ export abstract class LuaTranspiler { // Transpile operands const lhs = this.transpileExpression(node.left, true); - const rhs = this.transpileExpression(node.right, true); + let rhs = this.transpileExpression(node.right, true); let result = ""; @@ -908,9 +908,9 @@ export abstract class LuaTranspiler { result = `${lhs}<=${rhs}`; break; case ts.SyntaxKind.EqualsToken: - const assignType = this.checker.getTypeAtLocation(node.left); // TOMB: will this work? - const arhs = this.transpileExpressionForAssignment(node.right, assignType); - result = this.transpileAssignment(node, lhs, arhs); + const assignType = this.checker.getTypeAtLocation(node.left); + rhs = this.transpileExpressionForAssignment(node.right, assignType); + result = this.transpileAssignment(node, lhs, rhs); break; case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: @@ -945,7 +945,7 @@ export abstract class LuaTranspiler { } if (ts.isArrayLiteralExpression(node.left)) { - // Destructing assignment + // Destructing assignment - TOMB: todo const vars = node.left.elements.map(e => this.transpileExpression(e)).join(","); const vals = tsHelper.isTupleReturnCall(node.right, this.checker) ? rhs : this.transpileDestructingAssignmentValue(node.right); @@ -1147,7 +1147,11 @@ export abstract class LuaTranspiler { public transpileNewExpression(node: ts.NewExpression): string { const name = this.transpileExpression(node.expression); const constructorType = this.checker.getTypeAtLocation(node.expression); - const sig = (constructorType.symbol.valueDeclaration as ts.SignatureDeclaration); + let sig: ts.SignatureDeclaration; + if (constructorType.symbol.members && constructorType.symbol.members.has(ts.InternalSymbolName.Constructor)) { + const constructorDecl = constructorType.symbol.members.get(ts.InternalSymbolName.Constructor); + sig = tsHelper.getFunctionSignature(constructorDecl.declarations); + } let params = node.arguments ? this.transpileArguments(node.arguments, sig, ts.createTrue()) : "true"; const type = this.checker.getTypeAtLocation(node); const classDecorators = tsHelper.getCustomDecorators(type, this.checker); @@ -1167,9 +1171,9 @@ export abstract class LuaTranspiler { && !ts.isElementAccessExpression(node.expression) && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); - params = this.transpileArguments(node.arguments, sig, context); + params = this.transpileArguments(node.arguments, null, context); } else { - params = this.transpileArguments(node.arguments, sig); + params = this.transpileArguments(node.arguments); } return `${customDecorator.args[0]}(${params})`; } @@ -1195,7 +1199,7 @@ export abstract class LuaTranspiler { } const type = this.checker.getTypeAtLocation(node.expression); - const sig = (type.symbol.valueDeclaration as ts.SignatureDeclaration); + const sig = tsHelper.getFunctionSignature(type.symbol.declarations); // Handle super calls properly if (node.expression.kind === ts.SyntaxKind.SuperKeyword) { @@ -1256,7 +1260,7 @@ export abstract class LuaTranspiler { } const type = this.checker.getTypeAtLocation(node.expression); - const sig = (type.symbol.valueDeclaration as ts.SignatureDeclaration); + const sig = tsHelper.getFunctionSignature(type.symbol.declarations); // Get the type of the function if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) { @@ -1416,15 +1420,17 @@ export abstract class LuaTranspiler { public transpileExpressionForAssignment(node: ts.Expression, assignType: ts.Type): string { if (tsHelper.isCallableType(assignType, this.checker)) { const type = this.checker.getTypeAtLocation(node); - const noContext = tsHelper.getCustomDecorators(type, this.checker) - .has(DecoratorKind.NoContext); - const assignNoContext = tsHelper.getCustomDecorators(assignType, this.checker) - .has(DecoratorKind.NoContext); - if (noContext && !assignNoContext) { - return `function(____, ...) return ${this.transpileExpression(node)}(...) end`; - } else if (!noContext && assignNoContext) { - const context = this.isStrict ? "nil" : "_G"; - return `function(...) return ${this.transpileExpression(node)}(${context}, ...) end`; + if (tsHelper.isCallableType(type, this.checker)) { + const noContext = tsHelper.getCustomDecorators(type, this.checker) + .has(DecoratorKind.NoContext); + const assignNoContext = tsHelper.getCustomDecorators(assignType, this.checker) + .has(DecoratorKind.NoContext); + if (noContext && !assignNoContext) { + return `function(____, ...) return ${this.transpileExpression(node)}(...) end`; + } else if (!noContext && assignNoContext) { + const context = this.isStrict ? "nil" : "_G"; + return `function(...) return ${this.transpileExpression(node)}(${context}, ...) end`; + } } } return this.transpileExpression(node); @@ -1633,7 +1639,7 @@ export abstract class LuaTranspiler { // Find variable identifier const identifierName = this.transpileIdentifier(node.name); if (node.initializer) { - const type = this.checker.getTypeAtLocation(node.type); // TOMB: will this work? + const type = this.checker.getTypeAtLocation(node.type); const value = this.transpileExpressionForAssignment(node.initializer, type); if (ts.isFunctionExpression(node.initializer) || ts.isArrowFunction(node.initializer)) { // Separate declaration and assignment for functions to allow recursion @@ -1956,6 +1962,9 @@ export abstract class LuaTranspiler { public transpileConstructor(node: ts.ConstructorDeclaration, className: string): string { + // Don't transpile methods without body (overload declarations) + if (!node.body) { return ""; } + const extraInstanceFields = []; const parameters = ["self"]; From fa5dd9b5c896fe7e7b987a89e182b381c0061761 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 29 Oct 2018 16:04:08 -0600 Subject: [PATCH 07/18] checking resolved signature when comparing function types passed as arguments --- src/Transpiler.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 0befc74c1..d76c237e8 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1146,12 +1146,7 @@ export abstract class LuaTranspiler { public transpileNewExpression(node: ts.NewExpression): string { const name = this.transpileExpression(node.expression); - const constructorType = this.checker.getTypeAtLocation(node.expression); - let sig: ts.SignatureDeclaration; - if (constructorType.symbol.members && constructorType.symbol.members.has(ts.InternalSymbolName.Constructor)) { - const constructorDecl = constructorType.symbol.members.get(ts.InternalSymbolName.Constructor); - sig = tsHelper.getFunctionSignature(constructorDecl.declarations); - } + const sig = this.checker.getResolvedSignature(node); let params = node.arguments ? this.transpileArguments(node.arguments, sig, ts.createTrue()) : "true"; const type = this.checker.getTypeAtLocation(node); const classDecorators = tsHelper.getCustomDecorators(type, this.checker); @@ -1198,8 +1193,7 @@ export abstract class LuaTranspiler { ? `({ ${result} })` : result; } - const type = this.checker.getTypeAtLocation(node.expression); - const sig = tsHelper.getFunctionSignature(type.symbol.declarations); + const sig = this.checker.getResolvedSignature(node); // Handle super calls properly if (node.expression.kind === ts.SyntaxKind.SuperKeyword) { @@ -1209,6 +1203,7 @@ export abstract class LuaTranspiler { return `${className}.__base.constructor(${params})`; } + const type = this.checker.getTypeAtLocation(node.expression); callPath = this.transpileExpression(node.expression); if (!ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression) @@ -1259,8 +1254,7 @@ export abstract class LuaTranspiler { return this.transpileFunctionCallExpression(node); } - const type = this.checker.getTypeAtLocation(node.expression); - const sig = tsHelper.getFunctionSignature(type.symbol.declarations); + const sig = this.checker.getResolvedSignature(node); // Get the type of the function if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) { @@ -1436,7 +1430,7 @@ export abstract class LuaTranspiler { return this.transpileExpression(node); } - public transpileArguments(params: ts.NodeArray, sig?: ts.SignatureDeclaration, + public transpileArguments(params: ts.NodeArray, sig?: ts.Signature, context?: ts.Expression): string { const parameters: string[] = []; @@ -1448,7 +1442,7 @@ export abstract class LuaTranspiler { if (sig && sig.parameters.length >= params.length) { for (let i = 0; i < params.length; ++i) { const param = params[i]; - const paramType = this.checker.getTypeAtLocation(sig.parameters[i].type); + const paramType = this.checker.getTypeAtLocation(sig.parameters[i].valueDeclaration); parameters.push(this.transpileExpressionForAssignment(param, paramType)); } } else { From a24caa2270b77ec0b4e448e33d903bf44d0471ac Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 19 Nov 2018 08:30:09 -0700 Subject: [PATCH 08/18] working on assignment checks for methods vs functions --- src/TSHelper.ts | 20 ++++++++++++++++++++ src/Transpiler.ts | 28 ++++++++++++++++------------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 576b9a1e1..07a176dc8 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -258,4 +258,24 @@ export class TSHelper { return declarations && declarations.find(d => (d as ts.FunctionLikeDeclaration).body !== undefined) as ts.SignatureDeclaration; } + + public static isDeclarationWithContext(sigDecl: ts.SignatureDeclaration): boolean { + const thisArg = sigDecl.parameters.find(p => ts.isIdentifier(p.name) + && p.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); + if (thisArg && thisArg.type && thisArg.type.kind === ts.SyntaxKind.VoidKeyword) { + return false; + } + return thisArg !== undefined || ts.isMethodDeclaration(sigDecl) || ts.isMethodSignature(sigDecl) + || ts.isPropertySignature(sigDecl.parent) || ts.isPropertyDeclaration(sigDecl.parent); + } + + public static isFunctionWithContext(type: ts.Type, checker: ts.TypeChecker): boolean { + if (!this.isCallableType(type, checker)) { + return false; + } + const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); + const sigDecls = sigs.map(s => s.getDeclaration()); + const isMethod = sigDecls.some(this.isDeclarationWithContext); + return isMethod; + } } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index d76c237e8..b398d1e62 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -4,7 +4,7 @@ import * as ts from "typescript"; import { CompilerOptions } from "./CompilerOptions"; import { DecoratorKind } from "./Decorator"; -import { TSTLErrors } from "./Errors"; +import { TSTLErrors, TranspileError } from "./Errors"; import { TSHelper as tsHelper } from "./TSHelper"; /* tslint:disable */ @@ -1412,18 +1412,22 @@ export abstract class LuaTranspiler { } public transpileExpressionForAssignment(node: ts.Expression, assignType: ts.Type): string { - if (tsHelper.isCallableType(assignType, this.checker)) { + if (tsHelper.isCallableType(assignType, this.checker) && !ts.isFunctionExpression(node) + && !ts.isArrowFunction(node)) { const type = this.checker.getTypeAtLocation(node); - if (tsHelper.isCallableType(type, this.checker)) { - const noContext = tsHelper.getCustomDecorators(type, this.checker) - .has(DecoratorKind.NoContext); - const assignNoContext = tsHelper.getCustomDecorators(assignType, this.checker) - .has(DecoratorKind.NoContext); - if (noContext && !assignNoContext) { - return `function(____, ...) return ${this.transpileExpression(node)}(...) end`; - } else if (!noContext && assignNoContext) { - const context = this.isStrict ? "nil" : "_G"; - return `function(...) return ${this.transpileExpression(node)}(${context}, ...) end`; + if (tsHelper.isCallableType(type, this.checker)) + { + const hasContext = tsHelper.isFunctionWithContext(type, this.checker); + const assignHasContext = tsHelper.isFunctionWithContext(assignType, this.checker); + if (hasContext !== assignHasContext) { + const pos = ts.getLineAndCharacterOfPosition(this.sourceFile, node.pos); + if (hasContext) { + console.error(`${this.sourceFile.fileName}:${pos.line + 1}:${pos.character} ` + + `Cannot convert method to function`); + } else { + console.error(`${this.sourceFile.fileName}:${pos.line + 1}:${pos.character} ` + + `Cannot convert function to method`); + } } } } From 7fb2cad7ec290356785a29f960a478f6341e5d4f Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 19 Nov 2018 13:13:09 -0700 Subject: [PATCH 09/18] handling context in calls and decls --- src/TSHelper.ts | 28 +++++++++++++++++++++------- src/Transpiler.ts | 32 ++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 07a176dc8..effc0d884 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -259,14 +259,29 @@ export class TSHelper { && declarations.find(d => (d as ts.FunctionLikeDeclaration).body !== undefined) as ts.SignatureDeclaration; } - public static isDeclarationWithContext(sigDecl: ts.SignatureDeclaration): boolean { + public static isDeclarationWithContext(sigDecl: ts.SignatureDeclaration, checker: ts.TypeChecker): boolean { const thisArg = sigDecl.parameters.find(p => ts.isIdentifier(p.name) && p.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); - if (thisArg && thisArg.type && thisArg.type.kind === ts.SyntaxKind.VoidKeyword) { - return false; + if (thisArg) { + // Explicit 'this' + return !thisArg.type || thisArg.type.kind !== ts.SyntaxKind.VoidKeyword; + } + if ((ts.isMethodDeclaration(sigDecl) || ts.isMethodSignature(sigDecl)) + && !(ts.getCombinedModifierFlags(sigDecl) & ts.ModifierFlags.Static)) { + // Non-static method + return true; + } + if ((ts.isPropertySignature(sigDecl.parent) || ts.isPropertyDeclaration(sigDecl.parent)) + && !(ts.getCombinedModifierFlags(sigDecl.parent) & ts.ModifierFlags.Static)) { + // Non-static lambda property + return true; } - return thisArg !== undefined || ts.isMethodDeclaration(sigDecl) || ts.isMethodSignature(sigDecl) - || ts.isPropertySignature(sigDecl.parent) || ts.isPropertyDeclaration(sigDecl.parent); + if (ts.isBinaryExpression(sigDecl.parent) + && this.isFunctionWithContext(checker.getTypeAtLocation(sigDecl.parent.left), checker)) { + // Function expression: check type being assigned to + return true; + } + return false; } public static isFunctionWithContext(type: ts.Type, checker: ts.TypeChecker): boolean { @@ -275,7 +290,6 @@ export class TSHelper { } const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); const sigDecls = sigs.map(s => s.getDeclaration()); - const isMethod = sigDecls.some(this.isDeclarationWithContext); - return isMethod; + return sigDecls.some(s => this.isDeclarationWithContext(s, checker)); } } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index b398d1e62..46681df5e 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1162,9 +1162,9 @@ export abstract class LuaTranspiler { if (!customDecorator.args[0]) { throw TSTLErrors.InvalidDecoratorArgumentNumber("!CustomConstructor", 0, 1, node); } - if (!ts.isPropertyAccessExpression(node.expression) - && !ts.isElementAccessExpression(node.expression) - && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { + if (!tsHelper.isFunctionWithContext(type, this.checker) + && !ts.isPropertyAccessExpression(node.expression) + && !ts.isElementAccessExpression(node.expression)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); params = this.transpileArguments(node.arguments, null, context); } else { @@ -1205,9 +1205,9 @@ export abstract class LuaTranspiler { const type = this.checker.getTypeAtLocation(node.expression); callPath = this.transpileExpression(node.expression); - if (!ts.isPropertyAccessExpression(node.expression) - && !ts.isElementAccessExpression(node.expression) - && !tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext)) { + if (tsHelper.isFunctionWithContext(type, this.checker) + && !ts.isPropertyAccessExpression(node.expression) + && !ts.isElementAccessExpression(node.expression)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); params = this.transpileArguments(node.arguments, sig, context); } else { @@ -1273,7 +1273,7 @@ export abstract class LuaTranspiler { return `(rawget(${expr}, ${params} )~=nil)`; } else { const type = this.checker.getTypeAtLocation(node.expression); - const op = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? "." : ":"; + const op = tsHelper.isFunctionWithContext(type, this.checker) ? ":" : "."; callPath = `${this.transpileExpression(node.expression.expression)}${op}${name}`; params = this.transpileArguments(node.arguments, sig); return `${callPath}(${params})`; @@ -1415,18 +1415,17 @@ export abstract class LuaTranspiler { if (tsHelper.isCallableType(assignType, this.checker) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node)) { const type = this.checker.getTypeAtLocation(node); - if (tsHelper.isCallableType(type, this.checker)) - { + if (tsHelper.isCallableType(type, this.checker)) { const hasContext = tsHelper.isFunctionWithContext(type, this.checker); const assignHasContext = tsHelper.isFunctionWithContext(assignType, this.checker); if (hasContext !== assignHasContext) { const pos = ts.getLineAndCharacterOfPosition(this.sourceFile, node.pos); if (hasContext) { console.error(`${this.sourceFile.fileName}:${pos.line + 1}:${pos.character} ` - + `Cannot convert method to function`); + + `Cannot convert method to function`); } else { console.error(`${this.sourceFile.fileName}:${pos.line + 1}:${pos.character} ` - + `Cannot convert function to method`); + + `Cannot convert function to method`); } } } @@ -1677,7 +1676,7 @@ export abstract class LuaTranspiler { const methodName = this.transpileIdentifier(node.name); const type = this.checker.getTypeAtLocation(node); - const context = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? null : "self"; + const context = tsHelper.isFunctionWithContext(type, this.checker) ? "self" : null; const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); let prefix = this.accessPrefix(node); @@ -1714,6 +1713,9 @@ export abstract class LuaTranspiler { // Only push parameter name to paramName array if it isn't a spread parameter for (const param of parameters) { + if (ts.isIdentifier(param.name) && param.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword) { + continue; + } const paramName = this.transpileIdentifier(param.name as ts.Identifier); // This parameter is a spread parameter (...param) @@ -1760,7 +1762,7 @@ export abstract class LuaTranspiler { } const type = this.checker.getTypeAtLocation(node); - const context = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? null : "self"; + const context = tsHelper.isFunctionWithContext(type, this.checker) ? "self" : null; const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); // Build function header @@ -2032,8 +2034,10 @@ export abstract class LuaTranspiler { } public transpileFunctionExpression(node: ts.FunctionLikeDeclaration, context: string | null): string { + const type = this.checker.getTypeAtLocation(node); + const hasContext = tsHelper.isFunctionWithContext(type, this.checker); // Build parameter string - const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); + const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, hasContext ? context : null); let result = `function(${paramNames.join(",")})\n`; this.pushIndent(); const body = ts.isBlock(node.body) ? node.body : ts.createBlock([ts.createReturn(node.body)]); From 8c75ff03fc6010895cfcbd55952b84676348f5d2 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 19 Nov 2018 15:37:27 -0700 Subject: [PATCH 10/18] refactoring and handling tuple destructuring --- src/Transpiler.ts | 67 +++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 46681df5e..75e0d30f1 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -4,7 +4,7 @@ import * as ts from "typescript"; import { CompilerOptions } from "./CompilerOptions"; import { DecoratorKind } from "./Decorator"; -import { TSTLErrors, TranspileError } from "./Errors"; +import { TSTLErrors } from "./Errors"; import { TSHelper as tsHelper } from "./TSHelper"; /* tslint:disable */ @@ -846,7 +846,7 @@ export abstract class LuaTranspiler { // Transpile operands const lhs = this.transpileExpression(node.left, true); - let rhs = this.transpileExpression(node.right, true); + const rhs = this.transpileExpression(node.right, true); let result = ""; @@ -908,8 +908,9 @@ export abstract class LuaTranspiler { result = `${lhs}<=${rhs}`; break; case ts.SyntaxKind.EqualsToken: - const assignType = this.checker.getTypeAtLocation(node.left); - rhs = this.transpileExpressionForAssignment(node.right, assignType); + const fromType = this.checker.getTypeAtLocation(node.right); + const toType = this.checker.getTypeAtLocation(node.left); + this.validateAssignmentExpression(fromType, toType, node.right.pos); result = this.transpileAssignment(node, lhs, rhs); break; case ts.SyntaxKind.EqualsEqualsToken: @@ -945,7 +946,14 @@ export abstract class LuaTranspiler { } if (ts.isArrayLiteralExpression(node.left)) { - // Destructing assignment - TOMB: todo + // Destructing assignment + const initializerType = this.checker.getTypeAtLocation(node.right) as ts.TypeReference; + const tupleType = this.checker.getTypeAtLocation(node.left) as ts.TypeReference; + if (tupleType.typeArguments && initializerType.typeArguments) { + tupleType.typeArguments.forEach((t, i) => { + this.validateAssignmentExpression(initializerType.typeArguments[i], t, node.right.pos); + }); + } const vars = node.left.elements.map(e => this.transpileExpression(e)).join(","); const vals = tsHelper.isTupleReturnCall(node.right, this.checker) ? rhs : this.transpileDestructingAssignmentValue(node.right); @@ -1411,26 +1419,21 @@ export abstract class LuaTranspiler { } } - public transpileExpressionForAssignment(node: ts.Expression, assignType: ts.Type): string { - if (tsHelper.isCallableType(assignType, this.checker) && !ts.isFunctionExpression(node) - && !ts.isArrowFunction(node)) { - const type = this.checker.getTypeAtLocation(node); - if (tsHelper.isCallableType(type, this.checker)) { - const hasContext = tsHelper.isFunctionWithContext(type, this.checker); - const assignHasContext = tsHelper.isFunctionWithContext(assignType, this.checker); - if (hasContext !== assignHasContext) { - const pos = ts.getLineAndCharacterOfPosition(this.sourceFile, node.pos); - if (hasContext) { - console.error(`${this.sourceFile.fileName}:${pos.line + 1}:${pos.character} ` - + `Cannot convert method to function`); - } else { - console.error(`${this.sourceFile.fileName}:${pos.line + 1}:${pos.character} ` - + `Cannot convert function to method`); - } + public validateAssignmentExpression(fromType: ts.Type, toType: ts.Type, pos: number): void { + if (tsHelper.isCallableType(toType, this.checker) && tsHelper.isCallableType(fromType, this.checker)) { + const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker); + const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker); + if (fromHasContext !== toHasContext) { + const linePos = ts.getLineAndCharacterOfPosition(this.sourceFile, pos); + if (fromHasContext) { + console.error(`${this.sourceFile.fileName}:${linePos.line + 1}:${linePos.character} ` + + `Cannot convert method to function`); + } else { + console.error(`${this.sourceFile.fileName}:${linePos.line + 1}:${linePos.character} ` + + `Cannot convert function to method`); } } } - return this.transpileExpression(node); } public transpileArguments(params: ts.NodeArray, sig?: ts.Signature, @@ -1445,8 +1448,10 @@ export abstract class LuaTranspiler { if (sig && sig.parameters.length >= params.length) { for (let i = 0; i < params.length; ++i) { const param = params[i]; - const paramType = this.checker.getTypeAtLocation(sig.parameters[i].valueDeclaration); - parameters.push(this.transpileExpressionForAssignment(param, paramType)); + const paramType = this.checker.getTypeAtLocation(param); + const sigType = this.checker.getTypeAtLocation(sig.parameters[i].valueDeclaration); + this.validateAssignmentExpression(paramType, sigType, param.pos); + parameters.push(this.transpileExpression(param)); } } else { params.forEach(param => { @@ -1636,8 +1641,10 @@ export abstract class LuaTranspiler { // Find variable identifier const identifierName = this.transpileIdentifier(node.name); if (node.initializer) { - const type = this.checker.getTypeAtLocation(node.type); - const value = this.transpileExpressionForAssignment(node.initializer, type); + const initializerType = this.checker.getTypeAtLocation(node.initializer); + const varType = this.checker.getTypeFromTypeNode(node.type); + this.validateAssignmentExpression(initializerType, varType, node.initializer.pos); + const value = this.transpileExpression(node.initializer); if (ts.isFunctionExpression(node.initializer) || ts.isArrowFunction(node.initializer)) { // Separate declaration and assignment for functions to allow recursion return `local ${identifierName}; ${identifierName} = ${value}`; @@ -1655,6 +1662,14 @@ export abstract class LuaTranspiler { throw TSTLErrors.ForbiddenEllipsisDestruction(node); } + const initializerType = this.checker.getTypeAtLocation(node.initializer) as ts.TypeReference; + const tupleType = this.checker.getTypeFromTypeNode(node.type) as ts.TypeReference; + if (tupleType.typeArguments && initializerType.typeArguments) { + tupleType.typeArguments.forEach((t, i) => { + this.validateAssignmentExpression(initializerType.typeArguments[i], t, node.pos); + }); + } + const vars = node.name.elements.map(e => this.transpileArrayBindingElement(e)).join(","); // Don't unpack TupleReturn decorated functions From fec41f0c70b3cded3b0fcbadd7b57e56c0c27350 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 19 Nov 2018 15:51:29 -0700 Subject: [PATCH 11/18] generalized tuple assignment checking --- src/Transpiler.ts | 51 ++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 75e0d30f1..09d7064cc 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -908,9 +908,6 @@ export abstract class LuaTranspiler { result = `${lhs}<=${rhs}`; break; case ts.SyntaxKind.EqualsToken: - const fromType = this.checker.getTypeAtLocation(node.right); - const toType = this.checker.getTypeAtLocation(node.left); - this.validateAssignmentExpression(fromType, toType, node.right.pos); result = this.transpileAssignment(node, lhs, rhs); break; case ts.SyntaxKind.EqualsEqualsToken: @@ -945,15 +942,12 @@ export abstract class LuaTranspiler { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, rhs); } + // Validate assignment + const rightType = this.checker.getTypeAtLocation(node.right); + const leftType = this.checker.getTypeAtLocation(node.left); + this.validateAssignment(rightType, leftType, node.right.pos); + if (ts.isArrayLiteralExpression(node.left)) { - // Destructing assignment - const initializerType = this.checker.getTypeAtLocation(node.right) as ts.TypeReference; - const tupleType = this.checker.getTypeAtLocation(node.left) as ts.TypeReference; - if (tupleType.typeArguments && initializerType.typeArguments) { - tupleType.typeArguments.forEach((t, i) => { - this.validateAssignmentExpression(initializerType.typeArguments[i], t, node.right.pos); - }); - } const vars = node.left.elements.map(e => this.transpileExpression(e)).join(","); const vals = tsHelper.isTupleReturnCall(node.right, this.checker) ? rhs : this.transpileDestructingAssignmentValue(node.right); @@ -1419,8 +1413,15 @@ export abstract class LuaTranspiler { } } - public validateAssignmentExpression(fromType: ts.Type, toType: ts.Type, pos: number): void { - if (tsHelper.isCallableType(toType, this.checker) && tsHelper.isCallableType(fromType, this.checker)) { + public validateAssignment(fromType: ts.Type, toType: ts.Type, pos: number): void { + if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) { + (fromType as ts.TypeReference).typeArguments.forEach((t, i) => { + // Recurse into tuples + this.validateAssignment(t, (toType as ts.TypeReference).typeArguments[i], pos); + }); + + } else if (tsHelper.isCallableType(toType, this.checker) && tsHelper.isCallableType(fromType, this.checker)) { + // Check function assignments const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker); const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker); if (fromHasContext !== toHasContext) { @@ -1450,7 +1451,7 @@ export abstract class LuaTranspiler { const param = params[i]; const paramType = this.checker.getTypeAtLocation(param); const sigType = this.checker.getTypeAtLocation(sig.parameters[i].valueDeclaration); - this.validateAssignmentExpression(paramType, sigType, param.pos); + this.validateAssignment(paramType, sigType, param.pos); parameters.push(this.transpileExpression(param)); } } else { @@ -1637,13 +1638,17 @@ export abstract class LuaTranspiler { } public transpileVariableDeclaration(node: ts.VariableDeclaration): string { + if (node.initializer) { + // Validate assignment + const initializerType = this.checker.getTypeAtLocation(node.initializer); + const varType = this.checker.getTypeFromTypeNode(node.type); + this.validateAssignment(initializerType, varType, node.initializer.pos); + } + if (ts.isIdentifier(node.name)) { // Find variable identifier const identifierName = this.transpileIdentifier(node.name); if (node.initializer) { - const initializerType = this.checker.getTypeAtLocation(node.initializer); - const varType = this.checker.getTypeFromTypeNode(node.type); - this.validateAssignmentExpression(initializerType, varType, node.initializer.pos); const value = this.transpileExpression(node.initializer); if (ts.isFunctionExpression(node.initializer) || ts.isArrowFunction(node.initializer)) { // Separate declaration and assignment for functions to allow recursion @@ -1662,21 +1667,13 @@ export abstract class LuaTranspiler { throw TSTLErrors.ForbiddenEllipsisDestruction(node); } - const initializerType = this.checker.getTypeAtLocation(node.initializer) as ts.TypeReference; - const tupleType = this.checker.getTypeFromTypeNode(node.type) as ts.TypeReference; - if (tupleType.typeArguments && initializerType.typeArguments) { - tupleType.typeArguments.forEach((t, i) => { - this.validateAssignmentExpression(initializerType.typeArguments[i], t, node.pos); - }); - } - const vars = node.name.elements.map(e => this.transpileArrayBindingElement(e)).join(","); // Don't unpack TupleReturn decorated functions if (tsHelper.isTupleReturnCall(node.initializer, this.checker)) { - return `local ${vars}=${this.transpileExpression(node.initializer)}`; // TOMB: todo + return `local ${vars}=${this.transpileExpression(node.initializer)}`; } else { - return `local ${vars}=${this.transpileDestructingAssignmentValue(node.initializer)}`; // TOMB: todo + return `local ${vars}=${this.transpileDestructingAssignmentValue(node.initializer)}`; } } else { throw TSTLErrors.UnsupportedKind("variable declaration", node.name.kind, node); From 422e6761a0833fd251b300cb517ab8f6ad34563c Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 21 Nov 2018 07:33:09 -0700 Subject: [PATCH 12/18] overloads with function and method signatures default to functions now --- src/TSHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index effc0d884..994a7dc13 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -290,6 +290,6 @@ export class TSHelper { } const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); const sigDecls = sigs.map(s => s.getDeclaration()); - return sigDecls.some(s => this.isDeclarationWithContext(s, checker)); + return sigDecls.every(s => this.isDeclarationWithContext(s, checker)); } } From 77313426668dd46f79a091745f4ed014284610c3 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 21 Nov 2018 07:49:41 -0700 Subject: [PATCH 13/18] preventing non-methods from being passed to bind/call/apply --- src/Transpiler.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 09d7064cc..549e197bf 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1398,6 +1398,12 @@ export abstract class LuaTranspiler { public transpileFunctionCallExpression(node: ts.CallExpression): string { const expression = node.expression as ts.PropertyAccessExpression; + const callerType = this.checker.getTypeAtLocation(expression.expression); + if (!tsHelper.isFunctionWithContext(callerType, this.checker)) { + const linePos = ts.getLineAndCharacterOfPosition(this.sourceFile, node.pos); + console.error(`${this.sourceFile.fileName}:${linePos.line + 1}:${linePos.character} ` + + `Cannot convert function to method`); + } const params = this.transpileArguments(node.arguments); const caller = this.transpileExpression(expression.expression); const expressionName = this.transpileIdentifier(expression.name); From eca6cc2a5f42f97f2a03e2316a21225c1960318e Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 21 Nov 2018 07:59:36 -0700 Subject: [PATCH 14/18] removed uneccessary helpers --- src/Decorator.ts | 1 - src/TSHelper.ts | 14 ++------------ src/Transpiler.ts | 5 ++--- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/Decorator.ts b/src/Decorator.ts index c73dff6b2..e0b3b6a00 100644 --- a/src/Decorator.ts +++ b/src/Decorator.ts @@ -21,5 +21,4 @@ export enum DecoratorKind { Phantom = "Phantom", TupleReturn = "TupleReturn", NoClassOr = "NoClassOr", - NoContext = "NoContext", } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 994a7dc13..c1c0bae25 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -79,11 +79,6 @@ export class TSHelper { return typeNode && this.isArrayTypeNode(typeNode); } - public static isCallableType(type: ts.Type, checker: ts.TypeChecker): boolean { - const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); - return sigs.length > 0; - } - public static isFunctionType(type: ts.Type, checker: ts.TypeChecker): boolean { const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); return typeNode && ts.isFunctionTypeNode(typeNode); @@ -254,11 +249,6 @@ export class TSHelper { return [false, null, null]; } - public static getFunctionSignature(declarations: ts.Declaration[]): ts.SignatureDeclaration { - return declarations - && declarations.find(d => (d as ts.FunctionLikeDeclaration).body !== undefined) as ts.SignatureDeclaration; - } - public static isDeclarationWithContext(sigDecl: ts.SignatureDeclaration, checker: ts.TypeChecker): boolean { const thisArg = sigDecl.parameters.find(p => ts.isIdentifier(p.name) && p.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); @@ -285,10 +275,10 @@ export class TSHelper { } public static isFunctionWithContext(type: ts.Type, checker: ts.TypeChecker): boolean { - if (!this.isCallableType(type, checker)) { + const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); + if (sigs.length === 0) { return false; } - const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); const sigDecls = sigs.map(s => s.getDeclaration()); return sigDecls.every(s => this.isDeclarationWithContext(s, checker)); } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 549e197bf..f268118d0 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1421,12 +1421,11 @@ export abstract class LuaTranspiler { public validateAssignment(fromType: ts.Type, toType: ts.Type, pos: number): void { if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) { + // Recurse into tuples/arrays (fromType as ts.TypeReference).typeArguments.forEach((t, i) => { - // Recurse into tuples this.validateAssignment(t, (toType as ts.TypeReference).typeArguments[i], pos); }); - - } else if (tsHelper.isCallableType(toType, this.checker) && tsHelper.isCallableType(fromType, this.checker)) { + } else { // Check function assignments const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker); const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker); From 8314a1edcb762a2a2a69d910d59f690898fa40f3 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 21 Nov 2018 08:30:08 -0700 Subject: [PATCH 15/18] using proper exceptions for function conversion errors --- src/Errors.ts | 6 ++++++ src/Transpiler.ts | 23 +++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Errors.ts b/src/Errors.ts index 9c0319904..37331ef36 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -68,4 +68,10 @@ export class TSTLErrors { public static UnsupportedObjectLiteralElement = (elementKind: ts.SyntaxKind, node: ts.Node) => new TranspileError(`Unsupported object literal element: ${elementKind}.`, node) + + public static UnsupportedFunctionConversion = (node: ts.Node) => + new TranspileError(`Unsupported conversion from method to function.`, node) + + public static UnsupportedMethodConversion = (node: ts.Node) => + new TranspileError(`Unsupported conversion from function to method.`, node) } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index f268118d0..2f57e7d13 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -945,7 +945,7 @@ export abstract class LuaTranspiler { // Validate assignment const rightType = this.checker.getTypeAtLocation(node.right); const leftType = this.checker.getTypeAtLocation(node.left); - this.validateAssignment(rightType, leftType, node.right.pos); + this.validateAssignment(node.right, rightType, leftType); if (ts.isArrayLiteralExpression(node.left)) { const vars = node.left.elements.map(e => this.transpileExpression(e)).join(","); @@ -1400,9 +1400,7 @@ export abstract class LuaTranspiler { const expression = node.expression as ts.PropertyAccessExpression; const callerType = this.checker.getTypeAtLocation(expression.expression); if (!tsHelper.isFunctionWithContext(callerType, this.checker)) { - const linePos = ts.getLineAndCharacterOfPosition(this.sourceFile, node.pos); - console.error(`${this.sourceFile.fileName}:${linePos.line + 1}:${linePos.character} ` - + `Cannot convert function to method`); + throw TSTLErrors.UnsupportedMethodConversion(node); } const params = this.transpileArguments(node.arguments); const caller = this.transpileExpression(expression.expression); @@ -1419,24 +1417,21 @@ export abstract class LuaTranspiler { } } - public validateAssignment(fromType: ts.Type, toType: ts.Type, pos: number): void { + public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type): void { if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) { // Recurse into tuples/arrays - (fromType as ts.TypeReference).typeArguments.forEach((t, i) => { - this.validateAssignment(t, (toType as ts.TypeReference).typeArguments[i], pos); + (toType as ts.TypeReference).typeArguments.forEach((t, i) => { + this.validateAssignment(node, (fromType as ts.TypeReference).typeArguments[i], t); }); } else { // Check function assignments const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker); const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker); if (fromHasContext !== toHasContext) { - const linePos = ts.getLineAndCharacterOfPosition(this.sourceFile, pos); if (fromHasContext) { - console.error(`${this.sourceFile.fileName}:${linePos.line + 1}:${linePos.character} ` - + `Cannot convert method to function`); + throw TSTLErrors.UnsupportedFunctionConversion(node); } else { - console.error(`${this.sourceFile.fileName}:${linePos.line + 1}:${linePos.character} ` - + `Cannot convert function to method`); + throw TSTLErrors.UnsupportedMethodConversion(node); } } } @@ -1456,7 +1451,7 @@ export abstract class LuaTranspiler { const param = params[i]; const paramType = this.checker.getTypeAtLocation(param); const sigType = this.checker.getTypeAtLocation(sig.parameters[i].valueDeclaration); - this.validateAssignment(paramType, sigType, param.pos); + this.validateAssignment(param, paramType, sigType); parameters.push(this.transpileExpression(param)); } } else { @@ -1647,7 +1642,7 @@ export abstract class LuaTranspiler { // Validate assignment const initializerType = this.checker.getTypeAtLocation(node.initializer); const varType = this.checker.getTypeFromTypeNode(node.type); - this.validateAssignment(initializerType, varType, node.initializer.pos); + this.validateAssignment(node.initializer, initializerType, varType); } if (ts.isIdentifier(node.name)) { From abde08f95b7062d21f191af7269adefc9b938f24 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 22 Nov 2018 06:52:56 -0700 Subject: [PATCH 16/18] removed context arg from custom constructors and added check for assigning to untyped vars --- src/Transpiler.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 2f57e7d13..86a7ad4e2 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1149,7 +1149,7 @@ export abstract class LuaTranspiler { public transpileNewExpression(node: ts.NewExpression): string { const name = this.transpileExpression(node.expression); const sig = this.checker.getResolvedSignature(node); - let params = node.arguments ? this.transpileArguments(node.arguments, sig, ts.createTrue()) : "true"; + const params = node.arguments ? this.transpileArguments(node.arguments, sig, ts.createTrue()) : "true"; const type = this.checker.getTypeAtLocation(node); const classDecorators = tsHelper.getCustomDecorators(type, this.checker); @@ -1164,15 +1164,7 @@ export abstract class LuaTranspiler { if (!customDecorator.args[0]) { throw TSTLErrors.InvalidDecoratorArgumentNumber("!CustomConstructor", 0, 1, node); } - if (!tsHelper.isFunctionWithContext(type, this.checker) - && !ts.isPropertyAccessExpression(node.expression) - && !ts.isElementAccessExpression(node.expression)) { - const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); - params = this.transpileArguments(node.arguments, null, context); - } else { - params = this.transpileArguments(node.arguments); - } - return `${customDecorator.args[0]}(${params})`; + return `${customDecorator.args[0]}(${this.transpileArguments(node.arguments)})`; } return `${name}.new(${params})`; @@ -1418,7 +1410,10 @@ export abstract class LuaTranspiler { } public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type): void { - if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) { + if ((toType.flags & ts.TypeFlags.Any) !== 0) { + // Assigning to un-typed variable + return; + } else if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) { // Recurse into tuples/arrays (toType as ts.TypeReference).typeArguments.forEach((t, i) => { this.validateAssignment(node, (fromType as ts.TypeReference).typeArguments[i], t); From 07887a527e171727f6808f558bc0e45536c3be30 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 22 Nov 2018 07:34:22 -0700 Subject: [PATCH 17/18] updated tests --- test/translation/lua/callNamespace.lua | 2 +- .../translation/lua/dotColonFunctionCalls.lua | 4 +- .../translation/lua/functionRestArguments.lua | 2 +- .../translation/lua/modulesFunctionExport.lua | 2 +- .../lua/modulesFunctionNoExport.lua | 2 +- ...modulesNamespaceNestedWithMemberExport.lua | 2 +- .../lua/modulesNamespaceWithMemberExport.lua | 2 +- .../modulesNamespaceWithMemberNoExport.lua | 2 +- test/translation/lua/namespace.lua | 2 +- test/translation/lua/namespaceMerge.lua | 12 +++--- test/translation/lua/namespaceNested.lua | 2 +- test/translation/lua/namespacePhantom.lua | 2 +- test/translation/lua/returnDefault.lua | 2 +- .../lua/shorthandPropertyAssignment.lua | 2 +- test/translation/lua/tupleReturn.lua | 40 +++++++++---------- test/unit/assignmentDestructuring.spec.ts | 4 +- test/unit/assignments.spec.ts | 6 +-- test/unit/curry.spec.ts | 4 +- test/unit/functions.spec.ts | 6 +-- test/unit/objectLiteral.spec.ts | 2 +- 20 files changed, 51 insertions(+), 51 deletions(-) diff --git a/test/translation/lua/callNamespace.lua b/test/translation/lua/callNamespace.lua index 40b907163..b0ec0d650 100644 --- a/test/translation/lua/callNamespace.lua +++ b/test/translation/lua/callNamespace.lua @@ -1 +1 @@ -Namespace:myFunction(); +Namespace.myFunction(); diff --git a/test/translation/lua/dotColonFunctionCalls.lua b/test/translation/lua/dotColonFunctionCalls.lua index 5deaffabb..554ca6709 100644 --- a/test/translation/lua/dotColonFunctionCalls.lua +++ b/test/translation/lua/dotColonFunctionCalls.lua @@ -2,5 +2,5 @@ classInstance:colonMethod(); classInstance:dotMethod(); interfaceInstance:colonMethod(); interfaceInstance:dotMethod(); -TestNameSpace:dotMethod(); -TestNameSpace:dotMethod2(); +TestNameSpace.dotMethod(); +TestNameSpace.dotMethod2(); diff --git a/test/translation/lua/functionRestArguments.lua b/test/translation/lua/functionRestArguments.lua index f2db4825d..78d60a18e 100644 --- a/test/translation/lua/functionRestArguments.lua +++ b/test/translation/lua/functionRestArguments.lua @@ -1,3 +1,3 @@ -function varargsFunction(self,a,...) +function varargsFunction(a,...) local b = { ... } end diff --git a/test/translation/lua/modulesFunctionExport.lua b/test/translation/lua/modulesFunctionExport.lua index 6fce9cb9e..5b447e5b2 100644 --- a/test/translation/lua/modulesFunctionExport.lua +++ b/test/translation/lua/modulesFunctionExport.lua @@ -1,5 +1,5 @@ local exports = exports or {} -local function publicFunc(self) +local function publicFunc() end exports.publicFunc = publicFunc return exports diff --git a/test/translation/lua/modulesFunctionNoExport.lua b/test/translation/lua/modulesFunctionNoExport.lua index 855900df8..e0e619215 100644 --- a/test/translation/lua/modulesFunctionNoExport.lua +++ b/test/translation/lua/modulesFunctionNoExport.lua @@ -1,2 +1,2 @@ -function publicFunc(self) +function publicFunc() end diff --git a/test/translation/lua/modulesNamespaceNestedWithMemberExport.lua b/test/translation/lua/modulesNamespaceNestedWithMemberExport.lua index 1c9884b12..326a00294 100644 --- a/test/translation/lua/modulesNamespaceNestedWithMemberExport.lua +++ b/test/translation/lua/modulesNamespaceNestedWithMemberExport.lua @@ -3,7 +3,7 @@ local TestSpace = exports.TestSpace or TestSpace or {} do local TestNestedSpace = TestNestedSpace or {} do - local function innerFunc(self) + local function innerFunc() end TestNestedSpace.innerFunc = innerFunc end diff --git a/test/translation/lua/modulesNamespaceWithMemberExport.lua b/test/translation/lua/modulesNamespaceWithMemberExport.lua index fe2a50e3c..7e9204160 100644 --- a/test/translation/lua/modulesNamespaceWithMemberExport.lua +++ b/test/translation/lua/modulesNamespaceWithMemberExport.lua @@ -1,7 +1,7 @@ local exports = exports or {} local TestSpace = exports.TestSpace or TestSpace or {} do - local function innerFunc(self) + local function innerFunc() end TestSpace.innerFunc = innerFunc end diff --git a/test/translation/lua/modulesNamespaceWithMemberNoExport.lua b/test/translation/lua/modulesNamespaceWithMemberNoExport.lua index 752f8edfe..a2afe84fe 100644 --- a/test/translation/lua/modulesNamespaceWithMemberNoExport.lua +++ b/test/translation/lua/modulesNamespaceWithMemberNoExport.lua @@ -1,7 +1,7 @@ local exports = exports or {} local TestSpace = exports.TestSpace or TestSpace or {} do - local function innerFunc(self) + local function innerFunc() end end exports.TestSpace = TestSpace diff --git a/test/translation/lua/namespace.lua b/test/translation/lua/namespace.lua index dd63c6b89..c5a266c7f 100644 --- a/test/translation/lua/namespace.lua +++ b/test/translation/lua/namespace.lua @@ -1,5 +1,5 @@ myNamespace = myNamespace or {} do - local function nsMember(self) + local function nsMember() end end diff --git a/test/translation/lua/namespaceMerge.lua b/test/translation/lua/namespaceMerge.lua index cb5157727..3259d6488 100644 --- a/test/translation/lua/namespaceMerge.lua +++ b/test/translation/lua/namespaceMerge.lua @@ -9,10 +9,10 @@ end end function MergedClass.constructor(self) end -function MergedClass.staticMethodA(self) +function MergedClass.staticMethodA() end -function MergedClass.staticMethodB(self) - self:staticMethodA(); +function MergedClass.staticMethodB() + self.staticMethodA(); end function MergedClass.methodA(self) end @@ -22,12 +22,12 @@ function MergedClass.methodB(self) end MergedClass = MergedClass or {} do - local function namespaceFunc(self) + local function namespaceFunc() end MergedClass.namespaceFunc = namespaceFunc end local mergedClass = MergedClass.new(true); mergedClass:methodB(); mergedClass:propertyFunc(); -MergedClass:staticMethodB(); -MergedClass:namespaceFunc(); +MergedClass.staticMethodB(); +MergedClass.namespaceFunc(); diff --git a/test/translation/lua/namespaceNested.lua b/test/translation/lua/namespaceNested.lua index a8ec92f30..bff87b32e 100644 --- a/test/translation/lua/namespaceNested.lua +++ b/test/translation/lua/namespaceNested.lua @@ -2,7 +2,7 @@ myNamespace = myNamespace or {} do local myNestedNamespace = myNestedNamespace or {} do - local function nsMember(self) + local function nsMember() end end end diff --git a/test/translation/lua/namespacePhantom.lua b/test/translation/lua/namespacePhantom.lua index 2f3381780..b74a81173 100644 --- a/test/translation/lua/namespacePhantom.lua +++ b/test/translation/lua/namespacePhantom.lua @@ -1,2 +1,2 @@ -function nsMember(self) +function nsMember() end diff --git a/test/translation/lua/returnDefault.lua b/test/translation/lua/returnDefault.lua index 436b8ed62..52c2123e3 100644 --- a/test/translation/lua/returnDefault.lua +++ b/test/translation/lua/returnDefault.lua @@ -1,3 +1,3 @@ -function myFunc(self) +function myFunc() return end diff --git a/test/translation/lua/shorthandPropertyAssignment.lua b/test/translation/lua/shorthandPropertyAssignment.lua index 3514556fe..5caf484a0 100644 --- a/test/translation/lua/shorthandPropertyAssignment.lua +++ b/test/translation/lua/shorthandPropertyAssignment.lua @@ -1,3 +1,3 @@ -local f; f = function(_,x) +local f; f = function(x) return ({x = x}) end; diff --git a/test/translation/lua/tupleReturn.lua b/test/translation/lua/tupleReturn.lua index 30816b99a..7d33868fc 100644 --- a/test/translation/lua/tupleReturn.lua +++ b/test/translation/lua/tupleReturn.lua @@ -1,28 +1,28 @@ -function tupleReturn(self) +function tupleReturn() return 0,"foobar" end -tupleReturn(_G); -noTupleReturn(_G); -local a,b=tupleReturn(_G); -local c,d=table.unpack(noTupleReturn(_G)); -do local __TS_tmp0,__TS_tmp1 = tupleReturn(_G); a,b = __TS_tmp0,__TS_tmp1 end; -do local __TS_tmp0,__TS_tmp1 = table.unpack(noTupleReturn(_G)); c,d = __TS_tmp0,__TS_tmp1 end; -local e = ({ tupleReturn(_G) }); -local f = noTupleReturn(_G); -e = ({ tupleReturn(_G) }); -f = noTupleReturn(_G); -foo(_G,({ tupleReturn(_G) })); -foo(_G,noTupleReturn(_G)); -function tupleReturnFromVar(self) +tupleReturn(); +noTupleReturn(); +local a,b=tupleReturn(); +local c,d=table.unpack(noTupleReturn()); +do local __TS_tmp0,__TS_tmp1 = tupleReturn(); a,b = __TS_tmp0,__TS_tmp1 end; +do local __TS_tmp0,__TS_tmp1 = table.unpack(noTupleReturn()); c,d = __TS_tmp0,__TS_tmp1 end; +local e = ({ tupleReturn() }); +local f = noTupleReturn(); +e = ({ tupleReturn() }); +f = noTupleReturn(); +foo(({ tupleReturn() })); +foo(noTupleReturn()); +function tupleReturnFromVar() local r = {1,"baz"}; return table.unpack(r) end -function tupleReturnForward(self) - return tupleReturn(_G) +function tupleReturnForward() + return tupleReturn() end -function tupleNoForward(self) - return ({ tupleReturn(_G) }) +function tupleNoForward() + return ({ tupleReturn() }) end -function tupleReturnUnpack(self) - return table.unpack(tupleNoForward(_G)) +function tupleReturnUnpack() + return table.unpack(tupleNoForward()) end diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index 7c7241d8e..650e13024 100644 --- a/test/unit/assignmentDestructuring.spec.ts +++ b/test/unit/assignmentDestructuring.spec.ts @@ -15,7 +15,7 @@ export class AssignmentDestructuringTests { this.assignmentDestruturingTs, {luaTarget: LuaTarget.Lua51, luaLibImport: "none"} ); // Assert - Expect(lua).toBe(`local a,b=unpack(myFunc(_G));`); + Expect(lua).toBe(`local a,b=unpack(myFunc());`); } @Test("Assignment destructuring [5.2]") @@ -25,6 +25,6 @@ export class AssignmentDestructuringTests { this.assignmentDestruturingTs, {luaTarget: LuaTarget.Lua52, luaLibImport: "none"} ); // Assert - Expect(lua).toBe(`local a,b=table.unpack(myFunc(_G));`); + Expect(lua).toBe(`local a,b=table.unpack(myFunc());`); } } diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index 5f41581ba..e3ccb60c0 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -81,7 +81,7 @@ export class AssignmentTests { + `let [a,b] = abc();`; const lua = util.transpileString(code); - Expect(lua).toBe("local a,b=abc(_G);"); + Expect(lua).toBe("local a,b=abc();"); } @Test("TupleReturn Single assignment") @@ -92,7 +92,7 @@ export class AssignmentTests { + `a = abc();`; const lua = util.transpileString(code); - Expect(lua).toBe("local a = ({ abc(_G) });\na = ({ abc(_G) });"); + Expect(lua).toBe("local a = ({ abc() });\na = ({ abc() });"); } @Test("TupleReturn interface assignment") @@ -116,7 +116,7 @@ export class AssignmentTests { + `let [a,b] = def.abc();`; const lua = util.transpileString(code); - Expect(lua).toBe("local a,b=def:abc();"); + Expect(lua).toBe("local a,b=def.abc();"); } @Test("TupleReturn method assignment") diff --git a/test/unit/curry.spec.ts b/test/unit/curry.spec.ts index e73a7a813..06b27f057 100644 --- a/test/unit/curry.spec.ts +++ b/test/unit/curry.spec.ts @@ -10,8 +10,8 @@ export class LuaCurryTests { `(x: number) => (y: number) => x + y;` ); // Assert - Expect(lua).toBe(`function(_,x) - return function(_,y) + Expect(lua).toBe(`function(x) + return function(y) return x+y end end;`); diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 4e5748824..cf0da8287 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -233,7 +233,7 @@ export class FunctionTests { @Test("Function bind") public functionBind(): void { - const source = `const abc = function (a: string, b: string) { return this.a + a + b; } + const source = `const abc = function (this: { a: number }, a: string, b: string) { return this.a + a + b; } return abc.bind({ a: 4 }, "b")("c");`; const result = util.transpileAndExecute(source); @@ -243,7 +243,7 @@ export class FunctionTests { @Test("Function apply") public functionApply(): void { - const source = `const abc = function (a: string) { return this.a + a; } + const source = `const abc = function (this: { a: number }, a: string) { return this.a + a; } return abc.apply({ a: 4 }, ["b"]);`; const result = util.transpileAndExecute(source); @@ -253,7 +253,7 @@ export class FunctionTests { @Test("Function call") public functionCall(): void { - const source = `const abc = function (a: string) { return this.a + a; } + const source = `const abc = function (this: { a: number }, a: string) { return this.a + a; } return abc.call({ a: 4 }, "b");`; const result = util.transpileAndExecute(source); diff --git a/test/unit/objectLiteral.spec.ts b/test/unit/objectLiteral.spec.ts index 55d488741..68fa4049d 100644 --- a/test/unit/objectLiteral.spec.ts +++ b/test/unit/objectLiteral.spec.ts @@ -9,7 +9,7 @@ export class ObjectLiteralTests { @TestCase(`{"a":3,b:"4"}`, `{["a"] = 3,b = "4"};`) @TestCase(`{["a"]:3,b:"4"}`, `{["a"] = 3,b = "4"};`) @TestCase(`{["a"+123]:3,b:"4"}`, `{["a" .. 123] = 3,b = "4"};`) - @TestCase(`{[myFunc()]:3,b:"4"}`, `{[myFunc(_G)] = 3,b = "4"};`) + @TestCase(`{[myFunc()]:3,b:"4"}`, `{[myFunc()] = 3,b = "4"};`) @TestCase(`{x}`, `{x = x};`) @Test("Object Literal") public objectLiteral(inp: string, out: string) { From 717008040a95ac54265303919faf412234d4d5f9 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 23 Nov 2018 14:44:12 -0700 Subject: [PATCH 18/18] removing leftover NoContext decorators --- src/lualib/ArrayConcat.ts | 3 --- src/lualib/ArrayEvery.ts | 1 - src/lualib/ArrayFilter.ts | 1 - src/lualib/ArrayForEach.ts | 1 - src/lualib/ArrayIndexOf.ts | 1 - src/lualib/ArrayMap.ts | 1 - src/lualib/ArrayPush.ts | 1 - src/lualib/ArrayReverse.ts | 1 - src/lualib/ArrayShift.ts | 2 -- src/lualib/ArraySlice.ts | 1 - src/lualib/ArraySome.ts | 1 - src/lualib/ArraySort.ts | 2 -- src/lualib/ArraySplice.ts | 1 - src/lualib/ArrayUnshift.ts | 2 -- src/lualib/FunctionApply.ts | 4 ---- src/lualib/FunctionBind.ts | 5 ----- src/lualib/FunctionCall.ts | 4 ---- src/lualib/InstanceOf.ts | 1 - src/lualib/StringReplace.ts | 2 -- src/lualib/StringSplit.ts | 1 - src/lualib/Ternary.ts | 1 - test/src/util.ts | 6 ++---- test/translation/ts/assignments.ts | 5 ----- 23 files changed, 2 insertions(+), 46 deletions(-) diff --git a/src/lualib/ArrayConcat.ts b/src/lualib/ArrayConcat.ts index 145673ab1..06d5206b3 100644 --- a/src/lualib/ArrayConcat.ts +++ b/src/lualib/ArrayConcat.ts @@ -1,9 +1,6 @@ -/** !NoContext */ declare function pcall(func: () => any): any; -/** !NoContext */ declare function type(val: any): string; -/** !NoContext */ function __TS__ArrayConcat(arr1: any[], ...args: any[]): any[] { const out: any[] = []; for (const val of arr1) { diff --git a/src/lualib/ArrayEvery.ts b/src/lualib/ArrayEvery.ts index 7774eb911..e8434175a 100644 --- a/src/lualib/ArrayEvery.ts +++ b/src/lualib/ArrayEvery.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArrayEvery(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): boolean { for (let i = 0; i < arr.length; i++) { if (!callbackfn(arr[i], i, arr)) { diff --git a/src/lualib/ArrayFilter.ts b/src/lualib/ArrayFilter.ts index d0495c399..8f0ac2fde 100644 --- a/src/lualib/ArrayFilter.ts +++ b/src/lualib/ArrayFilter.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArrayFilter(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): T[] { const result: T[] = []; for (let i = 0; i < arr.length; i++) { diff --git a/src/lualib/ArrayForEach.ts b/src/lualib/ArrayForEach.ts index 28cd54b8b..3001915f0 100644 --- a/src/lualib/ArrayForEach.ts +++ b/src/lualib/ArrayForEach.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArrayForEach(arr: T[], callbackFn: (value: T, index?: number, array?: any[]) => any): void { for (let i = 0; i < arr.length; i++) { callbackFn(arr[i], i, arr); diff --git a/src/lualib/ArrayIndexOf.ts b/src/lualib/ArrayIndexOf.ts index fd2e19da5..c50ce5ded 100644 --- a/src/lualib/ArrayIndexOf.ts +++ b/src/lualib/ArrayIndexOf.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArrayIndexOf(arr: T[], searchElement: T, fromIndex?: number): number { const len = arr.length; if (len === 0) { diff --git a/src/lualib/ArrayMap.ts b/src/lualib/ArrayMap.ts index dd595b329..f7ea7aa50 100644 --- a/src/lualib/ArrayMap.ts +++ b/src/lualib/ArrayMap.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArrayMap(arr: T[], callbackfn: (value: T, index?: number, array?: T[]) => U): U[] { const newArray: U[] = []; for (let i = 0; i < arr.length; i++) { diff --git a/src/lualib/ArrayPush.ts b/src/lualib/ArrayPush.ts index 104663375..8e1d8e324 100644 --- a/src/lualib/ArrayPush.ts +++ b/src/lualib/ArrayPush.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArrayPush(arr: T[], ...items: T[]): number { for (const item of items) { arr[arr.length] = item; diff --git a/src/lualib/ArrayReverse.ts b/src/lualib/ArrayReverse.ts index 65349a905..3c4839417 100644 --- a/src/lualib/ArrayReverse.ts +++ b/src/lualib/ArrayReverse.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArrayReverse(arr: any[]): any[] { let i = 0; let j = arr.length - 1; diff --git a/src/lualib/ArrayShift.ts b/src/lualib/ArrayShift.ts index 11a5b9a89..a95df1a49 100644 --- a/src/lualib/ArrayShift.ts +++ b/src/lualib/ArrayShift.ts @@ -1,8 +1,6 @@ declare namespace table { - /** !NoContext */ function remove(arr: T[], idx: number): T; } -/** !NoContext */ function __TS__ArrayShift(arr: T[]): T { return table.remove(arr, 1); } diff --git a/src/lualib/ArraySlice.ts b/src/lualib/ArraySlice.ts index bfb0d021c..f3eb5b3d6 100644 --- a/src/lualib/ArraySlice.ts +++ b/src/lualib/ArraySlice.ts @@ -1,5 +1,4 @@ // https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf 22.1.3.23 -/** !NoContext */ function __TS__ArraySlice(list: T[], first: number, last: number): T[] { const len = list.length; diff --git a/src/lualib/ArraySome.ts b/src/lualib/ArraySome.ts index ad2817eac..d03e7a9fe 100644 --- a/src/lualib/ArraySome.ts +++ b/src/lualib/ArraySome.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArraySome(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): boolean { for (let i = 0; i < arr.length; i++) { if (callbackfn(arr[i], i, arr)) { diff --git a/src/lualib/ArraySort.ts b/src/lualib/ArraySort.ts index 2e02d2572..18745695c 100644 --- a/src/lualib/ArraySort.ts +++ b/src/lualib/ArraySort.ts @@ -1,8 +1,6 @@ declare namespace table { - /** !NoContext */ function sort(arr: T[], compareFn?: (a: T, b: T) => number): void; } -/** !NoContext */ function __TS__ArraySort(arr: T[], compareFn?: (a: T, b: T) => number): T[] { table.sort(arr, compareFn); return arr; diff --git a/src/lualib/ArraySplice.ts b/src/lualib/ArraySplice.ts index 2c20c98e0..b875d706c 100644 --- a/src/lualib/ArraySplice.ts +++ b/src/lualib/ArraySplice.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__ArraySplice(list: T[], start: number, deleteCount: number, ...items: T[]): T[] { const len = list.length; diff --git a/src/lualib/ArrayUnshift.ts b/src/lualib/ArrayUnshift.ts index 3c5508b02..cb0031100 100644 --- a/src/lualib/ArrayUnshift.ts +++ b/src/lualib/ArrayUnshift.ts @@ -1,8 +1,6 @@ declare namespace table { - /** !NoContext */ function insert(arr: T[], idx: number, val: T): void; } -/** !NoContext */ function __TS__ArrayUnshift(arr: T[], ...items: T[]): number { for (let i = items.length - 1; i >= 0; --i) { table.insert(arr, 1, items[i]); diff --git a/src/lualib/FunctionApply.ts b/src/lualib/FunctionApply.ts index c58691959..6776f0e02 100644 --- a/src/lualib/FunctionApply.ts +++ b/src/lualib/FunctionApply.ts @@ -1,15 +1,11 @@ -/** !NoContext */ declare function unpack(list: T[], i?: number, j?: number): T[]; declare namespace table { - /** !NoContext */ export function unpack(list: T[], i?: number, j?: number): T[]; } -/** !NoContext */ type ApplyFn = (...argArray: any[]) => any; -/** !NoContext */ function __TS__FunctionApply(fn: ApplyFn, thisArg: any, argsArray?: any[]): any { if (argsArray) { return fn(thisArg, (unpack || table.unpack)(argsArray)); diff --git a/src/lualib/FunctionBind.ts b/src/lualib/FunctionBind.ts index fad7c70ae..e1e7675b5 100644 --- a/src/lualib/FunctionBind.ts +++ b/src/lualib/FunctionBind.ts @@ -1,18 +1,13 @@ -/** !NoContext */ declare function unpack(list: T[], i?: number, j?: number): T[]; declare namespace table { - /** !NoContext */ export function insert(t: T[], pos: number, value: T): void; - /** !NoContext */ export function unpack(list: T[], i?: number, j?: number): T[]; } -/** !NoContext */ type BindFn = (...argArray: any[]) => any; -/** !NoContext */ function __TS__FunctionBind(fn: BindFn, thisArg: any, ...boundArgs: any[]): (...args: any[]) => any { return (...argArray: any[]) => { for (let i = 0; i < boundArgs.length; ++i) { diff --git a/src/lualib/FunctionCall.ts b/src/lualib/FunctionCall.ts index 5f7acd3d7..fe126ccd5 100644 --- a/src/lualib/FunctionCall.ts +++ b/src/lualib/FunctionCall.ts @@ -1,15 +1,11 @@ -/** !NoContext */ declare function unpack(list: T[], i?: number, j?: number): T[]; declare namespace table { - /** !NoContext */ export function unpack(list: T[], i?: number, j?: number): T[]; } -/** !NoContext */ type CallFn = (...argArray: any[]) => any; -/** !NoContext */ function __TS__FunctionCall(fn: CallFn, thisArg: any, ...args: any[]): any { return fn(thisArg, (unpack || table.unpack)(args)); } diff --git a/src/lualib/InstanceOf.ts b/src/lualib/InstanceOf.ts index eb013aaaf..3b6a12dab 100644 --- a/src/lualib/InstanceOf.ts +++ b/src/lualib/InstanceOf.ts @@ -3,7 +3,6 @@ interface LuaClass { __base: LuaClass; } -/** !NoContext */ function __TS__InstanceOf(obj: LuaClass, classTbl: LuaClass): boolean { while (obj !== undefined) { if (obj.__index === classTbl) { diff --git a/src/lualib/StringReplace.ts b/src/lualib/StringReplace.ts index f5b6fd4d3..7a5df979c 100644 --- a/src/lualib/StringReplace.ts +++ b/src/lualib/StringReplace.ts @@ -1,10 +1,8 @@ declare namespace string { - /** !NoContext */ /** !TupleReturn */ function gsub(source: string, searchValue: string, replaceValue: string): [string, number]; } -/** !NoContext */ function __TS__StringReplace(source: string, searchValue: string, replaceValue: string): string { return string.gsub(source, searchValue, replaceValue)[0]; } diff --git a/src/lualib/StringSplit.ts b/src/lualib/StringSplit.ts index 862e71ddc..005f57436 100644 --- a/src/lualib/StringSplit.ts +++ b/src/lualib/StringSplit.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__StringSplit(source: string, separator?: string, limit?: number): string[] { if (limit === undefined) { limit = 4294967295; diff --git a/src/lualib/Ternary.ts b/src/lualib/Ternary.ts index 4e2201d7d..f1331f4f4 100644 --- a/src/lualib/Ternary.ts +++ b/src/lualib/Ternary.ts @@ -1,4 +1,3 @@ -/** !NoContext */ function __TS__Ternary(condition: boolean, cb1: () => T, cb2: () => T): T { if (condition) { return cb1(); diff --git a/test/src/util.ts b/test/src/util.ts index c4a0f8872..490323d3e 100644 --- a/test/src/util.ts +++ b/test/src/util.ts @@ -3,7 +3,7 @@ import * as ts from "typescript"; import { Expect } from "alsatian"; -import { transpileString as _transpileString } from "../../src/Compiler"; +import { transpileString } from "../../src/Compiler"; import { CompilerOptions } from "../../src/CompilerOptions"; import { LuaTarget, LuaTranspiler } from "../../src/Transpiler"; import { createTranspiler } from "../../src/TranspilerFactory"; @@ -12,9 +12,7 @@ import {lauxlib, lua, lualib, to_jsstring, to_luastring } from "fengari"; import * as fs from "fs"; -export function transpileString(str: string, options?: CompilerOptions): string { - return _transpileString("/** !NoContext */ declare function JSONStringify(t: any): string;\n" + str, options); -} +export { transpileString }; export function executeLua(luaStr: string, withLib = true): any { if (withLib) { diff --git a/test/translation/ts/assignments.ts b/test/translation/ts/assignments.ts index e124a3019..a8e51e59a 100644 --- a/test/translation/ts/assignments.ts +++ b/test/translation/ts/assignments.ts @@ -2,19 +2,14 @@ declare let x: number; declare let y: number; declare let z: number; declare let obj: {prop: number, arr: number[]}; -/** !NoContext */ declare function getObj(): typeof obj; declare let arr: number[]; declare let arr2: number[][]; -/** !NoContext */ declare function getArr(): typeof arr; -/** !NoContext */ declare function getIndex(): number; declare let xTup: [number, number]; declare let yTup: [number, number]; -/** !NoContext */ declare function getTup(): [number, number]; -/** !NoContext */ /** !TupleReturn */ declare function getTupRet(): [number, number]; x = y;