From 7f25ee5ca0bfa99e62318798df8b65cc07f2de28 Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Fri, 19 Oct 2018 06:00:13 -0600 Subject: [PATCH 01/59] converting 'else if' statements to lua 'elseif' instead of nested ifs (#254) --- src/Transpiler.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index bfbd58a6e..bb884134b 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -458,10 +458,20 @@ export abstract class LuaTranspiler { result += this.transpileStatement(node.thenStatement); this.popIndent(); - if (node.elseStatement) { + let elseStatement = node.elseStatement; + while (elseStatement && ts.isIfStatement(elseStatement)) { + const elseIfCondition = this.transpileExpression(elseStatement.expression); + result += this.indent + `elseif ${elseIfCondition} then\n`; + this.pushIndent(); + result += this.transpileStatement(elseStatement.thenStatement); + this.popIndent(); + elseStatement = elseStatement.elseStatement; + } + + if (elseStatement) { result += this.indent + "else\n"; this.pushIndent(); - result += this.transpileStatement(node.elseStatement); + result += this.transpileStatement(elseStatement); this.popIndent(); } From 9399f10c8f07e885bbc700cf3e755f421bb54d81 Mon Sep 17 00:00:00 2001 From: Kirill Artemov Date: Fri, 19 Oct 2018 15:00:26 +0300 Subject: [PATCH 02/59] Const enum support (#253) * Const enum support * Changed string concatenation to interpolation --- src/Transpiler.ts | 95 ++++++++++++++++++++++++++++++------------ test/unit/enum.spec.ts | 57 +++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 26 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index bb884134b..958b9ad71 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -184,6 +184,34 @@ export abstract class LuaTranspiler { return filePath.replace(new RegExp("\\\\|\/", "g"), "."); } + public computeEnumMembers(node: ts.EnumDeclaration): Array<{ name: string, value: string | number }> { + let val: number | string = 0; + let hasStringInitializers = false; + + return node.members.map(member => { + if (member.initializer) { + if (ts.isNumericLiteral(member.initializer)) { + val = parseInt(member.initializer.text); + } else if (ts.isStringLiteral(member.initializer)) { + hasStringInitializers = true; + val = `"${member.initializer.text}"`; + } else { + throw TSTLErrors.InvalidEnumMember(member.initializer); + } + } else if (hasStringInitializers) { + throw TSTLErrors.HeterogeneousEnum(node); + } + + const enumMember = { name: this.transpileIdentifier(member.name as ts.Identifier), value: val }; + + if (typeof val === "number") { + val++; + } + + return enumMember; + }); + } + // Transpile a source file public transpileSourceFile(): string { let header = ""; @@ -393,48 +421,36 @@ export abstract class LuaTranspiler { } public transpileEnum(node: ts.EnumDeclaration): string { - let val: number | string = 0; - let result = ""; - const type = this.checker.getTypeAtLocation(node); + + // Const enums should never appear in the resulting code + if (type.symbol.getFlags() & ts.SymbolFlags.ConstEnum) { + return ""; + } + const membersOnly = tsHelper.getCustomDecorators(type, this.checker) .has(DecoratorKind.CompileMembersOnly); + let result = ""; + if (!membersOnly) { const name = this.transpileIdentifier(node.name); result += this.indent + this.accessPrefix(node) + `${name}={}\n`; this.pushExport(name, node); } - let hasStringInitializers = false; - node.members.forEach(member => { - if (member.initializer) { - if (ts.isNumericLiteral(member.initializer)) { - val = parseInt(member.initializer.text); - } else if (ts.isStringLiteral(member.initializer)) { - hasStringInitializers = true; - val = `"${member.initializer.text}"`; - } else { - throw TSTLErrors.InvalidEnumMember(member.initializer); - } - } else if (hasStringInitializers) { - throw TSTLErrors.HeterogeneousEnum(node); - } - + this.computeEnumMembers(node).forEach(enumMember => { if (membersOnly) { - const defName = this.definitionName(this.transpileIdentifier(member.name as ts.Identifier)); - result += this.indent + `${defName}=${val}\n`; + const defName = this.definitionName(enumMember.name); + result += this.indent + `${defName}=${enumMember.value}\n`; } else { const defName = this.definitionName( - `${this.transpileIdentifier(node.name)}.${this.transpileIdentifier((member.name as ts.Identifier))}` + `${this.transpileIdentifier(node.name)}.${enumMember.name}` ); - result += this.indent + `${defName}=${val}\n`; - } - - if (typeof val === "number") { - val++; + result += this.indent + `${defName}=${enumMember.value}\n`; } }); + return result; } @@ -1408,6 +1424,33 @@ export abstract class LuaTranspiler { } } + if (type.symbol && (type.symbol.flags & ts.SymbolFlags.ConstEnum)) { + const propertyValueDeclaration = this.checker.getTypeAtLocation(node).symbol.valueDeclaration; + + if (propertyValueDeclaration && propertyValueDeclaration.kind === ts.SyntaxKind.EnumMember) { + const enumMember = propertyValueDeclaration as ts.EnumMember; + + if (enumMember.initializer) { + return this.transpileExpression(enumMember.initializer); + } else { + const enumMembers = this.computeEnumMembers(enumMember.parent); + const memberPosition = enumMember.parent.members.indexOf(enumMember); + + if (memberPosition === -1) { + throw TSTLErrors.UnsupportedProperty(type.symbol.name, property, node); + } + + const value = enumMembers[memberPosition].value; + + if (typeof value === "string") { + return `"${value}"`; + } + + return value.toString(); + } + } + } + this.checkForLuaLibType(type); const decorators = tsHelper.getCustomDecorators(type, this.checker); diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index 1ece6b740..ee7e5548e 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -4,6 +4,63 @@ import * as util from "../src/util"; import { TranspileError } from "../../src/Errors"; export class EnumTests { + @Test("Declare const enum") + public declareConstEnum(): void { + const testCode = ` + declare const enum TestEnum { + MEMBER_ONE = "test", + MEMBER_TWO = "test2" + } + + const valueOne = TestEnum.MEMBER_ONE; + `; + + Expect(util.transpileString(testCode)).toBe(`local valueOne = "test";`); + } + + @Test("Const enum") + public constEnum(): void { + const testCode = ` + const enum TestEnum { + MEMBER_ONE = "test", + MEMBER_TWO = "test2" + } + + const valueOne = TestEnum.MEMBER_ONE; + `; + + Expect(util.transpileString(testCode)).toBe(`local valueOne = "test";`); + } + + @Test("Const enum without initializer") + public constEnumNoInitializer(): void { + const testCode = ` + const enum TestEnum { + MEMBER_ONE, + MEMBER_TWO + } + + const valueOne = TestEnum.MEMBER_ONE; + `; + + Expect(util.transpileString(testCode)).toBe(`local valueOne = 0;`); + } + + @Test("Const enum without initializer in some values") + public constEnumNoInitializerInSomeValues(): void { + const testCode = ` + const enum TestEnum { + MEMBER_ONE = 3, + MEMBER_TWO, + MEMBER_THREE = 5 + } + + const valueOne = TestEnum.MEMBER_TWO; + `; + + Expect(util.transpileString(testCode)).toBe(`local valueOne = 4;`); + } + @Test("Invalid heterogeneous enum") public invalidHeterogeneousEnum(): void { // Transpile & Assert From 34d3f4ad376861e9b60d9117e63f3d0c947dfbb0 Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Fri, 19 Oct 2018 14:27:43 -0600 Subject: [PATCH 03/59] refactored try block to allow rethrow (#251) * refactored try block to allow rethrow * updated rethrow test with something less convoluted --- src/Transpiler.ts | 40 ++++++++++++++----- test/translation/lua/tryCatch.lua | 14 ++++--- test/translation/lua/tryCatchFinally.lua | 18 +++++---- test/translation/lua/tryFinally.lua | 14 ++++--- test/unit/error.spec.ts | 51 ++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 29 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 958b9ad71..6bcae227e 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -687,24 +687,44 @@ export abstract class LuaTranspiler { } public transpileTry(node: ts.TryStatement): string { - let tryFunc = "function()\n"; + let result = this.indent + "do\n"; + this.pushIndent(); + + result += this.indent; + if (node.catchClause) { + result += "local __TS_try"; + if (node.catchClause.variableDeclaration) { + const variableName = this.transpileIdentifier( + node.catchClause.variableDeclaration.name as ts.Identifier); + result += ", " + variableName; + } + result += " = "; + } + + result += "pcall(function()\n"; this.pushIndent(); - tryFunc += this.transpileBlock(node.tryBlock); + result += this.transpileBlock(node.tryBlock); this.popIndent(); - tryFunc += "end"; - let catchFunc = "function(e)\nend"; - if (node.catchClause && node.catchClause.variableDeclaration) { - const variableName = this.transpileIdentifier(node.catchClause.variableDeclaration.name as ts.Identifier); - catchFunc = this.indent + `function(${variableName})\n`; + result += this.indent + "end);\n"; + + if (node.catchClause) { + result += this.indent + `if not __TS_try then\n`; this.pushIndent(); - catchFunc += this.transpileBlock(node.catchClause.block); + result += this.transpileBlock(node.catchClause.block); this.popIndent(); - catchFunc += "end"; + result += this.indent + "end\n"; } - let result = this.indent + `xpcall(${tryFunc},\n${catchFunc})\n`; + if (node.finallyBlock) { + result += this.indent + "do\n"; + this.pushIndent(); result += this.transpileBlock(node.finallyBlock); + this.popIndent(); + result += this.indent + "end\n"; } + + this.popIndent(); + result += this.indent + "end\n"; return result; } diff --git a/test/translation/lua/tryCatch.lua b/test/translation/lua/tryCatch.lua index 082aafc57..c5df1423b 100644 --- a/test/translation/lua/tryCatch.lua +++ b/test/translation/lua/tryCatch.lua @@ -1,6 +1,8 @@ -xpcall(function() - local a = 42; -end, -function(er) - local b = "fail"; -end) +do + local __TS_try, er = pcall(function() + local a = 42; + end); + if not __TS_try then + local b = "fail"; + end +end diff --git a/test/translation/lua/tryCatchFinally.lua b/test/translation/lua/tryCatchFinally.lua index c08fa2bc4..ac0269f8c 100644 --- a/test/translation/lua/tryCatchFinally.lua +++ b/test/translation/lua/tryCatchFinally.lua @@ -1,7 +1,11 @@ -xpcall(function() - local a = 42; -end, -function(er) - local b = "fail"; -end) -local c = "finally"; +do + local __TS_try, er = pcall(function() + local a = 42; + end); + if not __TS_try then + local b = "fail"; + end + do + local c = "finally"; + end +end diff --git a/test/translation/lua/tryFinally.lua b/test/translation/lua/tryFinally.lua index 4eb2f1c34..a117b8bb2 100644 --- a/test/translation/lua/tryFinally.lua +++ b/test/translation/lua/tryFinally.lua @@ -1,6 +1,8 @@ -xpcall(function() - local a = 42; -end, -function(e) -end) -local b = "finally"; +do + pcall(function() + local a = 42; + end); + do + local b = "finally"; + end +end diff --git a/test/unit/error.spec.ts b/test/unit/error.spec.ts index f3f737a09..da0e59473 100644 --- a/test/unit/error.spec.ts +++ b/test/unit/error.spec.ts @@ -24,4 +24,55 @@ export class LuaErrorTests { ); }).toThrowError(TranspileError, "Invalid throw expression, only strings can be thrown."); } + + @TestCase(0, "A") + @TestCase(1, "B") + @TestCase(2, "C") + @Test("re-throw") + public reThrow(i: number, expected: any): void { + const source = + `const i = ${i}; + function foo() { + try { + try { + if (i === 0) { throw "z"; } + } catch (e) { + throw "a"; + } finally { + if (i === 1) { throw "b"; } + } + } catch (e) { + throw (e as string).toUpperCase(); + } finally { + throw "C"; + } + } + let result = "x"; + try { + foo(); + } catch (e) { + result = (e as string)[(e as string).length - 1]; + } + return result;`; + const result = util.transpileAndExecute(source); + Expect(result).toBe(expected); + } + + @Test("re-throw (no catch var)") + public reThrowWithoutCatchVar(): void { + const source = + `let result = "x"; + try { + try { + throw "y"; + } catch { + throw "z"; + } + } catch (e) { + result = (e as string)[(e as string).length - 1]; + } + return result;`; + const result = util.transpileAndExecute(source); + Expect(result).toBe("z"); + } } From abb9f07e94f6b6f34399cd3e6ab11403d8d81ce7 Mon Sep 17 00:00:00 2001 From: Perry van Wesel Date: Fri, 19 Oct 2018 22:27:56 +0200 Subject: [PATCH 04/59] Made LuaJIT use table.unpack for destructing assignments (#257) --- src/targets/Transpiler.JIT.ts | 5 +++++ test/unit/assignmentDestructuring.spec.ts | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/targets/Transpiler.JIT.ts b/src/targets/Transpiler.JIT.ts index 8aa2c2b36..97173044f 100644 --- a/src/targets/Transpiler.JIT.ts +++ b/src/targets/Transpiler.JIT.ts @@ -34,4 +34,9 @@ export class LuaTranspilerJIT extends LuaTranspiler52 { throw TSTLErrors.UnsupportedKind("bitwise operator", node.operatorToken.kind, node); } } + + /** @override */ + public transpileDestructingAssignmentValue(node: ts.Expression): string { + return `table.unpack(${this.transpileExpression(node)})`; + } } diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index 650e13024..994c3f83c 100644 --- a/test/unit/assignmentDestructuring.spec.ts +++ b/test/unit/assignmentDestructuring.spec.ts @@ -9,7 +9,7 @@ export class AssignmentDestructuringTests { let [a, b] = myFunc();`; @Test("Assignment destructuring [5.1]") - public assignmentDestructuring51() { + public assignmentDestructuring51(): void { // Transpile const lua = util.transpileString( this.assignmentDestruturingTs, {luaTarget: LuaTarget.Lua51, luaLibImport: "none"} @@ -19,7 +19,7 @@ export class AssignmentDestructuringTests { } @Test("Assignment destructuring [5.2]") - public tupleDestructing52() { + public tupleDestructing52(): void { // Transpile const lua = util.transpileString( this.assignmentDestruturingTs, {luaTarget: LuaTarget.Lua52, luaLibImport: "none"} @@ -27,4 +27,14 @@ export class AssignmentDestructuringTests { // Assert Expect(lua).toBe(`local a,b=table.unpack(myFunc());`); } + + @Test("Assignment destructuring [JIT]") + public assignmentDestructuringJIT(): void { + // Transpile + const lua = util.transpileString( + this.assignmentDestruturingTs, {luaTarget: LuaTarget.LuaJIT, luaLibImport: "none"} + ); + // Assert + Expect(lua).toBe(`local a,b=table.unpack(myFunc());`); + } } From 3054adb90b421041de783c923b48d4d22b3dae55 Mon Sep 17 00:00:00 2001 From: Lorenz Junglas Date: Sat, 20 Oct 2018 16:36:45 +0200 Subject: [PATCH 05/59] Fixed JIT using table.unpack (#258) * Fixed JIT using table.unpack sry * Fixed test for JIT unpack * Moved transpileDestructingAssignmentValue from 5.1 to 5.0 --- src/Transpiler.ts | 2 +- src/targets/Transpiler.51.ts | 5 +---- src/targets/Transpiler.JIT.ts | 2 +- test/unit/assignmentDestructuring.spec.ts | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 6bcae227e..fe6069ca9 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1623,7 +1623,7 @@ export abstract class LuaTranspiler { } public transpileDestructingAssignmentValue(node: ts.Expression): string { - throw TSTLErrors.UnsupportedForTarget("Destructing statements", this.options.luaTarget, node); + return `unpack(${this.transpileExpression(node)})`; } public transpileVariableDeclaration(node: ts.VariableDeclaration): string { diff --git a/src/targets/Transpiler.51.ts b/src/targets/Transpiler.51.ts index f84d74fe6..bd4d63359 100644 --- a/src/targets/Transpiler.51.ts +++ b/src/targets/Transpiler.51.ts @@ -4,8 +4,5 @@ import { TSHelper as tsHelper } from "../TSHelper"; import * as ts from "typescript"; export class LuaTranspiler51 extends LuaTranspiler { - /** @override */ - public transpileDestructingAssignmentValue(node: ts.Expression): string { - return `unpack(${this.transpileExpression(node)})`; - } + } diff --git a/src/targets/Transpiler.JIT.ts b/src/targets/Transpiler.JIT.ts index 97173044f..76bf2fac2 100644 --- a/src/targets/Transpiler.JIT.ts +++ b/src/targets/Transpiler.JIT.ts @@ -37,6 +37,6 @@ export class LuaTranspilerJIT extends LuaTranspiler52 { /** @override */ public transpileDestructingAssignmentValue(node: ts.Expression): string { - return `table.unpack(${this.transpileExpression(node)})`; + return `unpack(${this.transpileExpression(node)})`; } } diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index 994c3f83c..bbe555adb 100644 --- a/test/unit/assignmentDestructuring.spec.ts +++ b/test/unit/assignmentDestructuring.spec.ts @@ -35,6 +35,6 @@ export class AssignmentDestructuringTests { this.assignmentDestruturingTs, {luaTarget: LuaTarget.LuaJIT, luaLibImport: "none"} ); // Assert - Expect(lua).toBe(`local a,b=table.unpack(myFunc());`); + Expect(lua).toBe(`local a,b=unpack(myFunc());`); } } From bab8df3e9916fbe06699bf5d17646b3c12a294dd Mon Sep 17 00:00:00 2001 From: Lorenz Junglas Date: Sat, 20 Oct 2018 16:52:04 +0200 Subject: [PATCH 06/59] Moved transpile switch to Lua 5.2 (#259) * Moved transpile switch to Lua 5.2 Closes #194 * Fixed typo --- src/Transpiler.ts | 70 +------------------------------- src/targets/Transpiler.52.ts | 73 ++++++++++++++++++++++++++++++++++ test/unit/conditionals.spec.ts | 8 ++++ 3 files changed, 82 insertions(+), 69 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index fe6069ca9..f4882b363 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -615,75 +615,7 @@ export abstract class LuaTranspiler { } public transpileSwitch(node: ts.SwitchStatement): string { - const expression = this.transpileExpression(node.expression, true); - const clauses = node.caseBlock.clauses; - - let result = this.indent + "-------Switch statement start-------\n"; - - const switchVarName = "____switch" + this.genVarCounter; - this.genVarCounter++; - - result += this.indent + `local ${switchVarName} = ${expression}\n`; - - let hasDefaultClause = false; - - // If statement to go to right entry label - clauses.forEach((clause, index) => { - if (ts.isCaseClause(clause)) { - result += this.indent + - `if ${this.transpileExpression(clause.expression, true)} == ${switchVarName} then\n`; - - this.pushIndent(); - result += this.indent + `goto ${switchVarName}_case_${index}\n`; - this.popIndent(); - - result += this.indent + "end\n"; - } else if (ts.isDefaultClause(clause)) { - hasDefaultClause = true; - } - }); - - result += "\n"; - - // If no case condition is matched jump to end or default immediately - if (hasDefaultClause) { - result += this.indent + `goto ${switchVarName}_default\n`; - } else { - result += this.indent + `goto ${switchVarName}_end\n`; - } - - result += "\n"; - - const transpileClauseBody = (clause: ts.CaseOrDefaultClause) => { - this.transpilingSwitch++; - result += this.indent + "do\n"; - this.pushIndent(); - result += this.transpileBlock(ts.createBlock(clause.statements)); - this.popIndent(); - result += this.indent + "end\n"; - this.transpilingSwitch--; - }; - - clauses.forEach((clause, index) => { - if (ts.isCaseClause(clause)) { - result += this.indent + `::${switchVarName}_case_${index}::\n`; - - transpileClauseBody(clause); - - if (tsHelper.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)) { - result += this.indent + `goto ${switchVarName}_end\n`; - } - } else if (ts.isDefaultClause(clause)) { - result += this.indent + `::${switchVarName}_default::\n`; - - transpileClauseBody(clause); - } - }); - - result += this.indent + `::${switchVarName}_end::\n`; - result += this.indent + "-------Switch statement end-------\n"; - - return result; + throw TSTLErrors.UnsupportedForTarget("Switch statements", this.options.luaTarget, node); } public transpileTry(node: ts.TryStatement): string { diff --git a/src/targets/Transpiler.52.ts b/src/targets/Transpiler.52.ts index 31a6f4dcb..0fcba461f 100644 --- a/src/targets/Transpiler.52.ts +++ b/src/targets/Transpiler.52.ts @@ -59,6 +59,79 @@ export class LuaTranspiler52 extends LuaTranspiler51 { } } + /** @override */ + public transpileSwitch(node: ts.SwitchStatement): string { + const expression = this.transpileExpression(node.expression, true); + const clauses = node.caseBlock.clauses; + + let result = this.indent + "-------Switch statement start-------\n"; + + const switchVarName = "____switch" + this.genVarCounter; + this.genVarCounter++; + + result += this.indent + `local ${switchVarName} = ${expression}\n`; + + let hasDefaultClause = false; + + // If statement to go to right entry label + clauses.forEach((clause, index) => { + if (ts.isCaseClause(clause)) { + result += this.indent + + `if ${this.transpileExpression(clause.expression, true)} == ${switchVarName} then\n`; + + this.pushIndent(); + result += this.indent + `goto ${switchVarName}_case_${index}\n`; + this.popIndent(); + + result += this.indent + "end\n"; + } else if (ts.isDefaultClause(clause)) { + hasDefaultClause = true; + } + }); + + result += "\n"; + + // If no case condition is matched jump to end or default immediately + if (hasDefaultClause) { + result += this.indent + `goto ${switchVarName}_default\n`; + } else { + result += this.indent + `goto ${switchVarName}_end\n`; + } + + result += "\n"; + + const transpileClauseBody = (clause: ts.CaseOrDefaultClause) => { + this.transpilingSwitch++; + result += this.indent + "do\n"; + this.pushIndent(); + result += this.transpileBlock(ts.createBlock(clause.statements)); + this.popIndent(); + result += this.indent + "end\n"; + this.transpilingSwitch--; + }; + + clauses.forEach((clause, index) => { + if (ts.isCaseClause(clause)) { + result += this.indent + `::${switchVarName}_case_${index}::\n`; + + transpileClauseBody(clause); + + if (tsHelper.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)) { + result += this.indent + `goto ${switchVarName}_end\n`; + } + } else if (ts.isDefaultClause(clause)) { + result += this.indent + `::${switchVarName}_default::\n`; + + transpileClauseBody(clause); + } + }); + + result += this.indent + `::${switchVarName}_end::\n`; + result += this.indent + "-------Switch statement end-------\n"; + + return result; + } + /** @override */ public transpileDestructingAssignmentValue(node: ts.Expression): string { return `table.unpack(${this.transpileExpression(node)})`; diff --git a/test/unit/conditionals.spec.ts b/test/unit/conditionals.spec.ts index 3e56620ca..e48fd962f 100644 --- a/test/unit/conditionals.spec.ts +++ b/test/unit/conditionals.spec.ts @@ -1,4 +1,6 @@ import { Expect, Test, TestCase } from "alsatian"; +import { TranspileError } from "../../src/Errors"; +import { LuaTarget } from "../../src/Transpiler"; import * as util from "../src/util"; export class LuaConditionalsTests { @@ -326,4 +328,10 @@ export class LuaConditionalsTests { Expect(result).toBe(5); } + + @Test("switch not allowed in 5.1") + public switchThrow51(): void { + Expect( () => util.transpileString(`switch ("abc") {}`, {luaTarget: LuaTarget.Lua51})) + .toThrowError(TranspileError, "Switch statements is/are not supported for target Lua 5.1."); + } } From c4cd09932bb18bcc345dea35bdb6d0f1dba5a507 Mon Sep 17 00:00:00 2001 From: Perry van Wesel Date: Sun, 21 Oct 2018 12:12:16 +0200 Subject: [PATCH 07/59] Cleaned up ForOf code (#260) * Cleaned up forOf loop, made it use numeric loop instead of ipairs * Fixed bug in non-array loop header and fixed tests * Fixed up some PR comments --- src/Transpiler.ts | 33 +++++++++++++++++++++++---------- test/translation/lua/forOf.lua | 4 +++- test/unit/loops.spec.ts | 20 ++++++++++++++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index f4882b363..17509b8ce 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -558,21 +558,32 @@ export abstract class LuaTranspiler { const variable = (node.initializer as ts.VariableDeclarationList).declarations[0]; // Transpile expression - const expression = this.transpileExpression(node.expression); + const iterable = this.transpileExpression(node.expression); // Use ipairs for array types, pairs otherwise const isArray = tsHelper.isArrayType(this.checker.getTypeAtLocation(node.expression), this.checker); - const pairs = isArray ? "ipairs" : "pairs"; - // Make header let result = ""; - if (ts.isIdentifier(variable.name)) { - result = this.indent + `for _, ${this.transpileIdentifier(variable.name)} in ${pairs}(${expression}) do\n`; - } else if (ts.isArrayBindingPattern(variable.name)) { - const valueVar = "__forOfValue" + this.genVarCounter; - result = this.indent + `for _, ${valueVar} in ${pairs}(${expression}) do\n`; - const declaration = ts.createVariableDeclaration(variable.name, undefined, ts.createIdentifier(valueVar)); - result += this.indent + this.transpileVariableDeclaration(declaration); + + if (!isArray && ts.isIdentifier(variable.name)) { + result = this.indent + `for _, ${this.transpileIdentifier(variable.name)} in pairs(${iterable}) do\n`; + } else { + let itemVariable: ts.Identifier; + if (isArray) { + // Cache the expression result + result += this.indent + `local __loopVariable${this.genVarCounter} = ${iterable};\n`; + result += this.indent + `for i${this.genVarCounter}=1, #__loopVariable${this.genVarCounter} do\n`; + itemVariable = ts.createIdentifier(`__loopVariable${this.genVarCounter}[i${this.genVarCounter}]`); + } else { + const variableName = `__forOfValue${this.genVarCounter}`; + itemVariable = ts.createIdentifier(variableName); + result += this.indent + `for _, ${variableName} in pairs(${iterable}) do\n`; + } + + const declaration = ts.createVariableDeclaration(variable.name, undefined, itemVariable); + this.pushIndent(); + result += this.indent + this.transpileVariableDeclaration(declaration) + ";\n"; + this.popIndent(); } // For body @@ -580,6 +591,8 @@ export abstract class LuaTranspiler { result += this.transpileLoopBody(node); this.popIndent(); + this.genVarCounter++; + return result + this.indent + "end\n"; } diff --git a/test/translation/lua/forOf.lua b/test/translation/lua/forOf.lua index 79c0a9fed..4a7ca76a9 100644 --- a/test/translation/lua/forOf.lua +++ b/test/translation/lua/forOf.lua @@ -1,4 +1,6 @@ -for _, i in ipairs({1,2,3,4,5,6,7,8,9,10}) do +local __loopVariable0 = {1,2,3,4,5,6,7,8,9,10}; +for i0=1, #__loopVariable0 do + local i = __loopVariable0[i0]; do end ::__continue0:: diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 007127d33..4d6d6e985 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -286,6 +286,26 @@ export class LuaLoopTests { Expect(result).toBe(JSON.stringify(expected)); } + @TestCase([[1, 2], [2, 3], [3, 4]], [3, 5, 7]) + @Test("forof destructing") + public forofDestructing(inp: number[][], expected: any): void { + // Transpile + const lua = util.transpileString( + `let objTest = ${JSON.stringify(inp)}; + let arrResultTest = []; + for (let [a,b] of objTest) { + arrResultTest.push(a + b) + } + return JSONStringify(arrResultTest);` + ); + + // Execute + const result = util.executeLua(lua); + + // Assert + Expect(result).toBe(JSON.stringify(expected)); + } + @TestCase([0, 1, 2, 3, 4], [0, 0, 2, 0, 4]) @Test("forof with continue") public forofWithContinue(inp: number[], expected: number[]): void { From e0ca0a4bffb4ccf5fa01c7bdba715926a2250906 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 25 Oct 2018 07:50:09 -0600 Subject: [PATCH 08/59] 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 09/59] 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 10/59] 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 11/59] 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 12/59] 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 f6e094f66a79bc3105a925cac28cb54f7957ccc8 Mon Sep 17 00:00:00 2001 From: Zeng Jie Date: Sat, 27 Oct 2018 02:34:24 +0800 Subject: [PATCH 13/59] Fix error in module namespace enum exporting (#272) --- src/Transpiler.ts | 4 +--- test/translation/lua/modulesNamespaceExportEnum.lua | 10 ++++++++++ test/translation/ts/modulesNamespaceExportEnum.ts | 6 ++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 test/translation/lua/modulesNamespaceExportEnum.lua create mode 100644 test/translation/ts/modulesNamespaceExportEnum.ts diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 17509b8ce..12cec8911 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -444,9 +444,7 @@ export abstract class LuaTranspiler { const defName = this.definitionName(enumMember.name); result += this.indent + `${defName}=${enumMember.value}\n`; } else { - const defName = this.definitionName( - `${this.transpileIdentifier(node.name)}.${enumMember.name}` - ); + const defName = `${this.transpileIdentifier(node.name)}.${enumMember.name}`; result += this.indent + `${defName}=${enumMember.value}\n`; } }); diff --git a/test/translation/lua/modulesNamespaceExportEnum.lua b/test/translation/lua/modulesNamespaceExportEnum.lua new file mode 100644 index 000000000..002807a03 --- /dev/null +++ b/test/translation/lua/modulesNamespaceExportEnum.lua @@ -0,0 +1,10 @@ +local exports = exports or {} +local test = exports.test or test or {} +do + local TestEnum={} + TestEnum.foo="foo" + TestEnum.bar="bar" + test.TestEnum = TestEnum +end +exports.test = test +return exports diff --git a/test/translation/ts/modulesNamespaceExportEnum.ts b/test/translation/ts/modulesNamespaceExportEnum.ts new file mode 100644 index 000000000..dd40d81cb --- /dev/null +++ b/test/translation/ts/modulesNamespaceExportEnum.ts @@ -0,0 +1,6 @@ +export namespace test { + export enum TestEnum { + foo = "foo", + bar = "bar", + } +} From 10be9634560321f6ec4ab2651fd7a24fdc28a75f Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 29 Oct 2018 07:13:34 -0600 Subject: [PATCH 14/59] 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 15/59] 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 85b9d3b3a40f2228fbf0e363481c67243d58f1f9 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Wed, 7 Nov 2018 21:39:17 +0100 Subject: [PATCH 16/59] 0.11.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 245ff34a6..3f18d09f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "0.10.0", + "version": "0.11.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d5b7f546f..b9c1a6ecc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typescript-to-lua", "license": "MIT", - "version": "0.10.0", + "version": "0.11.0", "repository": "https://github.com/Perryvw/TypescriptToLua", "keywords": [ "typescript", From ad5f2e322f6470ebc62fcc86f66efc6167633902 Mon Sep 17 00:00:00 2001 From: Perry van Wesel Date: Wed, 7 Nov 2018 22:27:15 +0100 Subject: [PATCH 17/59] Update CHANGELOG.md --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aeb1a386..d5653a64e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 0.11.0 +* Fixed bug when throwing anything that was not a string. (@tomblind) +* Added support for object literal method declarations. (@tomblind) +* Fixed several issues with assignment operators (@tomblind) +* `else if` statements are now transpiled to Lua `elseif` instead of nested ifs statements. (@tomblind) +* Occurrences of const enum values are now directly replaced with their value in the Lua output. (@DoctorGester) +* Rethrowing is now possible from try/catch blocks (@tomblind) +* Destructing statements in LuaJit now use `unpack` instead of `table.unpack` +* Removed support for switch statements for versions <= 5.1. +* Refactored `for ... of` translation, it now uses numeric `for ` loops instead of `ipairs` for performance reasons. + +## 0.10.0 +* Added support for NonNullExpression (`abc!` transforming the type from `abc | undefined` to `abc`) +* Added expression position to replacement binary expression to improve error messages. +* Fixed various issues with !TupleReturn (@tomblind) +* Added support for `array.reverse`, `array.shift`, `array.unshift`, `array.sort`. (@andreiradu) +* Added translation for `Object.hasOwnProperty()`. (@andreiradu) +* Added support for class expressions (@andreiradu) +* Fixed bug in detecting array types (@tomblind) +* Added public API functions and better webpack functionality. + ## 0.9.0 * Fixed an issue where default parameter values were ignored in function declarations. * Fixed a bug where `self` was undefined in function properties. From 80010158412712dea69c66d216f18c804ace6cdf Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Thu, 8 Nov 2018 19:34:20 +1000 Subject: [PATCH 18/59] Luajit unpack fix (#273) * LuaJIT uses LuaTranspiler's spread * Added some unit tests for spreading * Stopped using prototype * Removed Transpiler import --- src/targets/Transpiler.JIT.ts | 5 +++++ test/unit/spreadElement.spec.ts | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/targets/Transpiler.JIT.ts b/src/targets/Transpiler.JIT.ts index 76bf2fac2..a23e3a8e2 100644 --- a/src/targets/Transpiler.JIT.ts +++ b/src/targets/Transpiler.JIT.ts @@ -39,4 +39,9 @@ export class LuaTranspilerJIT extends LuaTranspiler52 { public transpileDestructingAssignmentValue(node: ts.Expression): string { return `unpack(${this.transpileExpression(node)})`; } + + /** @override */ + public transpileSpreadElement(node: ts.SpreadElement): string { + return "unpack(" + this.transpileExpression(node.expression) + ")"; + } } diff --git a/test/unit/spreadElement.spec.ts b/test/unit/spreadElement.spec.ts index 50bf8f2dc..effd3d846 100644 --- a/test/unit/spreadElement.spec.ts +++ b/test/unit/spreadElement.spec.ts @@ -21,4 +21,20 @@ export class SpreadElementTest { const lua = util.transpileString(`[].push(...${JSON.stringify([1, 2, 3])});`, {luaTarget: LuaTarget.Lua51}); Expect(lua).toBe("__TS__ArrayPush({}, unpack({1,2,3}));"); } + + @Test("Spread Element Lua 5.2") + public spreadElement52() { + const lua = util.transpileString(`[...[0, 1, 2]]`, {luaTarget: LuaTarget.Lua52, luaLibImport: "none"}); + Expect(lua).toBe("{table.unpack({0,1,2})};"); + } + @Test("Spread Element Lua 5.3") + public spreadElement53() { + const lua = util.transpileString(`[...[0, 1, 2]]`, {luaTarget: LuaTarget.Lua53, luaLibImport: "none"}); + Expect(lua).toBe("{table.unpack({0,1,2})};"); + } + @Test("Spread Element Lua JIT") + public spreadElementJIT() { + const lua = util.transpileString(`[...[0, 1, 2]]`, {luaTarget: LuaTarget.LuaJIT, luaLibImport: "none"}); + Expect(lua).toBe("{unpack({0,1,2})};"); + } } From 26fe3571d462f398826630191895bc0923b239a0 Mon Sep 17 00:00:00 2001 From: Lorenz Junglas Date: Thu, 8 Nov 2018 11:00:58 +0100 Subject: [PATCH 19/59] Fixed constructor overloads (#275) * Fixed constructor overloads Fixes #274 * Removed lambda body * Removed transpileFile (should be added to changelog since this was exposed in the API) --- src/Compiler.ts | 13 --------- src/Transpiler.ts | 9 +++--- src/tstl.ts | 1 - test/unit/overloads.spec.ts | 58 ++++++++++++++++++++++++++++++++++--- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/Compiler.ts b/src/Compiler.ts index b15c3850f..1f2eaa8e4 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -193,19 +193,6 @@ export function transpileString(str: string, return result.trim(); } -export function transpileFile(filePath: string): string { - const program = ts.createProgram([filePath], {}); - const checker = program.getTypeChecker(); - - // Output errors - const diagnostics = ts.getPreEmitDiagnostics(program).filter(diag => diag.code !== 6054); - diagnostics.forEach(diagnostic => console.log(`${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`)); - - const options: ts.CompilerOptions = { luaLibImport: "none" }; - const result = createTranspiler(checker, options, program.getSourceFile(filePath)).transpileSourceFile(); - return result.trim(); -} - function reportDiagnostic(diagnostic: ts.Diagnostic): void { if (diagnostic.file) { const { line, character } = diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 12cec8911..48f697fcc 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -337,9 +337,7 @@ export abstract class LuaTranspiler { public transpileLuaLibFunction(func: LuaLibFeature, ...params: string[]): string { this.importLuaLibFeature(func); - params = params.filter(element => { - return element.toString() !== ""; - }); + params = params.filter(element => element.toString() !== ""); return `__TS__${func}(${params.join(", ")})`; } @@ -1778,8 +1776,9 @@ export abstract class LuaTranspiler { result += this.indent + `${className}.${fieldName} = ${value}\n`; } - // Try to find constructor - const constructor = node.members.filter(ts.isConstructorDeclaration)[0]; + // Find first constructor with body + const constructor = + node.members.filter(n => ts.isConstructorDeclaration(n) && n.body)[0] as ts.ConstructorDeclaration; if (constructor) { // Add constructor plus initialization of instance fields result += this.transpileConstructor(constructor, className); diff --git a/src/tstl.ts b/src/tstl.ts index cef7e8cc0..31174588c 100644 --- a/src/tstl.ts +++ b/src/tstl.ts @@ -9,7 +9,6 @@ export { export { compile, compileFilesWithOptions, - transpileFile, transpileString, watchWithOptions } from "./Compiler"; diff --git a/test/unit/overloads.spec.ts b/test/unit/overloads.spec.ts index 69fa96cc1..e98fd6f0b 100644 --- a/test/unit/overloads.spec.ts +++ b/test/unit/overloads.spec.ts @@ -4,7 +4,7 @@ import * as util from "../src/util"; export class OverloadTests { @Test("overload function1") - public overloadFunction1() { + public overloadFunction1(): void { const lua = util.transpileString( `function abc(def: number): string; function abc(def: string): string; @@ -23,7 +23,7 @@ export class OverloadTests { } @Test("overload function2") - public overloadFunction2() { + public overloadFunction2(): void { const lua = util.transpileString( `function abc(def: number): string; function abc(def: string): string; @@ -42,7 +42,7 @@ export class OverloadTests { } @Test("overload method1") - public overloadMethod1() { + public overloadMethod1(): void { const lua = util.transpileString( `class myclass { static abc(def: number): string; @@ -63,7 +63,7 @@ export class OverloadTests { } @Test("overload method2") - public overloadMethod2() { + public overloadMethod2(): void { const lua = util.transpileString( `class myclass { static abc(def: number): string; @@ -82,4 +82,54 @@ export class OverloadTests { Expect(result).toBe("ghj"); } + + @Test("constructor1") + public constructor1(): void { + const lua = util.transpileString( + `class myclass { + num: number; + str: string; + + constructor(def: number): string; + constructor(def: string): string; + constructor(def: number | string): string { + if (typeof def == "number") { + this.num = def; + } else { + this.str = def; + } + } + } + const inst = new myclass(3); + return inst.num`); + + const result = util.executeLua(lua); + + Expect(result).toBe(3); + } + + @Test("constructor2") + public constructor2(): void { + const lua = util.transpileString( + `class myclass { + num: number; + str: string; + + constructor(def: number): string; + constructor(def: string): string; + constructor(def: number | string): string { + if (typeof def == "number") { + this.num = def; + } else { + this.str = def; + } + } + } + const inst = new myclass("ghj"); + return inst.str`); + + const result = util.executeLua(lua); + + Expect(result).toBe("ghj"); + } } From 9eb59627e06dce42a5ceffa40c1bd7176f4b6f30 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Sun, 11 Nov 2018 18:34:46 +1000 Subject: [PATCH 20/59] Lualib omit when unused (#280) * lualib inline omit header when no features are used * Tests to enforce no lualib text when unused, unless using always --- src/Transpiler.ts | 2 +- test/unit/modules.spec.ts | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 48f697fcc..fd888576a 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -233,7 +233,7 @@ export abstract class LuaTranspiler { } // Inline lualib features - if (this.options.luaLibImport === LuaLibImportKind.Inline) { + if (this.options.luaLibImport === LuaLibImportKind.Inline && this.luaLibFeatureSet.size > 0) { result += "\n" + "-- Lua Library Imports\n"; for (const feature of this.luaLibFeatureSet) { const featureFile = path.resolve(__dirname, `../dist/lualib/${feature}.lua`); diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index 62187e060..7d2509376 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -23,15 +23,6 @@ export class LuaModuleTests { Expect(lua.startsWith(`require("lualib_bundle")`)); } - @Test("lualibRequireNoUses") - public lualibRequireNoUses(): void { - // Transpile - const lua = util.transpileString(``, { luaLibImport: LuaLibImportKind.Require, luaTarget: LuaTarget.LuaJIT }); - - // Assert - Expect(lua).toBe(``); - } - @Test("lualibRequireAlways") public lualibRequireAlways(): void { // Transpile @@ -63,4 +54,16 @@ export class LuaModuleTests { Expect(result).toBe(3); } + + @TestCase(LuaLibImportKind.Inline) + @TestCase(LuaLibImportKind.None) + @TestCase(LuaLibImportKind.Require) + @Test("LuaLib no uses? No code") + public lualibNoUsesNoCode(impKind: LuaLibImportKind): void { + // Transpile + const lua = util.transpileString(``, { luaLibImport: impKind }); + + // Assert + Expect(lua).toBe(``); + } } From 6c6af51354ca943248f2357d0991c5745b8d122b Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 18 Nov 2018 20:49:44 +0100 Subject: [PATCH 21/59] Fixed bug with default values for constructor parameters --- src/Transpiler.ts | 35 ++++++++++++++++++----------------- test/unit/class.spec.ts | 22 +++++++++++++++++++++- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index fd888576a..7ca71b507 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1889,29 +1889,30 @@ export abstract class LuaTranspiler { public transpileConstructor(node: ts.ConstructorDeclaration, className: string): string { - const extraInstanceFields = []; + // Check for field declarations in constructor + const constructorFieldsDeclarations = node.parameters.filter(p => p.modifiers !== undefined); - const parameters = ["self"]; - node.parameters.forEach(param => { - // If param has decorators, add extra instance field - if (param.modifiers !== undefined) { - extraInstanceFields.push(this.transpileIdentifier(param.name as ts.Identifier)); - } - // Add to parameter list - parameters.push(this.transpileIdentifier(param.name as ts.Identifier)); - }); - - let result = this.indent + `function ${className}.constructor(${parameters.join(",")})\n`; + const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters); - // Add in instance field declarations - for (const f of extraInstanceFields) { - result += this.indent + ` self.${f} = ${f}\n`; - } + let result = this.indent + `function ${className}.constructor(${["self"].concat(paramNames).join(",")})\n`; // Transpile constructor body this.pushIndent(); this.classStack.push(className); - result += this.transpileBlock(node.body); + + // Add in instance field declarations + for (const declaration of constructorFieldsDeclarations) { + const declarationName = this.transpileIdentifier(declaration.name as ts.Identifier); + if (declaration.initializer) { + const value = this.transpileExpression(declaration.initializer); + result += this.indent + `self.${declarationName} = ${declarationName} or ${value}\n`; + } else { + result += this.indent + `self.${declarationName} = ${declarationName}\n`; + } + } + + result += this.transpileFunctionBody(node.parameters, node.body, spreadIdentifier); + this.classStack.pop(); this.popIndent(); diff --git a/test/unit/class.spec.ts b/test/unit/class.spec.ts index d3655034f..de67a5425 100644 --- a/test/unit/class.spec.ts +++ b/test/unit/class.spec.ts @@ -44,7 +44,7 @@ export class ClassTests { @Test("ClassConstructorAssignment") public classConstructorAssignment(): void { - // Transpile + // Transpile const lua = util.transpileString( `class a { constructor(public field: number) {} } return new a(4).field;` @@ -57,6 +57,26 @@ export class ClassTests { Expect(result).toBe(4); } + @Test("ClassConstructorDefaultParameter") + public classConstructorDefaultParameter(): void { + const result = util.transpileAndExecute( + `class a { public field: number; constructor(f: number = 3) { this.field = f; } } + return new a().field;` + ); + + Expect(result).toBe(3); + } + + @Test("ClassConstructorAssignmentDefault") + public classConstructorAssignmentParameterDefault(): void { + const result = util.transpileAndExecute( + `class a { constructor(public field: number = 3) { } } + return new a().field;` + ); + + Expect(result).toBe(3); + } + @Test("ClassNewNoBrackets") public classNewNoBrackets(): void { // Transpile From 1aae661460bb244759c809898f3c8191730206ab Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 18 Nov 2018 20:55:01 +0100 Subject: [PATCH 22/59] 0.11.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f18d09f2..917dc78de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "0.11.0", + "version": "0.11.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b9c1a6ecc..1f083c6e4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typescript-to-lua", "license": "MIT", - "version": "0.11.0", + "version": "0.11.1", "repository": "https://github.com/Perryvw/TypescriptToLua", "keywords": [ "typescript", From a24caa2270b77ec0b4e448e33d903bf44d0471ac Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 19 Nov 2018 08:30:09 -0700 Subject: [PATCH 23/59] 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 24/59] 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 25/59] 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 26/59] 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 27/59] 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 28/59] 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 29/59] 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 30/59] 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 31/59] 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 32/59] 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 33/59] 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; From 6ac127b21dc469dbe96379b07e7d309189a9a519 Mon Sep 17 00:00:00 2001 From: Perry van Wesel Date: Sat, 24 Nov 2018 12:58:24 +0100 Subject: [PATCH 34/59] Added discord badge and link to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 852dae602..903972572 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Large projects written in lua can become hard to maintain and make it easy to ma [![Coverage](https://codecov.io/gh/perryvw/typescripttolua/branch/master/graph/badge.svg)](https://codecov.io/gh/perryvw/typescripttolua) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/TypescriptToLua/Lobby) +You can also find us on Discord: [![Discord](https://img.shields.io/discord/515854149821267971.svg)](https://discord.gg/BWAq58Y) + ## Documentation More detailed documentation and info on writing declarations can be found [on the wiki](https://github.com/Perryvw/TypescriptToLua/wiki). From dbe201a34965ef88ab33172ff03dcc2f9555e3da Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 25 Nov 2018 18:27:34 +0100 Subject: [PATCH 35/59] Added support for JSDoc tags as decorators --- src/Decorator.ts | 2 +- src/TSHelper.ts | 21 +++- test/src/util.ts | 48 +++++++++ test/translation/ts/enumMembersOnly.ts | 2 +- test/unit/tshelper.spec.ts | 138 +++++++++++++++++++++++-- 5 files changed, 198 insertions(+), 13 deletions(-) diff --git a/src/Decorator.ts b/src/Decorator.ts index e0b3b6a00..f8b92a070 100644 --- a/src/Decorator.ts +++ b/src/Decorator.ts @@ -7,7 +7,7 @@ export class Decorator { if (nameEnd === -1) { nameEnd = raw.length; } - this.kind = DecoratorKind[raw.substring(1, nameEnd)]; + this.kind = DecoratorKind[raw.substring(0, nameEnd)]; this.args = raw.split(" ").slice(1); } } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index d35973f32..aa58b5901 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -109,14 +109,29 @@ export class TSHelper { const comments = type.symbol.getDocumentationComment(checker); const decorators = comments.filter(comment => comment.kind === "text") - .map(comment => comment.text.trim().split("\n")) + .map(comment => comment.text.split("\n")) .reduce((a, b) => a.concat(b), []) + .map(line => line.trim()) .filter(comment => comment[0] === "!"); + const decMap = new Map(); + decorators.forEach(decStr => { - const dec = new Decorator(decStr); - decMap.set(dec.kind, dec); + const dec = new Decorator(decStr.substr(1)); + if (dec.kind !== undefined) { + decMap.set(dec.kind, dec); + } else { + console.warn(`Encountered unknown decorator ${decStr}.`); + } }); + + type.symbol.getJsDocTags().forEach(tag => { + const dec = new Decorator(tag.name); + if (dec.kind !== undefined) { + decMap.set(dec.kind, dec); + } + }); + return decMap; } return new Map(); diff --git a/test/src/util.ts b/test/src/util.ts index 71f8227ac..197f87208 100644 --- a/test/src/util.ts +++ b/test/src/util.ts @@ -67,6 +67,54 @@ export function transpileAndExecute(tsStr: string): any { return executeLua(transpileString(tsStr)); } +export function parseTypeScript(typescript: string, target: LuaTarget = LuaTarget.Lua53) + : [ts.SourceFile, ts.TypeChecker] { + const compilerHost = { + directoryExists: () => true, + fileExists: (fileName): boolean => true, + getCanonicalFileName: fileName => fileName, + getCurrentDirectory: () => "", + getDefaultLibFileName: () => "lib.es6.d.ts", + getDirectories: () => [], + getNewLine: () => "\n", + + getSourceFile: (filename, languageVersion) => { + if (filename === "file.ts") { + return ts.createSourceFile(filename, typescript, ts.ScriptTarget.Latest, false); + } + if (filename === "lib.es6.d.ts") { + const libPath = path.join(path.dirname(require.resolve("typescript")), "lib.es6.d.ts"); + const libSource = fs.readFileSync(libPath).toString(); + return ts.createSourceFile(filename, libSource, ts.ScriptTarget.Latest, false); + } + return undefined; + }, + + readFile: () => "", + + useCaseSensitiveFileNames: () => false, + // Don't write output + writeFile: (name, text, writeByteOrderMark) => null, + }; + + const program = ts.createProgram(["file.ts"], { luaTarget: target }, compilerHost); + return [program.getSourceFile("file.ts"), program.getTypeChecker()]; +} + +export function findFirstChild(node: ts.Node, predicate: (node: ts.Node) => boolean): ts.Node | undefined { + for (const child of node.getChildren()) { + if (predicate(child)) { + return child; + } + + const childChild = findFirstChild(child, predicate); + if (childChild !== undefined) { + return childChild; + } + } + return undefined; +} + const jsonlib = fs.readFileSync("test/src/json.lua") + "\n"; export const minimalTestLib = jsonlib; diff --git a/test/translation/ts/enumMembersOnly.ts b/test/translation/ts/enumMembersOnly.ts index 1032bea82..4a692f4ec 100644 --- a/test/translation/ts/enumMembersOnly.ts +++ b/test/translation/ts/enumMembersOnly.ts @@ -3,7 +3,7 @@ enum TestEnum { val1 = 0, val2 = 2, val3, - val4 = "bye" + val4 = "bye", } const a = TestEnum.val1; diff --git a/test/unit/tshelper.spec.ts b/test/unit/tshelper.spec.ts index 2a14d34f2..95878abc7 100644 --- a/test/unit/tshelper.spec.ts +++ b/test/unit/tshelper.spec.ts @@ -1,7 +1,10 @@ +import { Expect, Test, TestCase } from "alsatian"; +import { TSHelper as tsHelper } from "../../src/TSHelper"; + import * as ts from "typescript"; +import * as util from "../src/util"; -import { Expect, FocusTest, IgnoreTest, Test, TestCase } from "alsatian"; -import { TSHelper as tsEx } from "../../src/TSHelper"; +import { DecoratorKind } from "../../src/Decorator"; enum TestEnum { testA = 1, @@ -15,8 +18,8 @@ export class TSHelperTests { @TestCase(-1, "unknown") @TestCase(TestEnum.testA | TestEnum.testB, "unknown") @Test("EnumName") - public testEnumName(inp, expected) { - const result = tsEx.enumName(inp, TestEnum); + public testEnumName(inp, expected): void { + const result = tsHelper.enumName(inp, TestEnum); Expect(result).toEqual(expected); } @@ -26,14 +29,133 @@ export class TSHelperTests { @TestCase(TestEnum.testA | TestEnum.testC, ["testA", "testC"]) @TestCase(TestEnum.testA | TestEnum.testB | TestEnum.testC, ["testA", "testB", "testC"]) @Test("EnumNames") - public testEnumNames(inp, expected) { - const result = tsEx.enumNames(inp, TestEnum); + public testEnumNames(inp, expected): void { + const result = tsHelper.enumNames(inp, TestEnum); Expect(result).toEqual(expected); } @Test("IsFileModuleNull") - public isFileModuleNull() { - Expect(tsEx.isFileModule(null)).toEqual(false); + public isFileModuleNull(): void { + Expect(tsHelper.isFileModule(null)).toEqual(false); + } + + @Test("GetCustomDecorators single") + public GetCustomDecoratorsSingle(): void { + const source = `/** !CompileMembersOnly */ + enum TestEnum { + val1 = 0, + val2 = 2, + val3, + val4 = "bye", + } + + const a = TestEnum.val1;`; + + const [sourceFile, typeChecker] = util.parseTypeScript(source); + const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const enumType = typeChecker.getTypeAtLocation(identifier); + + const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); + + Expect(decorators.size).toBe(1); + Expect(decorators.has(DecoratorKind.CompileMembersOnly)).toBeTruthy(); + } + + @Test("GetCustomDecorators multiple") + public GetCustomDecoratorsMultiple(): void { + const source = `/** !CompileMembersOnly + * !Phantom */ + enum TestEnum { + val1 = 0, + val2 = 2, + val3, + val4 = "bye", + } + + const a = TestEnum.val1;`; + + const [sourceFile, typeChecker] = util.parseTypeScript(source); + const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const enumType = typeChecker.getTypeAtLocation(identifier); + + const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); + + Expect(decorators.size).toBe(2); + Expect(decorators.has(DecoratorKind.CompileMembersOnly)).toBeTruthy(); + Expect(decorators.has(DecoratorKind.Phantom)).toBeTruthy(); + } + + @Test("GetCustomDecorators single jsdoc") + public GetCustomDecoratorsSingleJSDoc(): void { + const source = `/** @CompileMembersOnly */ + enum TestEnum { + val1 = 0, + val2 = 2, + val3, + val4 = "bye", + } + + const a = TestEnum.val1;`; + + const [sourceFile, typeChecker] = util.parseTypeScript(source); + const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const enumType = typeChecker.getTypeAtLocation(identifier); + + const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); + + Expect(decorators.size).toBe(1); + Expect(decorators.has(DecoratorKind.CompileMembersOnly)).toBeTruthy(); + } + + @Test("GetCustomDecorators multiple jsdoc") + public GetCustomDecoratorsMultipleJSDoc(): void { + const source = `/** @Phantom + * @CompileMembersOnly */ + enum TestEnum { + val1 = 0, + val2 = 2, + val3, + val4 = "bye", + } + + const a = TestEnum.val1;`; + + const [sourceFile, typeChecker] = util.parseTypeScript(source); + const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const enumType = typeChecker.getTypeAtLocation(identifier); + + const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); + + Expect(decorators.size).toBe(2); + Expect(decorators.has(DecoratorKind.Phantom)).toBeTruthy(); + Expect(decorators.has(DecoratorKind.CompileMembersOnly)).toBeTruthy(); + } + + @Test("GetCustomDecorators multiple default jsdoc") + public GetCustomDecoratorsMultipleDefaultJSDoc(): void { + const source = `/** + * @description + * @param abc def + * @Phantom + * @CompileMembersOnly */ + enum TestEnum { + val1 = 0, + val2 = 2, + val3, + val4 = "bye", + } + + const a = TestEnum.val1;`; + + const [sourceFile, typeChecker] = util.parseTypeScript(source); + const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const enumType = typeChecker.getTypeAtLocation(identifier); + + const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); + + Expect(decorators.size).toBe(2); + Expect(decorators.has(DecoratorKind.Phantom)).toBeTruthy(); + Expect(decorators.has(DecoratorKind.CompileMembersOnly)).toBeTruthy(); } } From daa1e67ddd60c13d4319f5db0f27d749ff1983de Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 25 Nov 2018 18:40:05 +0100 Subject: [PATCH 36/59] Fixed invalid jsdoc failing a test --- test/unit/tshelper.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/unit/tshelper.spec.ts b/test/unit/tshelper.spec.ts index 95878abc7..bc5f5ff10 100644 --- a/test/unit/tshelper.spec.ts +++ b/test/unit/tshelper.spec.ts @@ -135,8 +135,7 @@ export class TSHelperTests { @Test("GetCustomDecorators multiple default jsdoc") public GetCustomDecoratorsMultipleDefaultJSDoc(): void { const source = `/** - * @description - * @param abc def + * @description abc * @Phantom * @CompileMembersOnly */ enum TestEnum { From 35381a98a423d32ee3c4b37d9305b1eaf39d52f2 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 26 Nov 2018 20:07:32 +0100 Subject: [PATCH 37/59] Added deprecation warning for ! decorators --- src/TSHelper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index aa58b5901..703c261ce 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -120,6 +120,8 @@ export class TSHelper { const dec = new Decorator(decStr.substr(1)); if (dec.kind !== undefined) { decMap.set(dec.kind, dec); + console.warn(`[Deprecated] Decorators with ! are being deprecated, ` + + `use @${decStr.substr(1)} instead`); } else { console.warn(`Encountered unknown decorator ${decStr}.`); } From 558682813c9433a85d579adf3afb0e24c1bcd717 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 26 Nov 2018 22:55:28 +0100 Subject: [PATCH 38/59] Refactored decorator creation, made decorators case-insensitive --- src/Decorator.ts | 30 +++++++++++++++++++++++------- src/TSHelper.ts | 9 +++++---- test/runner.ts | 4 ---- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Decorator.ts b/src/Decorator.ts index f8b92a070..870cc9344 100644 --- a/src/Decorator.ts +++ b/src/Decorator.ts @@ -1,14 +1,30 @@ export class Decorator { + + public static isValid(decoratorKindString: string): boolean { + return this.getDecoratorKind(decoratorKindString) !== undefined; + } + + public static getDecoratorKind(decoratorKindString: string): DecoratorKind { + switch (decoratorKindString.toLowerCase()) { + case "extension": return DecoratorKind.Extension; + case "metaextension": return DecoratorKind.MetaExtension; + case "customconstructor": return DecoratorKind.CustomConstructor; + case "compilemembersonly": return DecoratorKind.CompileMembersOnly; + case "pureabstract": return DecoratorKind.PureAbstract; + case "phantom": return DecoratorKind.Phantom; + case "tuplereturn": return DecoratorKind.TupleReturn; + case "noclassor": return DecoratorKind.NoClassOr; + } + + return undefined; + } + public kind: DecoratorKind; public args: string[]; - constructor(raw: string) { - let nameEnd = raw.indexOf(" "); - if (nameEnd === -1) { - nameEnd = raw.length; - } - this.kind = DecoratorKind[raw.substring(0, nameEnd)]; - this.args = raw.split(" ").slice(1); + constructor(name: string, args: string[]) { + this.kind = Decorator.getDecoratorKind(name); + this.args = args; } } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 703c261ce..5051083d5 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -117,8 +117,9 @@ export class TSHelper { const decMap = new Map(); decorators.forEach(decStr => { - const dec = new Decorator(decStr.substr(1)); - if (dec.kind !== undefined) { + const [decoratorName, ...decoratorArguments] = decStr.split(" "); + if (Decorator.isValid(decoratorName.substr(1))) { + const dec = new Decorator(decoratorName.substr(1), decoratorArguments); decMap.set(dec.kind, dec); console.warn(`[Deprecated] Decorators with ! are being deprecated, ` + `use @${decStr.substr(1)} instead`); @@ -128,8 +129,8 @@ export class TSHelper { }); type.symbol.getJsDocTags().forEach(tag => { - const dec = new Decorator(tag.name); - if (dec.kind !== undefined) { + if (Decorator.isValid(tag.name)) { + const dec = new Decorator(tag.name, tag.text.split(" ")); decMap.set(dec.kind, dec); } }); diff --git a/test/runner.ts b/test/runner.ts index 0d5d542fc..10b4a8a54 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -1,5 +1,4 @@ import { TestRunner, TestSet } from "alsatian"; -import { TapBark } from "tap-bark"; import * as fs from "fs"; import * as path from "path"; @@ -21,9 +20,6 @@ fs.copyFileSync( // setup the output testRunner.outputStream - // this will use alsatian's default output if you remove this - // you'll get TAP or you can add your favourite TAP reporter in it's place - .pipe(TapBark.create().getPipeable()) // pipe to the console .pipe(process.stdout); From ea2942b58df896b1a3e25c3fb24aa9d37b594785 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Tue, 27 Nov 2018 21:56:09 +0100 Subject: [PATCH 39/59] Fixed jsdoc tests --- src/TSHelper.ts | 2 +- test/runner.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 5051083d5..203181681 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -130,7 +130,7 @@ export class TSHelper { type.symbol.getJsDocTags().forEach(tag => { if (Decorator.isValid(tag.name)) { - const dec = new Decorator(tag.name, tag.text.split(" ")); + const dec = new Decorator(tag.name, tag.text ? tag.text.split(" ") : []); decMap.set(dec.kind, dec); } }); diff --git a/test/runner.ts b/test/runner.ts index 10b4a8a54..9e1ecd0d6 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -34,4 +34,7 @@ testRunner.run(testSet) .catch(error => { // Remove lualib bundle again fs.unlinkSync("lualib_bundle.lua"); + + console.error(error); + process.exit(1); }); From dcae417934c024332e0e10d423eb72d30192d0d0 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Tue, 27 Nov 2018 22:56:21 +0100 Subject: [PATCH 40/59] Fixed test runner not failing --- test/runner.ts | 55 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/test/runner.ts b/test/runner.ts index 9e1ecd0d6..b37dbe17f 100644 --- a/test/runner.ts +++ b/test/runner.ts @@ -1,4 +1,4 @@ -import { TestRunner, TestSet } from "alsatian"; +import { TestRunner, TestSet, TestOutcome } from "alsatian"; import * as fs from "fs"; import * as path from "path"; @@ -20,21 +20,44 @@ fs.copyFileSync( // setup the output testRunner.outputStream - // pipe to the console - .pipe(process.stdout); + // pipe to the console + .pipe(process.stdout); + +let success = 0; +let ignored = 0; +let run = 0; +testRunner.onTestComplete(test => { + run++; + + if (test.outcome === TestOutcome.Pass) { + success++; + } else if (test.outcome === TestOutcome.Skip) { + ignored++; + } +}); // run the test set testRunner.run(testSet) - // this will be called after all tests have been run - .then(result => { - // Remove lualib bundle again - fs.unlinkSync("lualib_bundle.lua"); - }) - // this will be called if there was a problem - .catch(error => { - // Remove lualib bundle again - fs.unlinkSync("lualib_bundle.lua"); - - console.error(error); - process.exit(1); - }); + // this will be called after all tests have been run + .then(result => { + // Remove lualib bundle again + fs.unlinkSync("lualib_bundle.lua"); + + const nonIgnoredTests = run - ignored; + const failedTests = nonIgnoredTests - success; + console.log(`Ignored ${ignored}/${run} tests.`); + console.log(`Failed ${failedTests}/${nonIgnoredTests} tests.`); + console.log(`Passed ${success}/${nonIgnoredTests} tests.`); + + if (failedTests > 0) { + process.exit(1); + } + }) + // this will be called if there was a problem + .catch(error => { + // Remove lualib bundle again + fs.unlinkSync("lualib_bundle.lua"); + + console.error(error); + process.exit(1); + }); From 3ac149ef2a96a16b1ebdb40125f292ae02077145 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 28 Nov 2018 06:28:04 -0700 Subject: [PATCH 41/59] recursing into interfaces during assignment validation --- src/Errors.ts | 18 ++++++++++--- src/Transpiler.ts | 64 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/Errors.ts b/src/Errors.ts index 37331ef36..00cdc91c0 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -69,9 +69,19 @@ 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 UnsupportedFunctionConversion = (node: ts.Node, name?: string) => { + if (name) { + return new TranspileError(`Unsupported conversion from method to function "${name}".`, node); + } else { + return new TranspileError(`Unsupported conversion from method to function.`, node); + } + } - public static UnsupportedMethodConversion = (node: ts.Node) => - new TranspileError(`Unsupported conversion from function to method.`, node) + public static UnsupportedMethodConversion = (node: ts.Node, name?: string) => { + if (name) { + return new TranspileError(`Unsupported conversion from function to method "${name}".`, node); + } else { + return new TranspileError(`Unsupported conversion from function to method.`, node); + } + } } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 86a7ad4e2..a18494cf6 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -82,6 +82,8 @@ export abstract class LuaTranspiler { public luaLibFeatureSet: Set; + private readonly typeValidationCache: Map> = new Map>(); + constructor(checker: ts.TypeChecker, options: CompilerOptions, sourceFile: ts.SourceFile) { this.indent = ""; this.checker = checker; @@ -948,6 +950,7 @@ export abstract class LuaTranspiler { this.validateAssignment(node.right, rightType, leftType); if (ts.isArrayLiteralExpression(node.left)) { + // Destructuring assignment const vars = node.left.elements.map(e => this.transpileExpression(e)).join(","); const vals = tsHelper.isTupleReturnCall(node.right, this.checker) ? rhs : this.transpileDestructingAssignmentValue(node.right); @@ -1409,26 +1412,59 @@ export abstract class LuaTranspiler { } } - public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type): void { + public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type, toName?: string): void { + if (toType === fromType) { + return; + } + 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) { + } + + // Use cache to avoid repeating check for same types (protects against infinite loop in recursive types) + let fromTypeCache = this.typeValidationCache.get(fromType); + if (fromTypeCache) { + if (fromTypeCache.has(toType)) { + return; + } + } else { + fromTypeCache = new Set(); + this.typeValidationCache.set(fromType, fromTypeCache); + } + fromTypeCache.add(toType); + + // Check function assignments + const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker); + const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker); + if (fromHasContext !== toHasContext) { + if (fromHasContext) { + throw TSTLErrors.UnsupportedFunctionConversion(node, toName); + } else { + throw TSTLErrors.UnsupportedMethodConversion(node, toName); + } + } + + 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); + this.validateAssignment(node, (fromType as ts.TypeReference).typeArguments[i], t, toName); }); - } else { - // Check function assignments - const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker); - const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker); - if (fromHasContext !== toHasContext) { - if (fromHasContext) { - throw TSTLErrors.UnsupportedFunctionConversion(node); - } else { - throw TSTLErrors.UnsupportedMethodConversion(node); + } + + if ((toType.flags & ts.TypeFlags.Object) !== 0 + && ((toType as ts.ObjectType).objectFlags & ts.ObjectFlags.ClassOrInterface) !== 0 + && toType.symbol && toType.symbol.members && fromType.symbol && fromType.symbol.members) { + // Recurse into interfaces + toType.symbol.members.forEach( + (toMember, memberName) => { + const fromMember = fromType.symbol.members.get(memberName); + const toMemberType = this.checker.getTypeOfSymbolAtLocation(toMember, node); + const fromMemberType = this.checker.getTypeOfSymbolAtLocation(fromMember, node); + this.validateAssignment(node, fromMemberType, toMemberType, + toName ? `${toName}.${memberName}` : memberName.toString()); } - } + ); } } @@ -1446,7 +1482,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(param, paramType, sigType); + this.validateAssignment(param, paramType, sigType, sig.parameters[i].name); parameters.push(this.transpileExpression(param)); } } else { From b9838b0b9c9357035f9186b3c35ce83a3f5b16c5 Mon Sep 17 00:00:00 2001 From: Perry van Wesel Date: Thu, 29 Nov 2018 18:12:00 +0100 Subject: [PATCH 42/59] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 903972572..c360d6dbd 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,8 @@ Large projects written in lua can become hard to maintain and make it easy to ma [![Build Status](https://travis-ci.org/Perryvw/TypescriptToLua.svg?branch=master)](https://travis-ci.org/Perryvw/TypescriptToLua) [![Build status](https://ci.appveyor.com/api/projects/status/github/perryvw/typescripttolua?branch=master&svg=true)](https://ci.appveyor.com/project/Perryvw/typescripttolua) [![Coverage](https://codecov.io/gh/perryvw/typescripttolua/branch/master/graph/badge.svg)](https://codecov.io/gh/perryvw/typescripttolua) -[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/TypescriptToLua/Lobby) -You can also find us on Discord: [![Discord](https://img.shields.io/discord/515854149821267971.svg)](https://discord.gg/BWAq58Y) +You can chat with us on Discord: [![Discord](https://img.shields.io/discord/515854149821267971.svg)](https://discord.gg/BWAq58Y) ## Documentation More detailed documentation and info on writing declarations can be found [on the wiki](https://github.com/Perryvw/TypescriptToLua/wiki). From 2913e930e6d342b49c5e7d594a9340d4ceb687ea Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 30 Nov 2018 08:22:45 -0700 Subject: [PATCH 43/59] fixes for issues with overloads using different context types --- src/Errors.ts | 12 ++++++++++++ src/TSHelper.ts | 35 ++++++++++++++++++++++++----------- src/Transpiler.ts | 29 ++++++++++++++++------------- 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/Errors.ts b/src/Errors.ts index 00cdc91c0..c1b188f48 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -84,4 +84,16 @@ export class TSTLErrors { return new TranspileError(`Unsupported conversion from function to method.`, node); } } + + public static UnsupportedOverloadAssignment = (node: ts.Node, name?: string) => { + if (name) { + return new TranspileError(`Unsupported assignment of mixed function/method overload to "${name}". ` + + `All overloads should either be functions or methods.`, + node); + } else { + return new TranspileError(`Unsupported assignment of mixed function/method overload. ` + + `All overloads should either be functions or methods.`, + node); + } + } } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index c1c0bae25..231a3cd7f 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -1,6 +1,13 @@ import * as ts from "typescript"; import { Decorator, DecoratorKind } from "./Decorator"; +export enum ContextType { + None, + Void, + NonVoid, + Mixed, +} + export class TSHelper { // Reverse lookup of enum key by value @@ -249,37 +256,43 @@ export class TSHelper { return [false, null, null]; } - public static isDeclarationWithContext(sigDecl: ts.SignatureDeclaration, checker: ts.TypeChecker): boolean { + public static getDeclarationContextType(sigDecl: ts.SignatureDeclaration, checker: ts.TypeChecker): ContextType { const thisArg = sigDecl.parameters.find(p => ts.isIdentifier(p.name) && p.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); if (thisArg) { // Explicit 'this' - return !thisArg.type || thisArg.type.kind !== ts.SyntaxKind.VoidKeyword; + return thisArg.type && thisArg.type.kind === ts.SyntaxKind.VoidKeyword + ? ContextType.Void : ContextType.NonVoid; } if ((ts.isMethodDeclaration(sigDecl) || ts.isMethodSignature(sigDecl)) && !(ts.getCombinedModifierFlags(sigDecl) & ts.ModifierFlags.Static)) { // Non-static method - return true; + return ContextType.NonVoid; } if ((ts.isPropertySignature(sigDecl.parent) || ts.isPropertyDeclaration(sigDecl.parent)) && !(ts.getCombinedModifierFlags(sigDecl.parent) & ts.ModifierFlags.Static)) { // Non-static lambda property - return true; + return ContextType.NonVoid; } - if (ts.isBinaryExpression(sigDecl.parent) - && this.isFunctionWithContext(checker.getTypeAtLocation(sigDecl.parent.left), checker)) { + if (ts.isBinaryExpression(sigDecl.parent)) { // Function expression: check type being assigned to - return true; + return this.getFunctionContextType(checker.getTypeAtLocation(sigDecl.parent.left), checker); } - return false; + return ContextType.Void; } - public static isFunctionWithContext(type: ts.Type, checker: ts.TypeChecker): boolean { + public static getFunctionContextType(type: ts.Type, checker: ts.TypeChecker): ContextType { const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); if (sigs.length === 0) { - return false; + return ContextType.None; } const sigDecls = sigs.map(s => s.getDeclaration()); - return sigDecls.every(s => this.isDeclarationWithContext(s, checker)); + const context = this.getDeclarationContextType(sigDecls[0], checker); + for (let i = 1; i < sigDecls.length; ++i) { + if (this.getDeclarationContextType(sigDecls[i], checker) !== context) { + return ContextType.Mixed; + } + } + return context; } } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index a18494cf6..533d24b4d 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -5,7 +5,7 @@ import * as ts from "typescript"; import { CompilerOptions } from "./CompilerOptions"; import { DecoratorKind } from "./Decorator"; import { TSTLErrors } from "./Errors"; -import { TSHelper as tsHelper } from "./TSHelper"; +import { ContextType, TSHelper as tsHelper } from "./TSHelper"; /* tslint:disable */ const packageJSON = require("../package.json"); @@ -1200,9 +1200,9 @@ export abstract class LuaTranspiler { return `${className}.__base.constructor(${params})`; } - const type = this.checker.getTypeAtLocation(node.expression); callPath = this.transpileExpression(node.expression); - if (tsHelper.isFunctionWithContext(type, this.checker) + const sigDecl = sig.getDeclaration(); + if (sigDecl && tsHelper.getDeclarationContextType(sigDecl, this.checker) === ContextType.NonVoid && !ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); @@ -1269,8 +1269,9 @@ export abstract class LuaTranspiler { params = this.transpileArguments(node.arguments, sig); return `(rawget(${expr}, ${params} )~=nil)`; } else { - const type = this.checker.getTypeAtLocation(node.expression); - const op = tsHelper.isFunctionWithContext(type, this.checker) ? ":" : "."; + const sigDecl = sig.getDeclaration(); + const op = !sigDecl || tsHelper.getDeclarationContextType(sigDecl, this.checker) !== ContextType.Void + ? ":" : "."; callPath = `${this.transpileExpression(node.expression.expression)}${op}${name}`; params = this.transpileArguments(node.arguments, sig); return `${callPath}(${params})`; @@ -1394,7 +1395,7 @@ 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)) { + if (tsHelper.getFunctionContextType(callerType, this.checker) === ContextType.Void) { throw TSTLErrors.UnsupportedMethodConversion(node); } const params = this.transpileArguments(node.arguments); @@ -1435,10 +1436,12 @@ export abstract class LuaTranspiler { fromTypeCache.add(toType); // Check function assignments - const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker); - const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker); - if (fromHasContext !== toHasContext) { - if (fromHasContext) { + const fromContext = tsHelper.getFunctionContextType(fromType, this.checker); + const toContext = tsHelper.getFunctionContextType(toType, this.checker); + if (fromContext === ContextType.Mixed || toContext === ContextType.Mixed) { + throw TSTLErrors.UnsupportedOverloadAssignment(node, toName); + } else if (fromContext !== toContext) { + if (toContext === ContextType.Void) { throw TSTLErrors.UnsupportedFunctionConversion(node, toName); } else { throw TSTLErrors.UnsupportedMethodConversion(node, toName); @@ -1719,7 +1722,7 @@ export abstract class LuaTranspiler { const methodName = this.transpileIdentifier(node.name); const type = this.checker.getTypeAtLocation(node); - const context = tsHelper.isFunctionWithContext(type, this.checker) ? "self" : null; + const context = tsHelper.getFunctionContextType(type, this.checker) !== ContextType.Void ? "self" : null; const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); let prefix = this.accessPrefix(node); @@ -1805,7 +1808,7 @@ export abstract class LuaTranspiler { } const type = this.checker.getTypeAtLocation(node); - const context = tsHelper.isFunctionWithContext(type, this.checker) ? "self" : null; + const context = tsHelper.getFunctionContextType(type, this.checker) !== ContextType.Void ? "self" : null; const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); // Build function header @@ -2078,7 +2081,7 @@ 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); + const hasContext = tsHelper.getFunctionContextType(type, this.checker) !== ContextType.Void; // Build parameter string const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, hasContext ? context : null); let result = `function(${paramNames.join(",")})\n`; From 3d994fa77664528a2706a903631b4255f3f3c185 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 1 Dec 2018 22:45:52 +0100 Subject: [PATCH 44/59] Fixed casing and symbol of decorators to be consistent with the new convention --- src/lualib/StringReplace.ts | 2 +- test/translation/ts/assignments.ts | 2 +- test/translation/ts/classExtension1.ts | 2 +- test/translation/ts/classExtension2.ts | 4 +-- test/translation/ts/classExtension3.ts | 4 +-- test/translation/ts/classExtension4.ts | 2 +- test/translation/ts/classPureAbstract.ts | 2 +- test/translation/ts/enumMembersOnly.ts | 2 +- test/translation/ts/namespacePhantom.ts | 2 +- test/translation/ts/tupleReturn.ts | 8 +++--- test/unit/assignments.spec.ts | 16 +++++------ test/unit/decoratorCustomConstructor.spec.ts | 4 +-- test/unit/decoratorMetaExtension.spec.ts | 6 ++-- test/unit/expressions.spec.ts | 2 +- test/unit/tshelper.spec.ts | 14 ++++----- test/unit/tuples.spec.ts | 30 ++++++++++---------- 16 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/lualib/StringReplace.ts b/src/lualib/StringReplace.ts index 7a5df979c..7324db680 100644 --- a/src/lualib/StringReplace.ts +++ b/src/lualib/StringReplace.ts @@ -1,5 +1,5 @@ declare namespace string { - /** !TupleReturn */ + /** @tupleReturn */ function gsub(source: string, searchValue: string, replaceValue: string): [string, number]; } diff --git a/test/translation/ts/assignments.ts b/test/translation/ts/assignments.ts index a8e51e59a..df4fb91db 100644 --- a/test/translation/ts/assignments.ts +++ b/test/translation/ts/assignments.ts @@ -10,7 +10,7 @@ declare function getIndex(): number; declare let xTup: [number, number]; declare let yTup: [number, number]; declare function getTup(): [number, number]; -/** !TupleReturn */ +/** @tupleReturn */ declare function getTupRet(): [number, number]; x = y; x = obj.prop; diff --git a/test/translation/ts/classExtension1.ts b/test/translation/ts/classExtension1.ts index cf293483d..5df5bdeac 100644 --- a/test/translation/ts/classExtension1.ts +++ b/test/translation/ts/classExtension1.ts @@ -1,4 +1,4 @@ -/** !Extension */ +/** @extension */ class MyClass { public myFunction() {} } diff --git a/test/translation/ts/classExtension2.ts b/test/translation/ts/classExtension2.ts index 109160d97..158f16422 100644 --- a/test/translation/ts/classExtension2.ts +++ b/test/translation/ts/classExtension2.ts @@ -1,8 +1,8 @@ -/** !Extension */ +/** @extension */ class TestClass { } -/** !Extension */ +/** @extension */ class MyClass extends TestClass { public myFunction() {} } diff --git a/test/translation/ts/classExtension3.ts b/test/translation/ts/classExtension3.ts index 287c46d3f..3fa37f05e 100644 --- a/test/translation/ts/classExtension3.ts +++ b/test/translation/ts/classExtension3.ts @@ -1,9 +1,9 @@ -/** !Extension RenamedTestClass */ +/** @extension RenamedTestClass */ class TestClass { public myFunction() {} } -/** !Extension RenamedMyClass */ +/** @extension RenamedMyClass */ class MyClass extends TestClass { public myFunction() {} } diff --git a/test/translation/ts/classExtension4.ts b/test/translation/ts/classExtension4.ts index 2183c5610..8cb996079 100644 --- a/test/translation/ts/classExtension4.ts +++ b/test/translation/ts/classExtension4.ts @@ -1,4 +1,4 @@ -/** !Extension */ +/** @extension */ class MyClass { public test: string = "test"; private testP: string = "testP"; diff --git a/test/translation/ts/classPureAbstract.ts b/test/translation/ts/classPureAbstract.ts index c511f0de2..178c23b59 100644 --- a/test/translation/ts/classPureAbstract.ts +++ b/test/translation/ts/classPureAbstract.ts @@ -1,3 +1,3 @@ -/** !PureAbstract */ +/** @pureAbstract */ declare class ClassA {} class ClassB extends ClassA {} \ No newline at end of file diff --git a/test/translation/ts/enumMembersOnly.ts b/test/translation/ts/enumMembersOnly.ts index 4a692f4ec..9d384dc6c 100644 --- a/test/translation/ts/enumMembersOnly.ts +++ b/test/translation/ts/enumMembersOnly.ts @@ -1,4 +1,4 @@ -/** !CompileMembersOnly */ +/** @compileMembersOnly */ enum TestEnum { val1 = 0, val2 = 2, diff --git a/test/translation/ts/namespacePhantom.ts b/test/translation/ts/namespacePhantom.ts index b107d3387..c99b88e8d 100644 --- a/test/translation/ts/namespacePhantom.ts +++ b/test/translation/ts/namespacePhantom.ts @@ -1,4 +1,4 @@ -/** !Phantom */ +/** @phantom */ namespace myNamespace { function nsMember() {} } \ No newline at end of file diff --git a/test/translation/ts/tupleReturn.ts b/test/translation/ts/tupleReturn.ts index 86fd32e97..0d2936071 100644 --- a/test/translation/ts/tupleReturn.ts +++ b/test/translation/ts/tupleReturn.ts @@ -1,4 +1,4 @@ -/** !TupleReturn */ +/** @tupleReturn */ function tupleReturn(): [number, string] { return [0, "foobar"]; } @@ -16,19 +16,19 @@ e = tupleReturn(); f = noTupleReturn(); foo(tupleReturn()); foo(noTupleReturn()); -/** !TupleReturn */ +/** @tupleReturn */ function tupleReturnFromVar(): [number, string] { const r: [number, string] = [1, "baz"]; return r; } -/** !TupleReturn */ +/** @tupleReturn */ function tupleReturnForward(): [number, string] { return tupleReturn(); } function tupleNoForward(): [number, string] { return tupleReturn(); } -/** !TupleReturn */ +/** @tupleReturn */ function tupleReturnUnpack(): [number, string] { return tupleNoForward(); } diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index e3ccb60c0..5ba09e62c 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -76,7 +76,7 @@ export class AssignmentTests { @Test("TupleReturn assignment") public tupleReturnFunction(): void { - const code = `/** !TupleReturn */\n` + const code = `/** @tupleReturn */\n` + `declare function abc() { return [1,2,3]; }\n` + `let [a,b] = abc();`; @@ -86,7 +86,7 @@ export class AssignmentTests { @Test("TupleReturn Single assignment") public tupleReturnSingleAssignment(): void { - const code = `/** !TupleReturn */\n` + const code = `/** @tupleReturn */\n` + `declare function abc(): [number, string]; }\n` + `let a = abc();` + `a = abc();`; @@ -98,7 +98,7 @@ export class AssignmentTests { @Test("TupleReturn interface assignment") public tupleReturnInterface(): void { const code = `interface def {\n` - + `/** !TupleReturn */\n` + + `/** @tupleReturn */\n` + `abc();\n` + `} declare const jkl : def;\n` + `let [a,b] = jkl.abc();`; @@ -110,7 +110,7 @@ export class AssignmentTests { @Test("TupleReturn namespace assignment") public tupleReturnNameSpace(): void { const code = `declare namespace def {\n` - + `/** !TupleReturn */\n` + + `/** @tupleReturn */\n` + `function abc() {}\n` + `}\n` + `let [a,b] = def.abc();`; @@ -122,7 +122,7 @@ export class AssignmentTests { @Test("TupleReturn method assignment") public tupleReturnMethod(): void { const code = `declare class def {\n` - + `/** !TupleReturn */\n` + + `/** @tupleReturn */\n` + `abc() { return [1,2,3]; }\n` + `} const jkl = new def();\n` + `let [a,b] = jkl.abc();`; @@ -133,7 +133,7 @@ export class AssignmentTests { @Test("TupleReturn functional") public tupleReturnFunctional(): void { - const code = `/** !TupleReturn */ + const code = `/** @tupleReturn */ function abc(): [number, string] { return [3, "a"]; } const [a, b] = abc(); return b + a;`; @@ -147,7 +147,7 @@ export class AssignmentTests { @Test("TupleReturn single") public tupleReturnSingle(): void { - const code = `/** !TupleReturn */ + const code = `/** @tupleReturn */ function abc(): [number, string] { return [3, "a"]; } const res = abc(); return res.length`; @@ -161,7 +161,7 @@ export class AssignmentTests { @Test("TupleReturn in expression") public tupleReturnInExpression(): void { - const code = `/** !TupleReturn */ + const code = `/** @tupleReturn */ function abc(): [number, string] { return [3, "a"]; } return abc()[1] + abc()[0];`; diff --git a/test/unit/decoratorCustomConstructor.spec.ts b/test/unit/decoratorCustomConstructor.spec.ts index b9e4c7242..5433b2f4f 100644 --- a/test/unit/decoratorCustomConstructor.spec.ts +++ b/test/unit/decoratorCustomConstructor.spec.ts @@ -9,7 +9,7 @@ export class DecoratorCustomConstructor { public customCreate(): void { // Transpile const lua = util.transpileString( - `/** !CustomConstructor Point2DCreate */ + `/** @customConstructor Point2DCreate */ class Point2D { x: number; y: number; @@ -29,7 +29,7 @@ export class DecoratorCustomConstructor { public incorrectUsage(): void { Expect(() => { util.transpileString( - `/** !CustomConstructor */ + `/** @customConstructor */ class Point2D { x: number; y: number; diff --git a/test/unit/decoratorMetaExtension.spec.ts b/test/unit/decoratorMetaExtension.spec.ts index d29f51467..eddba794b 100644 --- a/test/unit/decoratorMetaExtension.spec.ts +++ b/test/unit/decoratorMetaExtension.spec.ts @@ -14,7 +14,7 @@ export class DecoratorMetaExtension { declare namespace debug { function getregistry(): any; } - /** !MetaExtension */ + /** @metaExtension */ class LoadedExt extends _LOADED { public static test() { return 5; @@ -33,7 +33,7 @@ export class DecoratorMetaExtension { Expect(() => { util.transpileString( ` - /** !MetaExtension */ + /** @metaExtension */ class LoadedExt { public static test() { return 5; @@ -51,7 +51,7 @@ export class DecoratorMetaExtension { util.transpileString( ` declare class _LOADED; - /** !MetaExtension */ + /** @metaExtension */ class Ext extends _LOADED { } const e = new Ext(); diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 2fa26da68..7fbea1209 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -324,7 +324,7 @@ export class ExpressionTests { `let x: [string, string] = ["x0", "x1"]; let y: [string, string] = ["y0", "y1"]; function t(): [string, string] { return ["t0", "t1"] }; - /** !TupleReturn */ + /** @tupleReturn */ function tr(): [string, string] { return ["tr0", "tr1"] }; const r = ${expression}; return \`\${r[0]},\${r[1]}\``); diff --git a/test/unit/tshelper.spec.ts b/test/unit/tshelper.spec.ts index bc5f5ff10..774babd93 100644 --- a/test/unit/tshelper.spec.ts +++ b/test/unit/tshelper.spec.ts @@ -42,7 +42,7 @@ export class TSHelperTests { @Test("GetCustomDecorators single") public GetCustomDecoratorsSingle(): void { - const source = `/** !CompileMembersOnly */ + const source = `/** @compileMembersOnly */ enum TestEnum { val1 = 0, val2 = 2, @@ -64,8 +64,8 @@ export class TSHelperTests { @Test("GetCustomDecorators multiple") public GetCustomDecoratorsMultiple(): void { - const source = `/** !CompileMembersOnly - * !Phantom */ + const source = `/** @compileMembersOnly + * @Phantom */ enum TestEnum { val1 = 0, val2 = 2, @@ -88,7 +88,7 @@ export class TSHelperTests { @Test("GetCustomDecorators single jsdoc") public GetCustomDecoratorsSingleJSDoc(): void { - const source = `/** @CompileMembersOnly */ + const source = `/** @compileMembersOnly */ enum TestEnum { val1 = 0, val2 = 2, @@ -110,7 +110,7 @@ export class TSHelperTests { @Test("GetCustomDecorators multiple jsdoc") public GetCustomDecoratorsMultipleJSDoc(): void { - const source = `/** @Phantom + const source = `/** @phantom * @CompileMembersOnly */ enum TestEnum { val1 = 0, @@ -136,8 +136,8 @@ export class TSHelperTests { public GetCustomDecoratorsMultipleDefaultJSDoc(): void { const source = `/** * @description abc - * @Phantom - * @CompileMembersOnly */ + * @phantom + * @compileMembersOnly */ enum TestEnum { val1 = 0, val2 = 2, diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts index 067776bd5..16bef9922 100644 --- a/test/unit/tuples.spec.ts +++ b/test/unit/tuples.spec.ts @@ -82,7 +82,7 @@ export class TupleTests { public tupleDestruct(): void { // Transpile const lua = util.transpileString( - `function tuple(): [number, number, number] { return [3,5,1]; }\n + `function tuple(): [number, number, number] { return [3,5,1]; } const [a,b,c] = tuple(); return b;` ); @@ -113,8 +113,8 @@ export class TupleTests { public tupleReturnAccess(): void { // Transpile const lua = util.transpileString( - `/** !TupleReturn */\n - function tuple(): [number, number, number] { return [3,5,1]; }\n + `/** @tupleReturn */ + function tuple(): [number, number, number] { return [3,5,1]; } return tuple()[2];` ); @@ -129,8 +129,8 @@ export class TupleTests { public tupleReturnDestructDeclaration(): void { // Transpile const lua = util.transpileString( - `/** !TupleReturn */\n - function tuple(): [number, number, number] { return [3,5,1]; }\n + `/** @tupleReturn */ + function tuple(): [number, number, number] { return [3,5,1]; } const [a,b,c] = tuple(); return b;` ); @@ -146,8 +146,8 @@ export class TupleTests { public tupleReturnDestructAssignment(): void { // Transpile const lua = util.transpileString( - `/** !TupleReturn */\n - function tuple(): [number, number] { return [3,6]; }\n + `/** @tupleReturn */ + function tuple(): [number, number] { return [3,6]; } const [a,b] = [1,2]; [b,a] = tuple(); return a - b;` @@ -164,10 +164,10 @@ export class TupleTests { public tupleStaticMethodReturnDestruct(): void { // Transpile const lua = util.transpileString( - `class Test {\n - /** !TupleReturn */\n - static tuple(): [number, number, number] { return [3,5,1]; }\n - }\n + `class Test { + /** @tupleReturn */ + static tuple(): [number, number, number] { return [3,5,1]; } + } const [a,b,c] = Test.tuple(); return b;` ); @@ -183,10 +183,10 @@ export class TupleTests { public tupleMethodNonStaticReturnDestruct(): void { // Transpile const lua = util.transpileString( - `class Test {\n - /** !TupleReturn */\n - tuple(): [number, number, number] { return [3,5,1]; }\n - }\n + `class Test { + /** @tupleReturn */ + tuple(): [number, number, number] { return [3,5,1]; } + } const t = new Test(); const [a,b,c] = t.tuple(); return b;` From b79756e93c342fe459d640b0b1ca43d33f7569be Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 2 Dec 2018 06:07:51 -0700 Subject: [PATCH 45/59] less-lazy variable naming and improved error message --- src/Errors.ts | 4 ++-- src/TSHelper.ts | 32 +++++++++++++++++--------------- src/Transpiler.ts | 26 ++++++++++++++------------ 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/Errors.ts b/src/Errors.ts index c1b188f48..b9fa04157 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -88,11 +88,11 @@ export class TSTLErrors { public static UnsupportedOverloadAssignment = (node: ts.Node, name?: string) => { if (name) { return new TranspileError(`Unsupported assignment of mixed function/method overload to "${name}". ` - + `All overloads should either be functions or methods.`, + + `Overloads should either be all functions or all methods, but not both.`, node); } else { return new TranspileError(`Unsupported assignment of mixed function/method overload. ` - + `All overloads should either be functions or methods.`, + + `Overloads should either be all functions or all methods, but not both.`, node); } } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 231a3cd7f..3e3b8e6e9 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -256,40 +256,42 @@ export class TSHelper { return [false, null, null]; } - public static getDeclarationContextType(sigDecl: ts.SignatureDeclaration, checker: ts.TypeChecker): ContextType { - const thisArg = sigDecl.parameters.find(p => ts.isIdentifier(p.name) - && p.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); + public static getDeclarationContextType(signatureDeclaration: ts.SignatureDeclaration, + checker: ts.TypeChecker): ContextType { + const thisArg = signatureDeclaration.parameters.find( + param => ts.isIdentifier(param.name) && param.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); if (thisArg) { // Explicit 'this' return thisArg.type && thisArg.type.kind === ts.SyntaxKind.VoidKeyword ? ContextType.Void : ContextType.NonVoid; } - if ((ts.isMethodDeclaration(sigDecl) || ts.isMethodSignature(sigDecl)) - && !(ts.getCombinedModifierFlags(sigDecl) & ts.ModifierFlags.Static)) { + if ((ts.isMethodDeclaration(signatureDeclaration) || ts.isMethodSignature(signatureDeclaration)) + && !(ts.getCombinedModifierFlags(signatureDeclaration) & ts.ModifierFlags.Static)) { // Non-static method return ContextType.NonVoid; } - if ((ts.isPropertySignature(sigDecl.parent) || ts.isPropertyDeclaration(sigDecl.parent)) - && !(ts.getCombinedModifierFlags(sigDecl.parent) & ts.ModifierFlags.Static)) { + if ((ts.isPropertySignature(signatureDeclaration.parent) + || ts.isPropertyDeclaration(signatureDeclaration.parent)) + && !(ts.getCombinedModifierFlags(signatureDeclaration.parent) & ts.ModifierFlags.Static)) { // Non-static lambda property return ContextType.NonVoid; } - if (ts.isBinaryExpression(sigDecl.parent)) { + if (ts.isBinaryExpression(signatureDeclaration.parent)) { // Function expression: check type being assigned to - return this.getFunctionContextType(checker.getTypeAtLocation(sigDecl.parent.left), checker); + return this.getFunctionContextType(checker.getTypeAtLocation(signatureDeclaration.parent.left), checker); } return ContextType.Void; } public static getFunctionContextType(type: ts.Type, checker: ts.TypeChecker): ContextType { - const sigs = checker.getSignaturesOfType(type, ts.SignatureKind.Call); - if (sigs.length === 0) { + const signatures = checker.getSignaturesOfType(type, ts.SignatureKind.Call); + if (signatures.length === 0) { return ContextType.None; } - const sigDecls = sigs.map(s => s.getDeclaration()); - const context = this.getDeclarationContextType(sigDecls[0], checker); - for (let i = 1; i < sigDecls.length; ++i) { - if (this.getDeclarationContextType(sigDecls[i], checker) !== context) { + const signatureDeclataions = signatures.map(sig => sig.getDeclaration()); + const context = this.getDeclarationContextType(signatureDeclataions[0], checker); + for (let i = 1; i < signatureDeclataions.length; ++i) { + if (this.getDeclarationContextType(signatureDeclataions[i], checker) !== context) { return ContextType.Mixed; } } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 533d24b4d..19adee73f 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1190,25 +1190,26 @@ export abstract class LuaTranspiler { ? `({ ${result} })` : result; } - const sig = this.checker.getResolvedSignature(node); + const signature = this.checker.getResolvedSignature(node); // Handle super calls properly if (node.expression.kind === ts.SyntaxKind.SuperKeyword) { - params = this.transpileArguments(node.arguments, sig, + params = this.transpileArguments(node.arguments, signature, 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 sigDecl = sig.getDeclaration(); - if (sigDecl && tsHelper.getDeclarationContextType(sigDecl, this.checker) === ContextType.NonVoid + const signatureDeclaration = signature.getDeclaration(); + if (signatureDeclaration + && tsHelper.getDeclarationContextType(signatureDeclaration, this.checker) === ContextType.NonVoid && !ts.isPropertyAccessExpression(node.expression) && !ts.isElementAccessExpression(node.expression)) { const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G"); - params = this.transpileArguments(node.arguments, sig, context); + params = this.transpileArguments(node.arguments, signature, context); } else { - params = this.transpileArguments(node.arguments, sig); + params = this.transpileArguments(node.arguments, signature); } return isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment && returnValueIsUsed ? `({ ${callPath}(${params}) })` : `${callPath}(${params})`; @@ -1251,12 +1252,12 @@ export abstract class LuaTranspiler { return this.transpileFunctionCallExpression(node); } - const sig = this.checker.getResolvedSignature(node); + const signature = this.checker.getResolvedSignature(node); // 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, sig, + params = this.transpileArguments(node.arguments, signature, ts.createNode(ts.SyntaxKind.ThisKeyword) as ts.Expression); return `${this.transpileExpression(node.expression)}(${params})`; } else { @@ -1266,14 +1267,15 @@ 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, sig); + params = this.transpileArguments(node.arguments, signature); return `(rawget(${expr}, ${params} )~=nil)`; } else { - const sigDecl = sig.getDeclaration(); - const op = !sigDecl || tsHelper.getDeclarationContextType(sigDecl, this.checker) !== ContextType.Void + const signatureDeclaration = signature.getDeclaration(); + const op = !signatureDeclaration + || tsHelper.getDeclarationContextType(signatureDeclaration, this.checker) !== ContextType.Void ? ":" : "."; callPath = `${this.transpileExpression(node.expression.expression)}${op}${name}`; - params = this.transpileArguments(node.arguments, sig); + params = this.transpileArguments(node.arguments, signature); return `${callPath}(${params})`; } } From ddc5a51190e03ecc876e89c613eb5a3bf03a5e13 Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Sun, 2 Dec 2018 06:33:34 -0700 Subject: [PATCH 46/59] removing assignments translation test (#296) --- test/translation/lua/assignments.lua | 143 ------------------------ test/translation/ts/assignments.ts | 157 --------------------------- 2 files changed, 300 deletions(-) delete mode 100644 test/translation/lua/assignments.lua delete mode 100644 test/translation/ts/assignments.ts diff --git a/test/translation/lua/assignments.lua b/test/translation/lua/assignments.lua deleted file mode 100644 index 6f4e5f9bd..000000000 --- a/test/translation/lua/assignments.lua +++ /dev/null @@ -1,143 +0,0 @@ -x = y; -x = obj.prop; -x = arr[(0)+1]; -x = ((function() local __TS_tmp = obj.prop; y = __TS_tmp; return __TS_tmp end)()); -x = obj.prop; -obj.prop = x; -arr[(0)+1] = x; -obj.prop = arr[(0)+1]; -obj.prop = ((function() arr[(0)+1] = x; return x end)()); -xTup = getTup(); -xTup = ({ getTupRet() }); -do local __TS_tmp0,__TS_tmp1 = table.unpack(getTup()); xTup[(1)+1],xTup[(0)+1] = __TS_tmp0,__TS_tmp1 end; -do local __TS_tmp0,__TS_tmp1 = getTupRet(); xTup[(1)+1],xTup[(0)+1] = __TS_tmp0,__TS_tmp1 end; -xTup = {yTup[(1)+1],yTup[(0)+1]}; -do local __TS_tmp0,__TS_tmp1 = table.unpack({yTup[(1)+1],yTup[(0)+1]}); xTup[(0)+1],xTup[(1)+1] = __TS_tmp0,__TS_tmp1 end; -x = (x+1); -x = (x+1); -x = (x-1); -x = (x-1); -x = (x+y); -x = (x-y); -x = (x*y); -y = (y/x); -y = (y%x); -y = (y^x); -x = (x | y); -x = (x & y); -x = (x ~ y); -x = (x << y); -x = (x >> y); -obj.prop = (obj.prop+1); -obj.prop = (obj.prop+1); -obj.prop = (obj.prop-1); -obj.prop = (obj.prop-1); -obj.prop = (obj.prop+arr[(0)+1]); -obj.prop = (obj.prop-arr[(0)+1]); -obj.prop = (obj.prop*arr[(0)+1]); -arr[(0)+1] = (arr[(0)+1]/obj.prop); -arr[(0)+1] = (arr[(0)+1]%obj.prop); -arr[(0)+1] = (arr[(0)+1]^obj.prop); -obj.prop = (obj.prop | arr[(0)+1]); -obj.prop = (obj.prop & arr[(0)+1]); -obj.prop = (obj.prop ~ arr[(0)+1]); -obj.prop = (obj.prop << arr[(0)+1]); -obj.prop = (obj.prop >> arr[(0)+1]); -do local __TS_obj, __TS_index = arr, (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = arr, (getIndex())+1; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp-(1)); end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]-(1)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp-(1)); end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]+(getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]-(getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]*(getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]/(getObj().prop)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]%(getObj().prop)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]^(getObj().prop)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] | (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] & (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] ~ (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] << (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] >> (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; end; -z = ((function() x = y; return y end)()); -z = ((function() obj.prop = x; return x end)()); -z = ((function(o, i, v) o[i] = v; return v end)(getObj(), "prop", x)); -z = ((function() arr[(0)+1] = x; return x end)()); -z = ((function(o, i, v) o[i] = v; return v end)(getArr(), (getIndex())+1, x)); -z = ((function() local __TS_tmp = obj.prop; x = __TS_tmp; return __TS_tmp end)()); -z = ((function() local __TS_tmp = getObj().prop; x = __TS_tmp; return __TS_tmp end)()); -z = ((function() local __TS_tmp = arr[(0)+1]; x = __TS_tmp; return __TS_tmp end)()); -z = ((function() local __TS_tmp = arr[(getIndex())+1]; x = __TS_tmp; return __TS_tmp end)()); -z = ((function() local __TS_tmp = getArr()[(getIndex())+1]; x = __TS_tmp; return __TS_tmp end)()); -z = (function() x = (x+1); return x end)(); -z = (function() local __TS_tmp = x; x = (__TS_tmp+1); return __TS_tmp end)(); -z = (function() x = (x-1); return x end)(); -z = (function() local __TS_tmp = x; x = (__TS_tmp-1); return __TS_tmp end)(); -z = (function() x = (x+y); return x end)(); -z = (function() x = (x-y); return x end)(); -z = (function() x = (x*y); return x end)(); -z = (function() y = (y/x); return y end)(); -z = (function() y = (y%x); return y end)(); -z = (function() y = (y^x); return y end)(); -z = (function() x = (x | y); return x end)(); -z = (function() x = (x & y); return x end)(); -z = (function() x = (x ~ y); return x end)(); -z = (function() x = (x << y); return x end)(); -z = (function() x = (x >> y); return x end)(); -z = (x+((function() y = (y+7); return y end)())); -z = (x+((function() y = (y+7); return y end)())); -z = ((function() local __TS_tmp = x; x = (__TS_tmp+1); return __TS_tmp end)()+((function() y = (y+7); return y end)())); -z = (function() local __TS_tmp = (obj.prop+1); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = obj.prop; obj.prop = (__TS_tmp+1); return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop-1); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = obj.prop; obj.prop = (__TS_tmp-1); return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop+arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop-arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop*arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (arr[(0)+1]/obj.prop); arr[(0)+1] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (arr[(0)+1]%obj.prop); arr[(0)+1] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (arr[(0)+1]^obj.prop); arr[(0)+1] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop | arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop & arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop ~ arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop << arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_tmp = (obj.prop >> arr[(0)+1]); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = (obj.prop+((function() local __TS_tmp = (arr[(0)+1]+7); arr[(0)+1] = __TS_tmp; return __TS_tmp end)())); -z = (function() local __TS_tmp = (obj.prop+((function() local __TS_tmp = (arr[(0)+1]+7); arr[(0)+1] = __TS_tmp; return __TS_tmp end)())); obj.prop = __TS_tmp; return __TS_tmp end)(); -z = ((function() local __TS_tmp = obj.prop; obj.prop = (__TS_tmp+1); return __TS_tmp end)()+((function() local __TS_tmp = (arr[(0)+1]+7); arr[(0)+1] = __TS_tmp; return __TS_tmp end)())); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]-(1)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp-(1)); return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]+(getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]-(getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]*(getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]/(getObj().prop)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]%(getObj().prop)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]^(getObj().prop)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] | (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] & (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] ~ (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] << (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index] >> (getArr()[(getIndex())+1])); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (getObj().prop+((function() local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(7)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)())); -z = (function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = (__TS_obj[__TS_index]+(((function() local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(7)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)()))); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = ((function() local __TS_obj, __TS_index = getObj(), "prop"; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); return __TS_tmp end)()+((function() local __TS_obj, __TS_index = getArr(), (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(7)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)())); -do local __TS_obj, __TS_index = getObj().arr, (0)+1; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = getObj().arr, (0)+1; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); end; -do local __TS_obj, __TS_index = getObj().arr, (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(13)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = arr2[(0)+1], (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = arr2[(0)+1], (getIndex())+1; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); end; -do local __TS_obj, __TS_index = arr2[(getIndex())+1], (0)+1; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; end; -do local __TS_obj, __TS_index = arr2[(getIndex())+1], (0)+1; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); end; -z = (function() local __TS_obj, __TS_index = getObj().arr, (0)+1; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj().arr, (0)+1; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = getObj().arr, (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(13)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = arr2[(0)+1], (getIndex())+1; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = arr2[(0)+1], (getIndex())+1; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = arr2[(getIndex())+1], (0)+1; local __TS_tmp = (__TS_obj[__TS_index]+(1)); __TS_obj[__TS_index] = __TS_tmp; return __TS_tmp end)(); -z = (function() local __TS_obj, __TS_index = arr2[(getIndex())+1], (0)+1; local __TS_tmp = __TS_obj[__TS_index]; __TS_obj[__TS_index] = (__TS_tmp+(1)); return __TS_tmp end)(); -z = ((function() local __TS_tmp = arr2[(0)+1][(getIndex())+1]; x = __TS_tmp; return __TS_tmp end)()); -z = ((function(o, i, v) o[i] = v; return v end)(getObj().arr, (0)+1, x)); -z = ((function(o, i, v) o[i] = v; return v end)(getObj().arr, (0)+1, arr2[(0)+1][(getIndex())+1])); diff --git a/test/translation/ts/assignments.ts b/test/translation/ts/assignments.ts deleted file mode 100644 index df4fb91db..000000000 --- a/test/translation/ts/assignments.ts +++ /dev/null @@ -1,157 +0,0 @@ -declare let x: number; -declare let y: number; -declare let z: number; -declare let obj: {prop: number, arr: number[]}; -declare function getObj(): typeof obj; -declare let arr: number[]; -declare let arr2: number[][]; -declare function getArr(): typeof arr; -declare function getIndex(): number; -declare let xTup: [number, number]; -declare let yTup: [number, number]; -declare function getTup(): [number, number]; -/** @tupleReturn */ -declare function getTupRet(): [number, number]; -x = y; -x = obj.prop; -x = arr[0]; -x = y = obj.prop; -x = obj.prop; -obj.prop = x; -arr[0] = x; -obj.prop = arr[0]; -obj.prop = arr[0] = x; -xTup = getTup(); -xTup = getTupRet(); -[xTup[1], xTup[0]] = getTup(); -[xTup[1], xTup[0]] = getTupRet(); -xTup = [yTup[1], yTup[0]]; -[xTup[0], xTup[1]] = [yTup[1], yTup[0]]; -++x; -x++; ---x; -x--; -x += y; -x -= y; -x *= y; -y /= x; -y %= x; -y **= x; -x |= y; -x &= y; -x ^= y; -x <<= y; -x >>= y; -++obj.prop; -obj.prop++; ---obj.prop; -obj.prop--; -obj.prop += arr[0]; -obj.prop -= arr[0]; -obj.prop *= arr[0]; -arr[0] /= obj.prop; -arr[0] %= obj.prop; -arr[0] **= obj.prop; -obj.prop |= arr[0]; -obj.prop &= arr[0]; -obj.prop ^= arr[0]; -obj.prop <<= arr[0]; -obj.prop >>= arr[0]; -++arr[getIndex()]; -arr[getIndex()]--; -++getObj().prop; -getObj().prop++; ---getObj().prop; -getObj().prop--; -getObj().prop += getArr()[getIndex()]; -getObj().prop -= getArr()[getIndex()]; -getObj().prop *= getArr()[getIndex()]; -getArr()[getIndex()] /= getObj().prop; -getArr()[getIndex()] %= getObj().prop; -getArr()[getIndex()] **= getObj().prop; -getObj().prop |= getArr()[getIndex()]; -getObj().prop &= getArr()[getIndex()]; -getObj().prop ^= getArr()[getIndex()]; -getObj().prop <<= getArr()[getIndex()]; -getObj().prop >>= getArr()[getIndex()]; -z = x = y; -z = obj.prop = x; -z = getObj().prop = x; -z = arr[0] = x; -z = getArr()[getIndex()] = x; -z = x = obj.prop; -z = x = getObj().prop; -z = x = arr[0]; -z = x = arr[getIndex()]; -z = x = getArr()[getIndex()]; -z = ++x; -z = x++; -z = --x; -z = x--; -z = x += y; -z = x -= y; -z = x *= y; -z = y /= x; -z = y %= x; -z = y **= x; -z = x |= y; -z = x &= y; -z = x ^= y; -z = x <<= y; -z = x >>= y; -z = x + (y += 7); -z = x + (y += 7); -z = x++ + (y += 7); -z = ++obj.prop; -z = obj.prop++; -z = --obj.prop; -z = obj.prop--; -z = obj.prop += arr[0]; -z = obj.prop -= arr[0]; -z = obj.prop *= arr[0]; -z = arr[0] /= obj.prop; -z = arr[0] %= obj.prop; -z = arr[0] **= obj.prop; -z = obj.prop |= arr[0]; -z = obj.prop &= arr[0]; -z = obj.prop ^= arr[0]; -z = obj.prop <<= arr[0]; -z = obj.prop >>= arr[0]; -z = obj.prop + (arr[0] += 7); -z = obj.prop += (arr[0] += 7); -z = obj.prop++ + (arr[0] += 7); -z = ++getObj().prop; -z = getObj().prop++; -z = --getObj().prop; -z = getObj().prop--; -z = getObj().prop += getArr()[getIndex()]; -z = getObj().prop -= getArr()[getIndex()]; -z = getObj().prop *= getArr()[getIndex()]; -z = getArr()[getIndex()] /= getObj().prop; -z = getArr()[getIndex()] %= getObj().prop; -z = getArr()[getIndex()] **= getObj().prop; -z = getObj().prop |= getArr()[getIndex()]; -z = getObj().prop &= getArr()[getIndex()]; -z = getObj().prop ^= getArr()[getIndex()]; -z = getObj().prop <<= getArr()[getIndex()]; -z = getObj().prop >>= getArr()[getIndex()]; -z = getObj().prop + (getArr()[getIndex()] += 7); -z = getObj().prop += (getArr()[getIndex()] += 7); -z = getObj().prop++ + (getArr()[getIndex()] += 7); -++getObj().arr[0]; -getObj().arr[0]++; -getObj().arr[getIndex()] += 13; -++arr2[0][getIndex()]; -arr2[0][getIndex()]++; -++arr2[getIndex()][0]; -arr2[getIndex()][0]++; -z = ++getObj().arr[0]; -z = getObj().arr[0]++; -z = getObj().arr[getIndex()] += 13; -z = ++arr2[0][getIndex()]; -z = arr2[0][getIndex()]++; -z = ++arr2[getIndex()][0]; -z = arr2[getIndex()][0]++; -z = x = arr2[0][getIndex()]; -z = getObj().arr[0] = x; -z = getObj().arr[0] = arr2[0][getIndex()]; From 6d5c0328a7893eb52e0ea2e6308bed1e6a3c3575 Mon Sep 17 00:00:00 2001 From: Perry van Wesel Date: Sun, 2 Dec 2018 15:49:34 +0100 Subject: [PATCH 47/59] Create CONTRIBUTING.md --- CONTRIBUTING.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..13f2db018 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,80 @@ +# Contributing to TypeScriptToLua + +1) [Project Overview](#project-overview) +2) [Running Tests](#running-tests) +3) [Testing Guidelines](#testing-guidelines) +4) [Coding Conventions](#coding-conventions) + +## Project Overview +To get familiar with the project structure, here is a short overview of each directory and their function. +- `src/` + * Source code for the project, has the transpiler core files in its root. + * `src/lualib/` + - Contains the TypeScript source for the lualib. This consists of implementations of standard TypeScript functions that are not present in Lua. These files are compiled to Lua using the transpiler. They are included in the Lua result when transpiling. + * `src/targets/` + - Version-specific transpiler overrides for the different Lua targets. The main transpiler transpiles Lua 5.0, each target-specific transpiler extends the transpiler of the version before it, so the 5.3 inherits 5.2 which inherits 5.1 which inherits 5.0. LuaJIT is based on 5.2 so inherits from the 5.2 transpiler. + * *Compiler.ts* - Main entry point of the transpiler, this is what interfaces with the TypeScript compiler API. + * *Transpiler.ts* - Main transpiler code, transforms a TypeScript AST to a Lua string. + * *TSHelper.ts* - Helper methods used during the transpilation process. +- `test/` + * This directory contains all testing code for the transpiler. + * `test/src/` + - Contains all extra source and utility used to run tests. + * `test/unit/` + - Unit/Functional tests for the transpiler. Tests in here are grouped by functionality they are testing. Generally each of these tests uses the transpiler to transpile some TypeScript to Lua, then executes it using the Fengari Lua VM. Assertion is done on the result of the lua code. + * `test/translation/` + - **[Obsolete]** Contains tests that only check the transpiled Lua String. We prefer adding unit/functional tests over translation tests. This directory will probably be removed at some point. + +## Running Tests +The tests for this project can be executed using the standard `npm test`. This runs all tests (can take a while!). + +### Testing while developing +Due to the time required to run all tests, it is impractical to run every test while developing part of the transpiler. To speed up the test run you can import `FocusTest` or `FocusTests` from Alsatian. If a class is decorated with `@FocusTests`, all other test classes will be ignored. Similarly, if any test method is decorated with `@FocusTest`, only `@FocusTest` methods will be run during `npm test`. + +For example: +```ts +import { Expect, FocusTests, Test, TestCase } from "alsatian"; + +@FocusTests +export class FunctionTests { + // All tests in here will be executed. +} + +// All other tests will be ignored. +``` + +Or + +```ts +import { Expect, FocusTest, Test, TestCase } from "alsatian"; + +export class FunctionTests { + @FocusTest + @Test("Example test 1") + public test1(): void { // Will be executed + } + + @FocusTest + @Test("Example test 2") + public test2(): void { // Will also be executed + } + + @Test("Example test 3") + public test3(): void { // Will be ignored + } +} + +// All other tests will be ignored. +``` + + +## Testing Guidelines +When submitting a pull request with new functionality, we require some functional (transpile and execute Lua) to be added, to ensure the new functionality works as expected, and will continue to work that way. + +Translation tests are discouraged as in most cases as we do not really care about the exact Lua output, as long as executing it results in the correct result (which is tested by functional tests). + +## Coding Conventions +Most coding conventions are enforced by the ts-lint configuration. The test process will fail if code does not pass the linter. Some extra conventions worth mentioning: +* Do not abbreviate variable names. The exception here are inline lambda arguments, if it is obvious what the argument is you can abbreviate to the first letter, e.g: `statements.filter(s => ts.VariableStatement(s))` +* Readability of code is more important than the amount of space it takes. If extra line breaks make your code more readable, add them. +* Functional style is encouraged! From be2b16419f499641c20548930b42ad08001a7949 Mon Sep 17 00:00:00 2001 From: Perry van Wesel Date: Sun, 2 Dec 2018 15:51:00 +0100 Subject: [PATCH 48/59] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c360d6dbd..307f56dae 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,9 @@ Changelog can be found in [CHANGELOG.md](https://github.com/Perryvw/TypescriptTo } ``` +## Contributing +All contributions are welcome, but please read our [contribution guidelines](https://github.com/Perryvw/TypescriptToLua/blob/master/CONTRIBUTING.md)! + ## Declarations The real power of this transpiler is usage together with good declarations for the Lua API provided. Some examples of Lua interface declarations can be found here: - [Dota 2 Modding](https://github.com/ModDota/API/tree/master/declarations/server) From a4f1f5690a30b986acf8d0e1fdc53f47b02df1ca Mon Sep 17 00:00:00 2001 From: Lars Melchior Date: Sun, 2 Dec 2018 15:51:58 +0100 Subject: [PATCH 49/59] Detecting types derived from array (#289) * check the base types to determine if a type inherits from Array * dont block exceptions * remove empty else clause * remove space * added tsHelper.isDefaultArrayCallExpression() * added derived array recognition test code * Added isExplicitArrayType() method * added unit test * rewrite isDefaultArrayCall() -> isDefaultArrayCallMethodName() * change switch statement to Set lookup --- src/TSHelper.ts | 36 +++++++++++++++++++++++++++++++++++- src/Transpiler.ts | 9 ++++++++- test/unit/array.spec.ts | 13 +++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 203181681..c3eac9460 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -1,6 +1,25 @@ import * as ts from "typescript"; import { Decorator, DecoratorKind } from "./Decorator"; +const defaultArrayCallMethodNames = new Set([ + "concat", + "push", + "reverse", + "shift", + "unshift", + "sort", + "pop", + "forEach", + "indexOf", + "map", + "filter", + "some", + "every", + "slice", + "splice", + "join", +]); + export class TSHelper { // Reverse lookup of enum key by value @@ -74,11 +93,21 @@ export class TSHelper { && (typeNode as ts.UnionOrIntersectionTypeNode).types.some(this.isArrayTypeNode)); } - public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { + public static isExplicitArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); return typeNode && this.isArrayTypeNode(typeNode); } + public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { + const baseTypes = type.getBaseTypes(); + if (baseTypes) { + for (const baseType of baseTypes) { + if (this.isExplicitArrayType(baseType, checker)) { return true; } + } + } + return this.isExplicitArrayType(type, checker); + } + public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isCallExpression(node)) { const type = checker.getTypeAtLocation(node.expression); @@ -254,4 +283,9 @@ export class TSHelper { } return [false, null, null]; } + + public static isDefaultArrayCallMethodName(methodName: string): boolean { + return defaultArrayCallMethodNames.has(methodName); + } + } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 7ca71b507..232d1c7fc 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1203,7 +1203,14 @@ export abstract class LuaTranspiler { } - if (tsHelper.isArrayType(ownerType, this.checker)) { + // if ownerType is a array, use only supported functions + if (tsHelper.isExplicitArrayType(ownerType, this.checker)) { + return this.transpileArrayCallExpression(node); + } + + // if ownerType inherits from an array, use array calls where appropriate + if (tsHelper.isArrayType(ownerType, this.checker) + && tsHelper.isDefaultArrayCallMethodName(this.transpileIdentifier(node.expression.name))) { return this.transpileArrayCallExpression(node); } diff --git a/test/unit/array.spec.ts b/test/unit/array.spec.ts index bca94494a..d1daa4268 100644 --- a/test/unit/array.spec.ts +++ b/test/unit/array.spec.ts @@ -38,4 +38,17 @@ export class ArrayTests { const result = util.executeLua(lua); Expect(result).toBe(5); } + + @Test("Derived array access") + public derivedArrayAccess(): void { + const lua = `local arr = {firstElement=function(self) return self[1]; end};` + + util.transpileString( + `interface CustomArray extends Array{ firstElement():number; }; + declare const arr: CustomArray; + arr[0] = 3; + return arr.firstElement();` + ); + const result = util.executeLua(lua); + Expect(result).toBe(3); + } } From f5ed6570d198a002c4fb91db54e59cdde3dbaf40 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Tue, 4 Dec 2018 21:58:50 +0100 Subject: [PATCH 50/59] 0.12.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 917dc78de..f62b8ad47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "0.11.1", + "version": "0.12.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1f083c6e4..2633ac1d0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typescript-to-lua", "license": "MIT", - "version": "0.11.1", + "version": "0.12.0", "repository": "https://github.com/Perryvw/TypescriptToLua", "keywords": [ "typescript", From 0b8e485bbdd97b8434f5d79d14e6822d0806e2de Mon Sep 17 00:00:00 2001 From: Perryvw Date: Tue, 4 Dec 2018 22:02:08 +0100 Subject: [PATCH 51/59] stabilize package lock --- package-lock.json | 1352 ++++++++++++++++++++++----------------------- 1 file changed, 676 insertions(+), 676 deletions(-) diff --git a/package-lock.json b/package-lock.json index f62b8ad47..10eb7c082 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ "integrity": "sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg==", "dev": true, "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "@types/events": "1.2.0", + "@types/minimatch": "3.0.3", + "@types/node": "9.6.23" } }, "@types/minimatch": { @@ -45,10 +45,10 @@ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "alsatian": { @@ -57,10 +57,10 @@ "integrity": "sha1-B+qeiU7bnqmX7VcaZOFMyYsj0/g=", "dev": true, "requires": { - "@types/node": ">=4.0.0", - "extendo-error": "^1.0.1", - "glob": "^7.0.3", - "reflect-metadata": "^0.1.3", + "@types/node": "9.6.23", + "extendo-error": "1.0.1", + "glob": "7.1.2", + "reflect-metadata": "0.1.12", "tap-bark": "1.0.0" } }, @@ -81,7 +81,7 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "argv": { @@ -132,9 +132,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "balanced-match": { @@ -150,7 +150,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "brace-expansion": { @@ -159,7 +159,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -192,11 +192,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "circular-json": { @@ -210,9 +210,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" }, "dependencies": { "ansi-regex": { @@ -225,7 +225,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -248,7 +248,7 @@ "dev": true, "requires": { "argv": "0.0.2", - "request": "^2.81.0", + "request": "2.87.0", "urlgrey": "0.4.4" } }, @@ -273,7 +273,7 @@ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { @@ -299,9 +299,9 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "dashdash": { @@ -310,7 +310,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "decamelize": { @@ -352,7 +352,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "escape-string-regexp": { @@ -390,13 +390,13 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "extend": { @@ -435,9 +435,9 @@ "integrity": "sha512-NleQtQymPtbjBPnGOQiXfZfKpP3R7si+Sbkke631PWNnZt86eZSlymRJ0qv/KUTcz0fSsWO1iltZYz5/JV1uYQ==", "dev": true, "requires": { - "readline-sync": "^1.4.9", - "sprintf-js": "^1.1.1", - "tmp": "^0.0.33" + "readline-sync": "1.4.9", + "sprintf-js": "1.1.1", + "tmp": "0.0.33" }, "dependencies": { "sprintf-js": { @@ -453,7 +453,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "forever-agent": { @@ -468,9 +468,9 @@ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.18" } }, "fs.realpath": { @@ -495,7 +495,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "glob": { @@ -504,12 +504,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "har-schema": { @@ -524,8 +524,8 @@ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, "has-ansi": { @@ -534,7 +534,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-flag": { @@ -549,9 +549,9 @@ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" } }, "inflight": { @@ -560,8 +560,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -620,8 +620,8 @@ "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.0" } }, "jsbn": { @@ -666,7 +666,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "locate-path": { @@ -674,8 +674,8 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "lru-cache": { @@ -683,8 +683,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "make-error": { @@ -698,7 +698,7 @@ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "mime-db": { @@ -713,7 +713,7 @@ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "~1.33.0" + "mime-db": "1.33.0" } }, "mimic-fn": { @@ -727,7 +727,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -764,7 +764,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -778,33 +778,33 @@ "integrity": "sha512-w8OdJAhXL5izerzZMdqzYKMj/pgHJyY3qEPYBjLLxrhcVoHEY9pU5ENIiZyCgG9OR7x3VcUMoD40o6PtVpfR4g==", "dev": true, "requires": { - "archy": "^1.0.0", - "arrify": "^1.0.1", - "caching-transform": "^1.0.0", - "convert-source-map": "^1.5.1", - "debug-log": "^1.0.1", - "default-require-extensions": "^1.0.0", - "find-cache-dir": "^0.1.1", - "find-up": "^2.1.0", - "foreground-child": "^1.5.3", - "glob": "^7.0.6", - "istanbul-lib-coverage": "^1.1.2", - "istanbul-lib-hook": "^1.1.0", - "istanbul-lib-instrument": "^1.10.0", - "istanbul-lib-report": "^1.1.3", - "istanbul-lib-source-maps": "^1.2.3", - "istanbul-reports": "^1.4.0", - "md5-hex": "^1.2.0", - "merge-source-map": "^1.1.0", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.0", - "resolve-from": "^2.0.0", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.1", - "spawn-wrap": "^1.4.2", - "test-exclude": "^4.2.0", + "archy": "1.0.0", + "arrify": "1.0.1", + "caching-transform": "1.0.1", + "convert-source-map": "1.5.1", + "debug-log": "1.0.1", + "default-require-extensions": "1.0.0", + "find-cache-dir": "0.1.1", + "find-up": "2.1.0", + "foreground-child": "1.5.6", + "glob": "7.1.2", + "istanbul-lib-coverage": "1.2.0", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "1.10.1", + "istanbul-lib-report": "1.1.3", + "istanbul-lib-source-maps": "1.2.3", + "istanbul-reports": "1.4.0", + "md5-hex": "1.3.0", + "merge-source-map": "1.1.0", + "micromatch": "3.1.10", + "mkdirp": "0.5.1", + "resolve-from": "2.0.0", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "spawn-wrap": "1.4.2", + "test-exclude": "4.2.1", "yargs": "11.1.0", - "yargs-parser": "^8.0.0" + "yargs-parser": "8.1.0" }, "dependencies": { "align-text": { @@ -812,9 +812,9 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -837,7 +837,7 @@ "bundled": true, "dev": true, "requires": { - "default-require-extensions": "^1.0.0" + "default-require-extensions": "1.0.0" } }, "archy": { @@ -890,9 +890,9 @@ "bundled": true, "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "babel-generator": { @@ -900,14 +900,14 @@ "bundled": true, "dev": true, "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" } }, "babel-messages": { @@ -915,7 +915,7 @@ "bundled": true, "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-runtime": { @@ -923,8 +923,8 @@ "bundled": true, "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.6", + "regenerator-runtime": "0.11.1" } }, "babel-template": { @@ -932,11 +932,11 @@ "bundled": true, "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.10" } }, "babel-traverse": { @@ -944,15 +944,15 @@ "bundled": true, "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.4", + "lodash": "4.17.10" } }, "babel-types": { @@ -960,10 +960,10 @@ "bundled": true, "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "1.0.3" } }, "babylon": { @@ -981,13 +981,13 @@ "bundled": true, "dev": true, "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -995,7 +995,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -1003,7 +1003,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -1011,7 +1011,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -1019,9 +1019,9 @@ "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -1041,7 +1041,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -1050,16 +1050,16 @@ "bundled": true, "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -1067,7 +1067,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -1082,15 +1082,15 @@ "bundled": true, "dev": true, "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" }, "dependencies": { "isobject": { @@ -1105,9 +1105,9 @@ "bundled": true, "dev": true, "requires": { - "md5-hex": "^1.2.0", - "mkdirp": "^0.5.1", - "write-file-atomic": "^1.1.4" + "md5-hex": "1.3.0", + "mkdirp": "0.5.1", + "write-file-atomic": "1.3.4" } }, "camelcase": { @@ -1122,8 +1122,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chalk": { @@ -1131,11 +1131,11 @@ "bundled": true, "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "class-utils": { @@ -1143,10 +1143,10 @@ "bundled": true, "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -1154,7 +1154,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "isobject": { @@ -1170,8 +1170,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -1193,8 +1193,8 @@ "bundled": true, "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "commondir": { @@ -1232,8 +1232,8 @@ "bundled": true, "dev": true, "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "which": "1.3.0" } }, "debug": { @@ -1264,7 +1264,7 @@ "bundled": true, "dev": true, "requires": { - "strip-bom": "^2.0.0" + "strip-bom": "2.0.0" } }, "define-property": { @@ -1272,8 +1272,8 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -1281,7 +1281,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -1289,7 +1289,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -1297,9 +1297,9 @@ "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -1319,7 +1319,7 @@ "bundled": true, "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "error-ex": { @@ -1327,7 +1327,7 @@ "bundled": true, "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "escape-string-regexp": { @@ -1345,13 +1345,13 @@ "bundled": true, "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" }, "dependencies": { "cross-spawn": { @@ -1359,9 +1359,9 @@ "bundled": true, "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.0" } } } @@ -1371,13 +1371,13 @@ "bundled": true, "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -1385,7 +1385,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -1393,7 +1393,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -1403,8 +1403,8 @@ "bundled": true, "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -1412,7 +1412,7 @@ "bundled": true, "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -1422,14 +1422,14 @@ "bundled": true, "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -1437,7 +1437,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -1445,7 +1445,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -1453,7 +1453,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -1461,7 +1461,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -1469,9 +1469,9 @@ "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "kind-of": { @@ -1486,10 +1486,10 @@ "bundled": true, "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -1497,7 +1497,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -1507,9 +1507,9 @@ "bundled": true, "dev": true, "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" } }, "find-up": { @@ -1517,7 +1517,7 @@ "bundled": true, "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "for-in": { @@ -1530,8 +1530,8 @@ "bundled": true, "dev": true, "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "cross-spawn": "4.0.2", + "signal-exit": "3.0.2" } }, "fragment-cache": { @@ -1539,7 +1539,7 @@ "bundled": true, "dev": true, "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "fs.realpath": { @@ -1567,12 +1567,12 @@ "bundled": true, "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "globals": { @@ -1590,10 +1590,10 @@ "bundled": true, "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "source-map": { @@ -1601,7 +1601,7 @@ "bundled": true, "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -1611,7 +1611,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-flag": { @@ -1624,9 +1624,9 @@ "bundled": true, "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -1641,8 +1641,8 @@ "bundled": true, "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { @@ -1650,7 +1650,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -1658,7 +1658,7 @@ "bundled": true, "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -1668,7 +1668,7 @@ "bundled": true, "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -1688,8 +1688,8 @@ "bundled": true, "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -1702,7 +1702,7 @@ "bundled": true, "dev": true, "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -1715,7 +1715,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-arrayish": { @@ -1733,7 +1733,7 @@ "bundled": true, "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-data-descriptor": { @@ -1741,7 +1741,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-descriptor": { @@ -1749,9 +1749,9 @@ "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -1771,7 +1771,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -1784,7 +1784,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-odd": { @@ -1792,7 +1792,7 @@ "bundled": true, "dev": true, "requires": { - "is-number": "^4.0.0" + "is-number": "4.0.0" }, "dependencies": { "is-number": { @@ -1807,7 +1807,7 @@ "bundled": true, "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -1857,7 +1857,7 @@ "bundled": true, "dev": true, "requires": { - "append-transform": "^0.4.0" + "append-transform": "0.4.0" } }, "istanbul-lib-instrument": { @@ -1865,13 +1865,13 @@ "bundled": true, "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.0", - "semver": "^5.3.0" + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" } }, "istanbul-lib-report": { @@ -1879,10 +1879,10 @@ "bundled": true, "dev": true, "requires": { - "istanbul-lib-coverage": "^1.1.2", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" }, "dependencies": { "supports-color": { @@ -1890,7 +1890,7 @@ "bundled": true, "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -1900,11 +1900,11 @@ "bundled": true, "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.1.2", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "debug": "3.1.0", + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" }, "dependencies": { "debug": { @@ -1922,7 +1922,7 @@ "bundled": true, "dev": true, "requires": { - "handlebars": "^4.0.3" + "handlebars": "4.0.11" } }, "js-tokens": { @@ -1940,7 +1940,7 @@ "bundled": true, "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "lazy-cache": { @@ -1954,7 +1954,7 @@ "bundled": true, "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "load-json-file": { @@ -1962,11 +1962,11 @@ "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" } }, "locate-path": { @@ -1974,8 +1974,8 @@ "bundled": true, "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" }, "dependencies": { "path-exists": { @@ -2000,7 +2000,7 @@ "bundled": true, "dev": true, "requires": { - "js-tokens": "^3.0.0" + "js-tokens": "3.0.2" } }, "lru-cache": { @@ -2008,8 +2008,8 @@ "bundled": true, "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "map-cache": { @@ -2022,7 +2022,7 @@ "bundled": true, "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "md5-hex": { @@ -2030,7 +2030,7 @@ "bundled": true, "dev": true, "requires": { - "md5-o-matic": "^0.1.1" + "md5-o-matic": "0.1.1" } }, "md5-o-matic": { @@ -2043,7 +2043,7 @@ "bundled": true, "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "merge-source-map": { @@ -2051,7 +2051,7 @@ "bundled": true, "dev": true, "requires": { - "source-map": "^0.6.1" + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -2066,19 +2066,19 @@ "bundled": true, "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "kind-of": { @@ -2098,7 +2098,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -2111,8 +2111,8 @@ "bundled": true, "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -2120,7 +2120,7 @@ "bundled": true, "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -2143,18 +2143,18 @@ "bundled": true, "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "arr-diff": { @@ -2179,10 +2179,10 @@ "bundled": true, "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" } }, "npm-run-path": { @@ -2190,7 +2190,7 @@ "bundled": true, "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -2208,9 +2208,9 @@ "bundled": true, "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -2218,7 +2218,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -2228,7 +2228,7 @@ "bundled": true, "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -2243,7 +2243,7 @@ "bundled": true, "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -2258,7 +2258,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "optimist": { @@ -2266,8 +2266,8 @@ "bundled": true, "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" } }, "os-homedir": { @@ -2280,9 +2280,9 @@ "bundled": true, "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "p-finally": { @@ -2295,7 +2295,7 @@ "bundled": true, "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -2303,7 +2303,7 @@ "bundled": true, "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.2.0" } }, "p-try": { @@ -2316,7 +2316,7 @@ "bundled": true, "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.1" } }, "pascalcase": { @@ -2329,7 +2329,7 @@ "bundled": true, "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -2352,9 +2352,9 @@ "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -2372,7 +2372,7 @@ "bundled": true, "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -2380,7 +2380,7 @@ "bundled": true, "dev": true, "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" }, "dependencies": { "find-up": { @@ -2388,8 +2388,8 @@ "bundled": true, "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } } } @@ -2409,9 +2409,9 @@ "bundled": true, "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -2419,8 +2419,8 @@ "bundled": true, "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" }, "dependencies": { "find-up": { @@ -2428,8 +2428,8 @@ "bundled": true, "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } } } @@ -2444,8 +2444,8 @@ "bundled": true, "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "repeat-element": { @@ -2463,7 +2463,7 @@ "bundled": true, "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "require-directory": { @@ -2497,7 +2497,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -2505,7 +2505,7 @@ "bundled": true, "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "safe-regex": { @@ -2513,7 +2513,7 @@ "bundled": true, "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "semver": { @@ -2531,10 +2531,10 @@ "bundled": true, "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -2542,7 +2542,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2552,7 +2552,7 @@ "bundled": true, "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -2575,14 +2575,14 @@ "bundled": true, "dev": true, "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "3.1.0" }, "dependencies": { "define-property": { @@ -2590,7 +2590,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -2598,7 +2598,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2608,9 +2608,9 @@ "bundled": true, "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -2618,7 +2618,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -2626,7 +2626,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -2634,7 +2634,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -2642,9 +2642,9 @@ "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -2664,7 +2664,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" } }, "source-map": { @@ -2677,11 +2677,11 @@ "bundled": true, "dev": true, "requires": { - "atob": "^2.0.0", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.1", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-url": { @@ -2694,12 +2694,12 @@ "bundled": true, "dev": true, "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" + "foreground-child": "1.5.6", + "mkdirp": "0.5.1", + "os-homedir": "1.0.2", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "which": "1.3.0" } }, "spdx-correct": { @@ -2707,8 +2707,8 @@ "bundled": true, "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, "spdx-exceptions": { @@ -2721,8 +2721,8 @@ "bundled": true, "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" } }, "spdx-license-ids": { @@ -2735,7 +2735,7 @@ "bundled": true, "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "static-extend": { @@ -2743,8 +2743,8 @@ "bundled": true, "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -2752,7 +2752,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -2762,8 +2762,8 @@ "bundled": true, "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -2776,7 +2776,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -2786,7 +2786,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -2794,7 +2794,7 @@ "bundled": true, "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "strip-eof": { @@ -2812,11 +2812,11 @@ "bundled": true, "dev": true, "requires": { - "arrify": "^1.0.1", - "micromatch": "^3.1.8", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", - "require-main-filename": "^1.0.1" + "arrify": "1.0.1", + "micromatch": "3.1.10", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" }, "dependencies": { "arr-diff": { @@ -2834,16 +2834,16 @@ "bundled": true, "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -2851,7 +2851,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2861,13 +2861,13 @@ "bundled": true, "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -2875,7 +2875,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -2883,7 +2883,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -2891,7 +2891,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -2899,7 +2899,7 @@ "bundled": true, "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -2909,7 +2909,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -2917,7 +2917,7 @@ "bundled": true, "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -2927,9 +2927,9 @@ "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -2944,14 +2944,14 @@ "bundled": true, "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -2959,7 +2959,7 @@ "bundled": true, "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -2967,7 +2967,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2977,10 +2977,10 @@ "bundled": true, "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -2988,7 +2988,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2998,7 +2998,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -3006,7 +3006,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -3014,9 +3014,9 @@ "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-number": { @@ -3024,7 +3024,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -3032,7 +3032,7 @@ "bundled": true, "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -3052,19 +3052,19 @@ "bundled": true, "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } } } @@ -3079,7 +3079,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "to-regex": { @@ -3087,10 +3087,10 @@ "bundled": true, "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -3098,8 +3098,8 @@ "bundled": true, "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" }, "dependencies": { "is-number": { @@ -3107,7 +3107,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } } } @@ -3123,9 +3123,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" }, "dependencies": { "yargs": { @@ -3134,9 +3134,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } @@ -3153,10 +3153,10 @@ "bundled": true, "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -3164,7 +3164,7 @@ "bundled": true, "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -3172,10 +3172,10 @@ "bundled": true, "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -3185,8 +3185,8 @@ "bundled": true, "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -3194,9 +3194,9 @@ "bundled": true, "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -3231,7 +3231,7 @@ "bundled": true, "dev": true, "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" }, "dependencies": { "kind-of": { @@ -3246,8 +3246,8 @@ "bundled": true, "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "which": { @@ -3255,7 +3255,7 @@ "bundled": true, "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -3279,8 +3279,8 @@ "bundled": true, "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "is-fullwidth-code-point": { @@ -3288,7 +3288,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -3296,9 +3296,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -3313,9 +3313,9 @@ "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "slide": "^1.1.5" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" } }, "y18n": { @@ -3333,18 +3333,18 @@ "bundled": true, "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" }, "dependencies": { "ansi-regex": { @@ -3362,9 +3362,9 @@ "bundled": true, "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" } }, "strip-ansi": { @@ -3372,7 +3372,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "yargs-parser": { @@ -3380,7 +3380,7 @@ "bundled": true, "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -3390,7 +3390,7 @@ "bundled": true, "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" }, "dependencies": { "camelcase": { @@ -3414,7 +3414,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-locale": { @@ -3422,9 +3422,9 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "os-tmpdir": { @@ -3443,7 +3443,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", "requires": { - "p-try": "^2.0.0" + "p-try": "2.0.0" } }, "p-locate": { @@ -3451,7 +3451,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.0.0" } }, "p-try": { @@ -3516,13 +3516,13 @@ "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "readline-sync": { @@ -3543,26 +3543,26 @@ "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "dev": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.1", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" } }, "require-directory": { @@ -3581,7 +3581,7 @@ "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.5" } }, "safe-buffer": { @@ -3612,7 +3612,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -3637,8 +3637,8 @@ "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "buffer-from": "1.1.0", + "source-map": "0.6.1" } }, "sprintf-js": { @@ -3653,15 +3653,15 @@ "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "dev": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "string-width": { @@ -3669,8 +3669,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -3683,7 +3683,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -3694,7 +3694,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "strip-ansi": { @@ -3702,7 +3702,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-eof": { @@ -3722,11 +3722,11 @@ "integrity": "sha1-bAPcUWh/7Xh3+COtSx3dHvXrVnQ=", "dev": true, "requires": { - "@types/node": ">=0.0.2", - "chalk": "^1.1.3", - "duplexer": "^0.1.1", - "tap-parser": "^3.0.3", - "through2": "^2.0.1" + "@types/node": "9.6.23", + "chalk": "1.1.3", + "duplexer": "0.1.1", + "tap-parser": "3.0.5", + "through2": "2.0.3" } }, "tap-parser": { @@ -3735,9 +3735,9 @@ "integrity": "sha1-uUf2ngs+U9S5IBH2zFUuFtrcfsk=", "dev": true, "requires": { - "events-to-array": "^1.0.1", - "js-yaml": "^3.2.7", - "readable-stream": "^2" + "events-to-array": "1.1.2", + "js-yaml": "3.10.0", + "readable-stream": "2.3.4" } }, "threads": { @@ -3746,8 +3746,8 @@ "integrity": "sha512-4B7hd61lDsVW1Z/+FAVX7D9QbiQYUbtGMHVkkwWT/nKPKas8u4FEc+Rg8E8h2erhNTQGNqNJ0TsholmhpKNPRg==", "dev": true, "requires": { - "eventemitter3": "^2.0.2", - "native-promise-only": "^0.8.1" + "eventemitter3": "2.0.3", + "native-promise-only": "0.8.1" } }, "through2": { @@ -3756,8 +3756,8 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.3.4", + "xtend": "4.0.1" } }, "tmp": { @@ -3766,7 +3766,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "tough-cookie": { @@ -3775,7 +3775,7 @@ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "dev": true, "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" } }, "ts-node": { @@ -3784,14 +3784,14 @@ "integrity": "sha512-klJsfswHP0FuOLsvBZ/zzCfUvakOSSxds78mVeK7I+qP76YWtxf16hEZsp3U+b0kIo82R5UatGFeblYMqabb2Q==", "dev": true, "requires": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" + "arrify": "1.0.1", + "buffer-from": "1.1.0", + "diff": "3.4.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.6", + "yn": "2.0.0" } }, "tslib": { @@ -3806,18 +3806,18 @@ "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.4.1", + "commander": "2.16.0", + "diff": "3.4.0", + "glob": "7.1.2", + "js-yaml": "3.10.0", + "minimatch": "3.0.4", + "resolve": "1.8.1", + "semver": "5.5.0", + "tslib": "1.9.3", + "tsutils": "2.28.0" }, "dependencies": { "ansi-styles": { @@ -3826,7 +3826,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.2" } }, "chalk": { @@ -3835,9 +3835,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "supports-color": { @@ -3846,7 +3846,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -3863,7 +3863,7 @@ "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", "dev": true, "requires": { - "tslib": "^1.8.1" + "tslib": "1.9.3" } }, "tunnel-agent": { @@ -3872,7 +3872,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.1" } }, "tweetnacl": { @@ -3911,9 +3911,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "which": { @@ -3921,7 +3921,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -3934,8 +3934,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "is-fullwidth-code-point": { @@ -3943,7 +3943,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -3951,9 +3951,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -3990,18 +3990,18 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" + "cliui": "4.1.0", + "decamelize": "2.0.0", + "find-up": "3.0.0", + "get-caller-file": "1.0.3", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "4.0.0", + "yargs-parser": "10.1.0" } }, "yargs-parser": { @@ -4009,7 +4009,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } }, "yn": { From d6c7a0dcd82dad89251366706581e921e74e8e41 Mon Sep 17 00:00:00 2001 From: Lars Melchior Date: Fri, 7 Dec 2018 13:50:15 +0100 Subject: [PATCH 52/59] Check inherited accessors (#297) * check for inherited accessor methods * diversify test * removed FocusTests and repositioned forAllTypes * rename forTypeOrAnySupertype --- src/TSHelper.ts | 50 +++++++++++++++++++++++------------ test/unit/expressions.spec.ts | 31 ++++++++++++++++++++++ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index c3eac9460..aa87170e5 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -80,6 +80,22 @@ export class TSHelper { || (ts.isBinaryExpression(node.parent) && ts.isArrayLiteralExpression(node.parent.left))); } + // iterate over a type and its bases until the callback returns true. + public static forTypeOrAnySupertype(type: ts.Type, callback: (type: ts.Type) => boolean): boolean { + if (callback(type)) { + return true; + } + const baseTypes = type.getBaseTypes(); + if (baseTypes) { + for (const baseType of baseTypes) { + if (this.forTypeOrAnySupertype(baseType, callback)) { + return true; + } + } + } + return false; + } + public static isStringType(type: ts.Type): boolean { return (type.flags & ts.TypeFlags.String) !== 0 || (type.flags & ts.TypeFlags.StringLike) !== 0 @@ -99,13 +115,7 @@ export class TSHelper { } public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { - const baseTypes = type.getBaseTypes(); - if (baseTypes) { - for (const baseType of baseTypes) { - if (this.isExplicitArrayType(baseType, checker)) { return true; } - } - } - return this.isExplicitArrayType(type, checker); + return this.forTypeOrAnySupertype(type, t => this.isExplicitArrayType(t, checker)); } public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean { @@ -182,28 +192,34 @@ export class TSHelper { return null; } + public static hasExplicitGetAccessor(type: ts.Type, name: ts.__String): boolean { + if (type && type.symbol && type.symbol.members) { + const field = type.symbol.members.get(name); + return field && (field.flags & ts.SymbolFlags.GetAccessor) !== 0; + } + } + public static hasGetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - - if (type && type.symbol && type.symbol.members) { - const field = type.symbol.members.get(name); - return field && (field.flags & ts.SymbolFlags.GetAccessor) !== 0; - } + return this.forTypeOrAnySupertype(type, t => this.hasExplicitGetAccessor(t, name)); } return false; } + public static hasExplicitSetAccessor(type: ts.Type, name: ts.__String): boolean { + if (type && type.symbol && type.symbol.members) { + const field = type.symbol.members.get(name); + return field && (field.flags & ts.SymbolFlags.SetAccessor) !== 0; + } + } + public static hasSetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - - if (type && type.symbol && type.symbol.members) { - const field = type.symbol.members.get(name); - return field && (field.flags & ts.SymbolFlags.SetAccessor) !== 0; - } + return this.forTypeOrAnySupertype(type, t => this.hasExplicitSetAccessor(t, name)); } return false; } diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 7fbea1209..868b9bee7 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -254,6 +254,37 @@ export class ExpressionTests { Expect(result).toBe(expected); } + @TestCase("inst.baseField", 7) + @TestCase("inst.field", 6) + @TestCase("inst.superField", 5) + @Test("Inherited accessors") + public inheritedAccessors(expression: string, expected: any): void { + const source = `class MyBaseClass {` + + ` public _baseField: number;` + + ` public get baseField(): number { return this._baseField + 6; }` + + ` public set baseField(v: number) { this._baseField = v; }` + + `}` + + `class MyClass extends MyBaseClass {` + + ` public _field: number;` + + ` public get field(): number { return this._field + 4; }` + + ` public set field(v: number) { this._field = v; }` + + `}` + + `class MySuperClass extends MyClass {` + + ` public _superField: number;` + + ` public get superField(): number { return this._superField + 2; }` + + ` public set superField(v: number) { this._superField = v; }` + + `}` + + `var inst = new MySuperClass();` + + `inst.baseField = 1;` + + `inst.field = 2;` + + `inst.superField = 3;` + + `return ${expression};`; + + const lua = util.transpileString(source); + const result = util.executeLua(lua); + Expect(result).toBe(expected); + } + @TestCase("i++", 10) @TestCase("i--", 10) @TestCase("++i", 11) From fc41aa3a179e1f1d7bd2546ebe2a068bc4828a46 Mon Sep 17 00:00:00 2001 From: Yan Soares Couto Date: Sun, 9 Dec 2018 12:12:02 -0200 Subject: [PATCH 53/59] Fixed default constructor on subclass (#301) --- src/Transpiler.ts | 2 +- test/unit/class.spec.ts | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 232d1c7fc..2c99f8bbc 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1789,7 +1789,7 @@ export abstract class LuaTranspiler { if (constructor) { // Add constructor plus initialization of instance fields result += this.transpileConstructor(constructor, className); - } else if (!isExtension) { + } else if (!isExtension && !extendsType) { // Generate a constructor if none was defined result += this.transpileConstructor(ts.createConstructor([], [], [], ts.createBlock([], true)), className); diff --git a/test/unit/class.spec.ts b/test/unit/class.spec.ts index de67a5425..bc83f8681 100644 --- a/test/unit/class.spec.ts +++ b/test/unit/class.spec.ts @@ -127,6 +127,59 @@ export class ClassTests { Expect(result).toBe(4); } + @Test("SubclassDefaultConstructor") + public subclassDefaultConstructor(): void { + const result = util.transpileAndExecute( + `class a { + field: number; + constructor(field: number) { + this.field = field; + } + } + class b extends a {} + return new b(10).field;` + ); + + Expect(result).toBe(10); + } + + @Test("SubsubclassDefaultConstructor") + public subsubclassDefaultConstructor(): void { + const result = util.transpileAndExecute( + `class a { + field: number; + constructor(field: number) { + this.field = field; + } + } + class b extends a {} + class c extends b {} + return new c(10).field;` + ); + + Expect(result).toBe(10); + } + + @Test("SubclassConstructor") + public subclassConstructor(): void { + const result = util.transpileAndExecute( + `class a { + field: number; + constructor(field: number) { + this.field = field; + } + } + class b extends a { + constructor(field: number) { + super(field + 1); + } + } + return new b(10).field;` + ); + + Expect(result).toBe(11); + } + @Test("classSuper") public classSuper(): void { // Transpile From c0b73bd056a268975d36e295741b51ecfb1d1177 Mon Sep 17 00:00:00 2001 From: Lars Melchior Date: Sun, 9 Dec 2018 19:55:37 +0100 Subject: [PATCH 54/59] Use declared types (#302) * update forTypeOrAnySupertype() to use declared type * added test case for accessor using this * enhance test to use generics --- src/TSHelper.ts | 14 +++++++++----- test/unit/array.spec.ts | 4 ++-- test/unit/expressions.spec.ts | 2 ++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index aa87170e5..352575c01 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -81,14 +81,18 @@ export class TSHelper { } // iterate over a type and its bases until the callback returns true. - public static forTypeOrAnySupertype(type: ts.Type, callback: (type: ts.Type) => boolean): boolean { + public static forTypeOrAnySupertype(type: ts.Type, checker: ts.TypeChecker, callback: (type: ts.Type) => boolean): + boolean { if (callback(type)) { return true; } + if (!type.isClassOrInterface() && type.symbol) { + type = checker.getDeclaredTypeOfSymbol(type.symbol); + } const baseTypes = type.getBaseTypes(); if (baseTypes) { for (const baseType of baseTypes) { - if (this.forTypeOrAnySupertype(baseType, callback)) { + if (this.forTypeOrAnySupertype(baseType, checker, callback)) { return true; } } @@ -115,7 +119,7 @@ export class TSHelper { } public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { - return this.forTypeOrAnySupertype(type, t => this.isExplicitArrayType(t, checker)); + return this.forTypeOrAnySupertype(type, checker, t => this.isExplicitArrayType(t, checker)); } public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean { @@ -203,7 +207,7 @@ export class TSHelper { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - return this.forTypeOrAnySupertype(type, t => this.hasExplicitGetAccessor(t, name)); + return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitGetAccessor(t, name)); } return false; } @@ -219,7 +223,7 @@ export class TSHelper { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - return this.forTypeOrAnySupertype(type, t => this.hasExplicitSetAccessor(t, name)); + return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitSetAccessor(t, name)); } return false; } diff --git a/test/unit/array.spec.ts b/test/unit/array.spec.ts index d1daa4268..cb7a4c43c 100644 --- a/test/unit/array.spec.ts +++ b/test/unit/array.spec.ts @@ -43,8 +43,8 @@ export class ArrayTests { public derivedArrayAccess(): void { const lua = `local arr = {firstElement=function(self) return self[1]; end};` + util.transpileString( - `interface CustomArray extends Array{ firstElement():number; }; - declare const arr: CustomArray; + `interface CustomArray extends Array{ firstElement():number; }; + declare const arr: CustomArray; arr[0] = 3; return arr.firstElement();` ); diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 868b9bee7..6f9c2ba30 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -257,6 +257,7 @@ export class ExpressionTests { @TestCase("inst.baseField", 7) @TestCase("inst.field", 6) @TestCase("inst.superField", 5) + @TestCase("inst.superBaseField", 4) @Test("Inherited accessors") public inheritedAccessors(expression: string, expected: any): void { const source = `class MyBaseClass {` @@ -273,6 +274,7 @@ export class ExpressionTests { + ` public _superField: number;` + ` public get superField(): number { return this._superField + 2; }` + ` public set superField(v: number) { this._superField = v; }` + + ` public get superBaseField() { return this.baseField - 3; }` + `}` + `var inst = new MySuperClass();` + `inst.baseField = 1;` From a61f8412dc490c3cec650b886eb1bbce3be8224a Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 10 Dec 2018 08:10:36 +0100 Subject: [PATCH 55/59] 0.12.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10eb7c082..45593c4c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "0.12.0", + "version": "0.12.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2633ac1d0..334c88907 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typescript-to-lua", "license": "MIT", - "version": "0.12.0", + "version": "0.12.1", "repository": "https://github.com/Perryvw/TypescriptToLua", "keywords": [ "typescript", From 055060a45021ef5c635d048c0418f4cdcbdc3525 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 11 Dec 2018 07:52:07 -0700 Subject: [PATCH 56/59] suite of tests for new functions and fixes for edge-cases found --- src/Errors.ts | 16 +- src/TSHelper.ts | 40 +++- test/unit/assignments.spec.ts | 421 ++++++++++++++++++++++++++++++++++ test/unit/functions.spec.ts | 21 ++ 4 files changed, 486 insertions(+), 12 deletions(-) diff --git a/src/Errors.ts b/src/Errors.ts index b9fa04157..c6d0f0af7 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -71,17 +71,25 @@ export class TSTLErrors { public static UnsupportedFunctionConversion = (node: ts.Node, name?: string) => { if (name) { - return new TranspileError(`Unsupported conversion from method to function "${name}".`, node); + return new TranspileError(`Unsupported conversion from method to function "${name}". ` + + `To fix, wrap the method in an arrow function.`, + node); } else { - return new TranspileError(`Unsupported conversion from method to function.`, node); + return new TranspileError(`Unsupported conversion from method to function. ` + + `To fix, wrap the method in an arrow function.`, + node); } } public static UnsupportedMethodConversion = (node: ts.Node, name?: string) => { if (name) { - return new TranspileError(`Unsupported conversion from function to method "${name}".`, node); + return new TranspileError(`Unsupported conversion from function to method "${name}". ` + + `To fix, wrap the function in an arrow function.`, + node); } else { - return new TranspileError(`Unsupported conversion from function to method.`, node); + return new TranspileError(`Unsupported conversion from function to method. ` + + `To fix, wrap the function in an arrow function.`, + node); } } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 3e3b8e6e9..ddb2389ac 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -256,13 +256,37 @@ export class TSHelper { return [false, null, null]; } + public static getExplicitThisParameter(signatureDeclaration: ts.SignatureDeclaration): ts.ParameterDeclaration { + return signatureDeclaration.parameters.find( + param => ts.isIdentifier(param.name) && param.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); + } + + public static getSignatureDeclarations(signatures: ts.Signature[], checker: ts.TypeChecker) + : ts.SignatureDeclaration[] { + const signatureDeclarations: ts.SignatureDeclaration[] = []; + for (const signature of signatures) { + const signatureDeclaration = signature.getDeclaration(); + if ((ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) + && !this.getExplicitThisParameter(signatureDeclaration)) { + // Function expressions: get signatures of type being assigned to, unless 'this' was explicit + const declType = checker.getTypeAtLocation(signatureDeclaration.parent); + const declSignatures = declType.getCallSignatures(); + if (declSignatures.length > 0) { + declSignatures.map(s => s.getDeclaration()).forEach(decl => signatureDeclarations.push(decl)); + continue; + } + } + signatureDeclarations.push(signatureDeclaration); + } + return signatureDeclarations; + } + public static getDeclarationContextType(signatureDeclaration: ts.SignatureDeclaration, checker: ts.TypeChecker): ContextType { - const thisArg = signatureDeclaration.parameters.find( - param => ts.isIdentifier(param.name) && param.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword); - if (thisArg) { + const thisParameter = this.getExplicitThisParameter(signatureDeclaration); + if (thisParameter) { // Explicit 'this' - return thisArg.type && thisArg.type.kind === ts.SyntaxKind.VoidKeyword + return thisParameter.type && thisParameter.type.kind === ts.SyntaxKind.VoidKeyword ? ContextType.Void : ContextType.NonVoid; } if ((ts.isMethodDeclaration(signatureDeclaration) || ts.isMethodSignature(signatureDeclaration)) @@ -288,10 +312,10 @@ export class TSHelper { if (signatures.length === 0) { return ContextType.None; } - const signatureDeclataions = signatures.map(sig => sig.getDeclaration()); - const context = this.getDeclarationContextType(signatureDeclataions[0], checker); - for (let i = 1; i < signatureDeclataions.length; ++i) { - if (this.getDeclarationContextType(signatureDeclataions[i], checker) !== context) { + const signatureDeclarations = this.getSignatureDeclarations(signatures, checker); + const context = this.getDeclarationContextType(signatureDeclarations[0], checker); + for (let i = 1; i < signatureDeclarations.length; ++i) { + if (this.getDeclarationContextType(signatureDeclarations[i], checker) !== context) { return ContextType.Mixed; } } diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index e3ccb60c0..56109779e 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -6,6 +6,23 @@ const fs = require("fs"); export class AssignmentTests { + public static readonly funcAssignTestCode = + `let func: {(s: string): string} = function(s) { return s + "+func"; }; + let lambda: (s: string) => string = s => s + "+lambda"; + let thisFunc: {(this: Foo, s: string): string} = function(s) { return s + "+thisFunc"; }; + let thisLambda: (this: Foo, s: string) => string = s => s + "+thisLambda"; + class Foo { + method(s: string): string { return s + "+method"; } + lambdaProp: (s: string) => string = s => s + "+lambdaProp"; + voidMethod(this: void, s: string): string { return s + "+voidMethod"; } + voidLambdaProp: (this: void, s: string) => string = s => s + "+voidLambdaProp"; + static staticMethod(s: string): string { return s + "+staticMethod"; } + static staticLambdaProp: (s: string) => string = s => s + "+staticLambdaProp"; + static thisStaticMethod(this: Foo, s: string): string { return s + "+thisStaticMethod"; } + static thisStaticLambdaProp: (this: Foo, s: string) => string = s => s + "+thisStaticLambdaProp"; + } + const foo = new Foo();`; + @TestCase(`"abc"`, `"abc"`) @TestCase("3", "3") @TestCase("[1,2,3]", "{1,2,3}") @@ -185,4 +202,408 @@ export class AssignmentTests { Expect(() => util.transpileString(`const ${identifier} = 3;`)) .toThrowError(TranspileError, `Cannot use Lua keyword ${identifier} as identifier.`); } + + @TestCase("func", "lambda", "foo+lambda") + @TestCase("func", "s => s", "foo") + @TestCase("func", "function(s) { return s; }", "foo") + @TestCase("func", "function(this: void, s: string) { return s; }", "foo") + @TestCase("func", "s => foo.method(s)", "foo+method") + @TestCase("func", "s => foo.lambdaProp(s)", "foo+lambdaProp") + @TestCase("func", "Foo.staticMethod", "foo+staticMethod") + @TestCase("func", "Foo.staticLambdaProp", "foo+staticLambdaProp") + @TestCase("func", "foo.voidMethod", "foo+voidMethod") + @TestCase("func", "foo.voidLambdaProp", "foo+voidLambdaProp") + @TestCase("lambda", "func", "foo+func") + @TestCase("lambda", "s => s", "foo") + @TestCase("lambda", "function(s) { return s; }", "foo") + @TestCase("lambda", "function(this: void, s: string) { return s; }", "foo") + @TestCase("lambda", "s => foo.method(s)", "foo+method") + @TestCase("lambda", "s => foo.lambdaProp(s)", "foo+lambdaProp") + @TestCase("lambda", "Foo.staticMethod", "foo+staticMethod") + @TestCase("lambda", "Foo.staticLambdaProp", "foo+staticLambdaProp") + @TestCase("lambda", "foo.voidMethod", "foo+voidMethod") + @TestCase("lambda", "foo.voidLambdaProp", "foo+voidLambdaProp") + @TestCase("Foo.staticMethod", "func", "foo+func") + @TestCase("Foo.staticMethod", "lambda", "foo+lambda") + @TestCase("Foo.staticMethod", "s => s", "foo") + @TestCase("Foo.staticMethod", "function(s) { return s; }", "foo") + @TestCase("Foo.staticMethod", "function(this: void, s: string) { return s; }", "foo") + @TestCase("Foo.staticMethod", "s => foo.method(s)", "foo+method") + @TestCase("Foo.staticMethod", "s => foo.lambdaProp(s)", "foo+lambdaProp") + @TestCase("Foo.staticMethod", "Foo.staticLambdaProp", "foo+staticLambdaProp") + @TestCase("Foo.staticMethod", "foo.voidMethod", "foo+voidMethod") + @TestCase("Foo.staticMethod", "foo.voidLambdaProp", "foo+voidLambdaProp") + @TestCase("Foo.staticLambdaProp", "func", "foo+func") + @TestCase("Foo.staticLambdaProp", "lambda", "foo+lambda") + @TestCase("Foo.staticLambdaProp", "s => s", "foo") + @TestCase("Foo.staticLambdaProp", "function(s) { return s; }", "foo") + @TestCase("Foo.staticLambdaProp", "function(this: void, s: string) { return s; }", "foo") + @TestCase("Foo.staticLambdaProp", "s => foo.method(s)", "foo+method") + @TestCase("Foo.staticLambdaProp", "s => foo.lambdaProp(s)", "foo+lambdaProp") + @TestCase("Foo.staticLambdaProp", "Foo.staticMethod", "foo+staticMethod") + @TestCase("Foo.staticLambdaProp", "foo.voidMethod", "foo+voidMethod") + @TestCase("Foo.staticLambdaProp", "foo.voidLambdaProp", "foo+voidLambdaProp") + @TestCase("foo.voidMethod", "func", "foo+func") + @TestCase("foo.voidMethod", "lambda", "foo+lambda") + @TestCase("foo.voidMethod", "s => s", "foo") + @TestCase("foo.voidMethod", "function(s) { return s; }", "foo") + @TestCase("foo.voidMethod", "function(this: void, s: string) { return s; }", "foo") + @TestCase("foo.voidMethod", "s => foo.method(s)", "foo+method") + @TestCase("foo.voidMethod", "s => foo.lambdaProp(s)", "foo+lambdaProp") + @TestCase("foo.voidMethod", "Foo.staticMethod", "foo+staticMethod") + @TestCase("foo.voidMethod", "Foo.staticLambdaProp", "foo+staticLambdaProp") + @TestCase("foo.voidMethod", "foo.voidLambdaProp", "foo+voidLambdaProp") + @TestCase("foo.voidLambdaProp", "func", "foo+func") + @TestCase("foo.voidLambdaProp", "lambda", "foo+lambda") + @TestCase("foo.voidLambdaProp", "s => s", "foo") + @TestCase("foo.voidLambdaProp", "function(s) { return s; }", "foo") + @TestCase("foo.voidLambdaProp", "function(this: void, s: string) { return s; }", "foo") + @TestCase("foo.voidLambdaProp", "s => foo.method(s)", "foo+method") + @TestCase("foo.voidLambdaProp", "s => foo.lambdaProp(s)", "foo+lambdaProp") + @TestCase("foo.voidLambdaProp", "Foo.staticMethod", "foo+staticMethod") + @TestCase("foo.voidLambdaProp", "Foo.staticLambdaProp", "foo+staticLambdaProp") + @TestCase("foo.voidLambdaProp", "foo.voidMethod", "foo+voidMethod") + @Test("Valid function assignment") + public validFunctionAssignment(func: string, assignTo: string, expectResult: string): void { + const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo}; return ${func}("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } + + @TestCase("func", "foo+func") + @TestCase("lambda", "foo+lambda") + @TestCase("Foo.staticMethod", "foo+staticMethod") + @TestCase("Foo.staticLambdaProp", "foo+staticLambdaProp") + @TestCase("foo.voidMethod", "foo+voidMethod") + @TestCase("foo.voidLambdaProp", "foo+voidLambdaProp") + @Test("Valid function argument") + public validFunctionArgument(func: string, expectResult: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + function takesFunc(fn: (s: string) => s) { return fn("foo"); } + return takesFunc(${func});`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } + + @TestCase("foo.method", "foo.lambdaProp", "foo+lambdaProp") + @TestCase("foo.method", "s => s", "foo") + @TestCase("foo.method", "function(s) { return s; }", "foo") + @TestCase("foo.method", "function(this: Foo, s: string) { return s; }", "foo") + @TestCase("foo.method", "s => func(s)", "foo+func") + @TestCase("foo.method", "s => lambda(s)", "foo+lambda") + @TestCase("foo.method", "Foo.thisStaticMethod", "foo+thisStaticMethod") + @TestCase("foo.method", "Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") + @TestCase("foo.method", "thisFunc", "foo+thisFunc") + @TestCase("foo.method", "thisLambda", "foo+thisLambda") + @TestCase("foo.lambdaProp", "foo.method", "foo+method") + @TestCase("foo.lambdaProp", "s => s", "foo") + @TestCase("foo.lambdaProp", "function(s) { return s; }", "foo") + @TestCase("foo.lambdaProp", "function(this: Foo, s: string) { return s; }", "foo") + @TestCase("foo.lambdaProp", "s => func(s)", "foo+func") + @TestCase("foo.lambdaProp", "s => lambda(s)", "foo+lambda") + @TestCase("foo.lambdaProp", "Foo.thisStaticMethod", "foo+thisStaticMethod") + @TestCase("foo.lambdaProp", "Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") + @TestCase("foo.lambdaProp", "thisFunc", "foo+thisFunc") + @TestCase("foo.lambdaProp", "thisLambda", "foo+thisLambda") + @TestCase("Foo.thisStaticMethod", "foo.method", "foo+method") + @TestCase("Foo.thisStaticMethod", "foo.lambdaProp", "foo+lambdaProp") + @TestCase("Foo.thisStaticMethod", "s => s", "foo") + @TestCase("Foo.thisStaticMethod", "function(s) { return s; }", "foo") + @TestCase("foo.thisStaticMethod", "function(this: Foo, s: string) { return s; }", "foo") + @TestCase("Foo.thisStaticMethod", "s => func(s)", "foo+func") + @TestCase("Foo.thisStaticMethod", "s => lambda(s)", "foo+lambda") + @TestCase("Foo.thisStaticMethod", "Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") + @TestCase("Foo.thisStaticMethod", "thisFunc", "foo+thisFunc") + @TestCase("Foo.thisStaticMethod", "thisLambda", "foo+thisLambda") + @TestCase("Foo.thisStaticLambdaProp", "foo.method", "foo+method") + @TestCase("Foo.thisStaticLambdaProp", "foo.lambdaProp", "foo+lambdaProp") + @TestCase("Foo.thisStaticLambdaProp", "s => s", "foo") + @TestCase("Foo.thisStaticLambdaProp", "function(s) { return s; }", "foo") + @TestCase("foo.thisStaticLambdaProp", "function(this: Foo, s: string) { return s; }", "foo") + @TestCase("Foo.thisStaticLambdaProp", "s => func(s)", "foo+func") + @TestCase("Foo.thisStaticLambdaProp", "s => lambda(s)", "foo+lambda") + @TestCase("Foo.thisStaticLambdaProp", "Foo.thisStaticMethod", "foo+thisStaticMethod") + @TestCase("Foo.thisStaticLambdaProp", "thisFunc", "foo+thisFunc") + @TestCase("Foo.thisStaticLambdaProp", "thisLambda", "foo+thisLambda") + @TestCase("thisFunc", "foo.method", "foo+method") + @TestCase("thisFunc", "foo.lambdaProp", "foo+lambdaProp") + @TestCase("thisFunc", "s => s", "foo") + @TestCase("thisFunc", "function(s) { return s; }", "foo") + @TestCase("thisFunc", "function(this: Foo, s: string) { return s; }", "foo") + @TestCase("thisFunc", "s => func(s)", "foo+func") + @TestCase("thisFunc", "s => lambda(s)", "foo+lambda") + @TestCase("thisFunc", "Foo.thisStaticMethod", "foo+thisStaticMethod") + @TestCase("thisFunc", "Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") + @TestCase("thisFunc", "thisLambda", "foo+thisLambda") + @TestCase("thisLambda", "foo.method", "foo+method") + @TestCase("thisLambda", "foo.lambdaProp", "foo+lambdaProp") + @TestCase("thisLambda", "s => s", "foo") + @TestCase("thisLambda", "function(s) { return s; }", "foo") + @TestCase("thisLambda", "function(this: Foo, s: string) { return s; }", "foo") + @TestCase("thisLambda", "s => func(s)", "foo+func") + @TestCase("thisLambda", "s => lambda(s)", "foo+lambda") + @TestCase("thisLambda", "Foo.thisStaticMethod", "foo+thisStaticMethod") + @TestCase("thisLambda", "Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") + @TestCase("thisLambda", "thisFunc", "foo+thisFunc") + @Test("Valid method assignment") + public validMethodAssignment(func: string, assignTo: string, expectResult: string): void { + const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo}; return ${func}("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } + + @TestCase("foo.method", "foo+method") + @TestCase("foo.lambdaProp", "foo+lambdaProp") + @TestCase("Foo.thisStaticMethod", "foo+thisStaticMethod") + @TestCase("Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") + @TestCase("thisFunc", "foo+thisFunc") + @TestCase("thisLambda", "foo+thisLambda") + @Test("Valid method argument") + public validMethodArgument(func: string, expectResult: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + const foo = new Foo(); + function takesMethod(meth: (this: Foo, s: string) => s) { foo.method = meth; } + takesMethod(${func}); + return foo.method("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } + + @TestCase("func", "foo.method") + @TestCase("func", "foo.lambdaProp") + @TestCase("func", "Foo.thisStaticMethod") + @TestCase("func", "Foo.thisStaticLambdaProp") + @TestCase("func", "function(this: Foo, s: string) { return s; }") + @TestCase("lambda", "foo.method") + @TestCase("lambda", "foo.lambdaProp") + @TestCase("lambda", "Foo.thisStaticMethod") + @TestCase("lambda", "Foo.thisStaticLambdaProp") + @TestCase("lambda", "function(this: Foo, s: string) { return s; }") + @TestCase("foo.voidMethod", "foo.method") + @TestCase("foo.voidMethod", "foo.lambdaProp") + @TestCase("foo.voidMethod", "Foo.thisStaticMethod") + @TestCase("foo.voidMethod", "Foo.thisStaticLambdaProp") + @TestCase("foo.voidMethod", "function(this: Foo, s: string) { return s; }") + @TestCase("foo.voidLambdaProp", "foo.method") + @TestCase("foo.voidLambdaProp", "foo.lambdaProp") + @TestCase("foo.voidLambdaProp", "Foo.thisStaticMethod") + @TestCase("foo.voidLambdaProp", "Foo.thisStaticLambdaProp") + @TestCase("foo.voidLambdaProp", "function(this: Foo, s: string) { return s; }") + @TestCase("Foo.staticMethod", "foo.method") + @TestCase("Foo.staticMethod", "foo.lambdaProp") + @TestCase("Foo.staticMethod", "Foo.thisStaticMethod") + @TestCase("Foo.staticMethod", "Foo.thisStaticLambdaProp") + @TestCase("Foo.staticMethod", "function(this: Foo, s: string) { return s; }") + @TestCase("Foo.staticLambdaProp", "foo.method") + @TestCase("Foo.staticLambdaProp", "foo.lambdaProp") + @TestCase("Foo.staticLambdaProp", "Foo.thisStaticMethod") + @TestCase("Foo.staticLambdaProp", "Foo.thisStaticLambdaProp") + @TestCase("Foo.staticLambdaProp", "function(this: Foo, s: string) { return s; }") + @Test("Invalid function assignment") + public invalidFunctionAssignment(func: string, assignTo: string): void { + const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo};`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from method to function. To fix, wrap the method in an arrow function."); + } + + @TestCase("foo.method") + @TestCase("foo.lambdaProp") + @TestCase("Foo.thisStaticMethod") + @TestCase("Foo.thisStaticLambdaProp") + @TestCase("thisFunc") + @TestCase("thisLambda") + @Test("Invalid function argument") + public invalidFunctionArgument(func: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + declare function takesFunc(fn: (s: string) => s); + takesFunc(${func});`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from method to function \"fn\". To fix, wrap the method in an arrow function."); + } + + @TestCase("foo.method", "func") + @TestCase("foo.method", "lambda") + @TestCase("foo.method", "Foo.staticMethod") + @TestCase("foo.method", "Foo.staticLambdaProp") + @TestCase("foo.method", "foo.voidMethod") + @TestCase("foo.method", "foo.voidLambdaProp") + @TestCase("foo.method", "function(this: void, s: string) { return s; }") + @TestCase("foo.lambdaProp", "func") + @TestCase("foo.lambdaProp", "lambda") + @TestCase("foo.lambdaProp", "Foo.staticMethod") + @TestCase("foo.lambdaProp", "Foo.staticLambdaProp") + @TestCase("foo.lambdaProp", "foo.voidMethod") + @TestCase("foo.lambdaProp", "foo.voidLambdaProp") + @TestCase("foo.lambdaProp", "function(this: void, s: string) { return s; }") + @TestCase("Foo.thisStaticMethod", "func") + @TestCase("Foo.thisStaticMethod", "lambda") + @TestCase("Foo.thisStaticMethod", "Foo.staticMethod") + @TestCase("Foo.thisStaticMethod", "Foo.staticLambdaProp") + @TestCase("Foo.thisStaticMethod", "foo.voidMethod") + @TestCase("Foo.thisStaticMethod", "foo.voidLambdaProp") + @TestCase("Foo.thisStaticMethod", "function(this: void, s: string) { return s; }") + @TestCase("Foo.thisStaticLambdaProp", "func") + @TestCase("Foo.thisStaticLambdaProp", "lambda") + @TestCase("Foo.thisStaticLambdaProp", "Foo.staticMethod") + @TestCase("Foo.thisStaticLambdaProp", "Foo.staticLambdaProp") + @TestCase("Foo.thisStaticLambdaProp", "foo.voidMethod") + @TestCase("Foo.thisStaticLambdaProp", "foo.voidLambdaProp") + @TestCase("Foo.thisStaticLambdaProp", "function(this: void, s: string) { return s; }") + @TestCase("thisFunc", "func") + @TestCase("thisFunc", "lambda") + @TestCase("thisFunc", "Foo.staticMethod") + @TestCase("thisFunc", "Foo.staticLambdaProp") + @TestCase("thisFunc", "foo.voidMethod") + @TestCase("thisFunc", "foo.voidLambdaProp") + @TestCase("thisFunc", "function(this: void, s: string) { return s; }") + @TestCase("thisLambda", "func") + @TestCase("thisLambda", "lambda") + @TestCase("thisLambda", "Foo.staticMethod") + @TestCase("thisLambda", "Foo.staticLambdaProp") + @TestCase("thisLambda", "foo.voidMethod") + @TestCase("thisLambda", "foo.voidLambdaProp") + @TestCase("thisLambda", "function(this: void, s: string) { return s; }") + @Test("Invalid method assignment") + public invalidMethodAssignment(func: string, assignTo: string): void { + const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo};`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from function to method. To fix, wrap the function in an arrow function."); + } + + @TestCase("func") + @TestCase("lambda") + @TestCase("Foo.staticMethod") + @TestCase("Foo.staticLambdaProp") + @TestCase("foo.voidMethod") + @TestCase("foo.voidLambdaProp") + @Test("Invalid method argument") + public invalidMethodArgument(func: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + declare function takesMethod(meth: (this: Foo, s: string) => s); + takesMethod(${func});`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from function to method \"meth\". To fix, wrap the function in an arrow function."); + } + + @Test("Interface method assignment") + public interfaceMethodAssignment(): void { + const code = `class Foo { + method(s: string): string { return s + "+method"; } + lambdaProp: (s: string) => string = s => s + "+lambdaProp"; + } + interface IFoo { + method: (s: string) => string; + lambdaProp(s: string): string; + } + const foo: IFoo = new Foo(); + return foo.method("foo") + "|" + foo.lambdaProp("bar");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe("foo+method|bar+lambdaProp"); + } + + @Test("Valid function tuple assignment") + public validFunctionTupleAssignment(): void { + const code = `interface Func { (s: string): string; } + function getTuple(): [number, Func] { return [1, s => s]; } + let [i, f]: [number, Func] = getTuple(); + return f("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe("foo"); + } + + @Test("Invalid function tuple assignment") + public invalidFunctionTupleAssignment(): void { + const code = `interface Func { (s: string): string; } + interface Meth { (this: {}, s: string): string; } + declare function getTuple(): [number, Meth]; + let [i, f]: [number, Func] = getTuple();`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from method to function. To fix, wrap the method in an arrow function."); + } + + @Test("Valid method tuple assignment") + public validMethodTupleAssignment(): void { + const code = `interface Foo { method(s: string): string; } + interface Meth { (this: Foo, s: string): string; } + let meth: Meth = s => s; + function getTuple(): [number, Meth] { return [1, meth]; } + let [i, f]: [number, Meth] = getTuple(); + let foo: Foo = {method: f}; + return foo.method("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe("foo"); + } + + @Test("Invalid method tuple assignment") + public invalidMethodTupleAssignment(): void { + const code = `interface Func { (s: string): string; } + interface Meth { (this: {}, s: string): string; } + declare function getTuple(): [number, Func]; + let [i, f]: [number, Meth] = getTuple();`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from function to method. To fix, wrap the function in an arrow function."); + } + + @Test("Valid interface method assignment") + public validInterfaceMethodAssignment(): void { + const code = `interface A { fn(this: void, s: string): string; } + interface B { fn(this: void, s: string): string; } + const a: A = { fn: s => s }; + const b: B = a; + return b.fn("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe("foo"); + } + + @Test("Invalid interface method assignment") + public invalidInterfaceMethodAssignment(): void { + const code = `interface A { fn(s: string): string; } + interface B { fn(this: void, s: string): string; } + declare const a: A; + const b: B = a;`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from method to function \"fn\". To fix, wrap the method in an arrow function."); + } + + @TestCase("(s: string) => string", ["foo"], "foobar") + @TestCase("{(s: string): string}", ["foo"], "foobar") + @TestCase("(s1: string, s2: string) => string", ["foo", "baz"], "foobaz") + @TestCase("{(s1: string, s2: string): string}", ["foo", "baz"], "foobaz") + @Test("Valid function overload assignment") + public validFunctionOverloadAssignment(assignType: string, args: string[], expectResult: string): void { + const code = `interface O { + (s1: string, s2: string): string; + (s: string): string; + } + const o: O = (s1: string, s2?: string) => s1 + (s2 || "bar"); + let f: ${assignType} = o; + return f(${args.map(a => "\"" + a + "\"").join(", ")});`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } + + @TestCase("(s: string) => string") + @TestCase("(s1: string, s2: string) => string") + @TestCase("{(s: string): string}") + @TestCase("{(this: {}, s1: string, s2: string): string}") + @Test("Invalid function overload assignment") + public invalidFunctionOverloadAssignment(assignType: string): void { + const code = `interface O { + (this: {}, s1: string, s2: string): string; + (s: string): string; + } + declare const o: O; + let f: ${assignType} = o;`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported assignment of mixed function/method overload. " + + "Overloads should either be all functions or all methods, but not both."); + } } diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index cf0da8287..5cafafe7d 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -319,4 +319,25 @@ export class FunctionTests { `let o = { v: 4, m(i: number): number { return this.v * i; } }; return o.m(3);`); Expect(result).toBe(12); } + + @TestCase(["bar"], "foobar") + @TestCase(["baz", "bar"], "bazbar") + @Test("Function overload") + public functionOverload(args: string[], expectResult: string): void { + const code = `class O { + prop = "foo"; + method(s: string): string; + method(this: void, s1: string, s2: string): string; + method(s1: string) { + if (typeof this === "string") { + return this + s1; + } + return this.prop + s1; + } + }; + const o = new O(); + return o.method(${args.map(a => "\"" + a + "\"").join(", ")});`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } } From 53f35e6672baa2007b431f7669d4fe3bcd98ad7e Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 13 Dec 2018 06:31:22 -0700 Subject: [PATCH 57/59] validating return values and hanlding inference of contexts when passing functions as arguments or return values --- src/Errors.ts | 6 ++- src/TSHelper.ts | 35 ++++++++++++-- src/Transpiler.ts | 5 ++ test/unit/assignments.spec.ts | 91 +++++++++++++++++++++++++++++++++-- 4 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/Errors.ts b/src/Errors.ts index c6d0f0af7..4d8144a14 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -84,11 +84,13 @@ export class TSTLErrors { public static UnsupportedMethodConversion = (node: ts.Node, name?: string) => { if (name) { return new TranspileError(`Unsupported conversion from function to method "${name}". ` - + `To fix, wrap the function in an arrow function.`, + + `To fix, wrap the function in an arrow function or declare the function with` + + ` an explicit 'this' parameter.`, node); } else { return new TranspileError(`Unsupported conversion from function to method. ` - + `To fix, wrap the function in an arrow function.`, + + `To fix, wrap the function in an arrow function or declare the function with` + + ` an explicit 'this' parameter.`, node); } } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index ddb2389ac..121d0414e 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -116,6 +116,15 @@ export class TSHelper { } } + public static getFunctionReturnType(node: ts.Node, checker: ts.TypeChecker): ts.Type { + const declaration = this.findFirstNodeAbove(node, (n): n is ts.Node => ts.isFunctionLike(n)); + if (declaration) { + const signature = checker.getSignatureFromDeclaration(declaration as ts.SignatureDeclaration); + return checker.getReturnTypeOfSignature(signature); + } + return null; + } + public static collectCustomDecorators(symbol: ts.Symbol, checker: ts.TypeChecker, decMap: Map): void { const comments = symbol.getDocumentationComment(checker); @@ -269,11 +278,27 @@ export class TSHelper { if ((ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) && !this.getExplicitThisParameter(signatureDeclaration)) { // Function expressions: get signatures of type being assigned to, unless 'this' was explicit - const declType = checker.getTypeAtLocation(signatureDeclaration.parent); - const declSignatures = declType.getCallSignatures(); - if (declSignatures.length > 0) { - declSignatures.map(s => s.getDeclaration()).forEach(decl => signatureDeclarations.push(decl)); - continue; + let declType: ts.Type; + if (ts.isCallExpression(signatureDeclaration.parent)) { + // Function expression being passed as argument to another function + const i = signatureDeclaration.parent.arguments.indexOf(signatureDeclaration); + if (i >= 0) { + const parentSignature = checker.getResolvedSignature(signatureDeclaration.parent); + const parentSignatureDeclaration = parentSignature.getDeclaration(); + declType = checker.getTypeAtLocation(parentSignatureDeclaration.parameters[i]); + } + } else if (ts.isReturnStatement(signatureDeclaration.parent)) { + declType = this.getFunctionReturnType(signatureDeclaration.parent, checker); + } else { + // Function expression being assigned + declType = checker.getTypeAtLocation(signatureDeclaration.parent); + } + if (declType) { + const declSignatures = declType.getCallSignatures(); + if (declSignatures.length > 0) { + declSignatures.map(s => s.getDeclaration()).forEach(decl => signatureDeclarations.push(decl)); + continue; + } } } signatureDeclarations.push(signatureDeclaration); diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 19adee73f..fca485374 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -702,6 +702,11 @@ export abstract class LuaTranspiler { public transpileReturn(node: ts.ReturnStatement): string { if (node.expression) { + const returnType = tsHelper.getFunctionReturnType(node, this.checker); + if (returnType) { + const expressionType = this.checker.getTypeAtLocation(node.expression); + this.validateAssignment(node, expressionType, returnType); + } if (tsHelper.isInTupleReturnFunction(node, this.checker)) { // Parent function is a TupleReturn function if (ts.isArrayLiteralExpression(node.expression)) { diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index 56109779e..f02a6cbde 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -276,6 +276,9 @@ export class AssignmentTests { @TestCase("Foo.staticLambdaProp", "foo+staticLambdaProp") @TestCase("foo.voidMethod", "foo+voidMethod") @TestCase("foo.voidLambdaProp", "foo+voidLambdaProp") + @TestCase("s => s", "foo") + @TestCase("function(s) { return s; }", "foo") + @TestCase("function(this: void, s: string) { return s; }", "foo") @Test("Valid function argument") public validFunctionArgument(func: string, expectResult: string): void { const code = `${AssignmentTests.funcAssignTestCode} @@ -285,6 +288,25 @@ export class AssignmentTests { Expect(result).toBe(expectResult); } + @TestCase("func", "foo+func") + @TestCase("lambda", "foo+lambda") + @TestCase("Foo.staticMethod", "foo+staticMethod") + @TestCase("Foo.staticLambdaProp", "foo+staticLambdaProp") + @TestCase("foo.voidMethod", "foo+voidMethod") + @TestCase("foo.voidLambdaProp", "foo+voidLambdaProp") + @TestCase("s => s", "foo") + @TestCase("function(s) { return s; }", "foo") + @TestCase("function(this: void, s: string) { return s; }", "foo") + @Test("Valid function return") + public validFunctionReturn(func: string, expectResult: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + function returnsFunc(): (s: string) => string { return ${func}; } + const fn = returnsFunc(); + return fn("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } + @TestCase("foo.method", "foo.lambdaProp", "foo+lambdaProp") @TestCase("foo.method", "s => s", "foo") @TestCase("foo.method", "function(s) { return s; }", "foo") @@ -358,17 +380,38 @@ export class AssignmentTests { @TestCase("Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") @TestCase("thisFunc", "foo+thisFunc") @TestCase("thisLambda", "foo+thisLambda") + @TestCase("s => s", "foo") + @TestCase("function(s) { return s; }", "foo") + @TestCase("function(this: Foo, s: string) { return s; }", "foo") @Test("Valid method argument") public validMethodArgument(func: string, expectResult: string): void { const code = `${AssignmentTests.funcAssignTestCode} - const foo = new Foo(); - function takesMethod(meth: (this: Foo, s: string) => s) { foo.method = meth; } + function takesMethod(meth: (this: Foo, s: string) => string) { foo.method = meth; } takesMethod(${func}); return foo.method("foo");`; const result = util.transpileAndExecute(code); Expect(result).toBe(expectResult); } + @TestCase("foo.method", "foo+method") + @TestCase("foo.lambdaProp", "foo+lambdaProp") + @TestCase("Foo.thisStaticMethod", "foo+thisStaticMethod") + @TestCase("Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") + @TestCase("thisFunc", "foo+thisFunc") + @TestCase("thisLambda", "foo+thisLambda") + @TestCase("s => s", "foo") + @TestCase("function(s) { return s; }", "foo") + @TestCase("function(this: Foo, s: string) { return s; }", "foo") + @Test("Valid method return") + public validMethodReturn(func: string, expectResult: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + function returnMethod(): (this: Foo, s: string) => string { return ${func}; } + foo.method = returnMethod(); + return foo.method("foo");`; + const result = util.transpileAndExecute(code); + Expect(result).toBe(expectResult); + } + @TestCase("func", "foo.method") @TestCase("func", "foo.lambdaProp") @TestCase("func", "Foo.thisStaticMethod") @@ -413,6 +456,7 @@ export class AssignmentTests { @TestCase("Foo.thisStaticLambdaProp") @TestCase("thisFunc") @TestCase("thisLambda") + @TestCase("function(this: Foo, s: string) { return s; }") @Test("Invalid function argument") public invalidFunctionArgument(func: string): void { const code = `${AssignmentTests.funcAssignTestCode} @@ -423,6 +467,22 @@ export class AssignmentTests { "Unsupported conversion from method to function \"fn\". To fix, wrap the method in an arrow function."); } + @TestCase("foo.method") + @TestCase("foo.lambdaProp") + @TestCase("Foo.thisStaticMethod") + @TestCase("Foo.thisStaticLambdaProp") + @TestCase("thisFunc") + @TestCase("thisLambda") + @TestCase("function(this: Foo, s: string) { return s; }") + @Test("Invalid function return") + public invalidFunctionReturn(func: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + function returnsFunc(): (s: string) => string { return ${func}; }`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from method to function. To fix, wrap the method in an arrow function."); + } + @TestCase("foo.method", "func") @TestCase("foo.method", "lambda") @TestCase("foo.method", "Foo.staticMethod") @@ -470,7 +530,8 @@ export class AssignmentTests { const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo};`; Expect(() => util.transpileString(code)).toThrowError( TranspileError, - "Unsupported conversion from function to method. To fix, wrap the function in an arrow function."); + "Unsupported conversion from function to method. To fix, wrap the function in an arrow function or declare" + + " the function with an explicit 'this' parameter."); } @TestCase("func") @@ -479,6 +540,7 @@ export class AssignmentTests { @TestCase("Foo.staticLambdaProp") @TestCase("foo.voidMethod") @TestCase("foo.voidLambdaProp") + @TestCase("function(this: void, s: string) { return s; }") @Test("Invalid method argument") public invalidMethodArgument(func: string): void { const code = `${AssignmentTests.funcAssignTestCode} @@ -486,7 +548,25 @@ export class AssignmentTests { takesMethod(${func});`; Expect(() => util.transpileString(code)).toThrowError( TranspileError, - "Unsupported conversion from function to method \"meth\". To fix, wrap the function in an arrow function."); + "Unsupported conversion from function to method \"meth\". To fix, wrap the function in an arrow function " + + "or declare the function with an explicit 'this' parameter."); + } + + @TestCase("func") + @TestCase("lambda") + @TestCase("Foo.staticMethod") + @TestCase("Foo.staticLambdaProp") + @TestCase("foo.voidMethod") + @TestCase("foo.voidLambdaProp") + @TestCase("function(this: void, s: string) { return s; }") + @Test("Invalid method return") + public invalidMethodReturn(func: string): void { + const code = `${AssignmentTests.funcAssignTestCode} + function returnsMethod(): (this: Foo, s: string) => s { return ${func}; }`; + Expect(() => util.transpileString(code)).toThrowError( + TranspileError, + "Unsupported conversion from function to method. To fix, wrap the function in an arrow function " + + "or declare the function with an explicit 'this' parameter."); } @Test("Interface method assignment") @@ -547,7 +627,8 @@ export class AssignmentTests { let [i, f]: [number, Meth] = getTuple();`; Expect(() => util.transpileString(code)).toThrowError( TranspileError, - "Unsupported conversion from function to method. To fix, wrap the function in an arrow function."); + "Unsupported conversion from function to method. To fix, wrap the function in an arrow function or declare" + + " the function with an explicit 'this' parameter."); } @Test("Valid interface method assignment") From 72d0edcfdd8d7f747bb948d077483f19c7a454ce Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 14 Dec 2018 05:58:43 -0700 Subject: [PATCH 58/59] renamed getFunctionReturnType to getContainingFunctionReturnType --- src/TSHelper.ts | 4 ++-- src/Transpiler.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 121d0414e..991d8bb5c 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -116,7 +116,7 @@ export class TSHelper { } } - public static getFunctionReturnType(node: ts.Node, checker: ts.TypeChecker): ts.Type { + public static getContainingFunctionReturnType(node: ts.Node, checker: ts.TypeChecker): ts.Type { const declaration = this.findFirstNodeAbove(node, (n): n is ts.Node => ts.isFunctionLike(n)); if (declaration) { const signature = checker.getSignatureFromDeclaration(declaration as ts.SignatureDeclaration); @@ -288,7 +288,7 @@ export class TSHelper { declType = checker.getTypeAtLocation(parentSignatureDeclaration.parameters[i]); } } else if (ts.isReturnStatement(signatureDeclaration.parent)) { - declType = this.getFunctionReturnType(signatureDeclaration.parent, checker); + declType = this.getContainingFunctionReturnType(signatureDeclaration.parent, checker); } else { // Function expression being assigned declType = checker.getTypeAtLocation(signatureDeclaration.parent); diff --git a/src/Transpiler.ts b/src/Transpiler.ts index fca485374..3b3c09d14 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -702,7 +702,7 @@ export abstract class LuaTranspiler { public transpileReturn(node: ts.ReturnStatement): string { if (node.expression) { - const returnType = tsHelper.getFunctionReturnType(node, this.checker); + const returnType = tsHelper.getContainingFunctionReturnType(node, this.checker); if (returnType) { const expressionType = this.checker.getTypeAtLocation(node.expression); this.validateAssignment(node, expressionType, returnType); From b4b0820b1d6b29f786638ca31fc197d8ae2ff43e Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 14 Dec 2018 16:00:51 -0700 Subject: [PATCH 59/59] handling more edge cases, adding more tests and a little bit of refactoring --- src/TSHelper.ts | 38 ++++++++--- src/Transpiler.ts | 14 ++-- test/translation/lua/namespaceMerge.lua | 2 +- test/unit/assignments.spec.ts | 90 ++++++++++++++++++++----- test/unit/functions.spec.ts | 19 ++++++ 5 files changed, 129 insertions(+), 34 deletions(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 991d8bb5c..0ef1e355b 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -117,9 +117,9 @@ export class TSHelper { } public static getContainingFunctionReturnType(node: ts.Node, checker: ts.TypeChecker): ts.Type { - const declaration = this.findFirstNodeAbove(node, (n): n is ts.Node => ts.isFunctionLike(n)); + const declaration = this.findFirstNodeAbove(node, ts.isFunctionLike); if (declaration) { - const signature = checker.getSignatureFromDeclaration(declaration as ts.SignatureDeclaration); + const signature = checker.getSignatureFromDeclaration(declaration); return checker.getReturnTypeOfSignature(signature); } return null; @@ -320,7 +320,8 @@ export class TSHelper { return ContextType.NonVoid; } if ((ts.isPropertySignature(signatureDeclaration.parent) - || ts.isPropertyDeclaration(signatureDeclaration.parent)) + || ts.isPropertyDeclaration(signatureDeclaration.parent) + || ts.isPropertyAssignment(signatureDeclaration.parent)) && !(ts.getCombinedModifierFlags(signatureDeclaration.parent) & ts.ModifierFlags.Static)) { // Non-static lambda property return ContextType.NonVoid; @@ -332,18 +333,35 @@ export class TSHelper { return ContextType.Void; } + public static reduceContextTypes(contexts: ContextType[]): ContextType { + const reducer = (a: ContextType, b: ContextType) => { + if (a === ContextType.None) { + return b; + } else if (b === ContextType.None) { + return a; + } else if (a !== b) { + return ContextType.Mixed; + } else { + return a; + } + }; + return contexts.reduce(reducer, ContextType.None); + } + public static getFunctionContextType(type: ts.Type, checker: ts.TypeChecker): ContextType { + if (type.isTypeParameter()) { + type = type.getConstraint() || type; + } + + if (type.isUnion()) { + return this.reduceContextTypes(type.types.map(t => this.getFunctionContextType(t, checker))); + } + const signatures = checker.getSignaturesOfType(type, ts.SignatureKind.Call); if (signatures.length === 0) { return ContextType.None; } const signatureDeclarations = this.getSignatureDeclarations(signatures, checker); - const context = this.getDeclarationContextType(signatureDeclarations[0], checker); - for (let i = 1; i < signatureDeclarations.length; ++i) { - if (this.getDeclarationContextType(signatureDeclarations[i], checker) !== context) { - return ContextType.Mixed; - } - } - return context; + return this.reduceContextTypes(signatureDeclarations.map(s => this.getDeclarationContextType(s, checker))); } } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 3b3c09d14..845b42b9f 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -800,7 +800,7 @@ export abstract class LuaTranspiler { case ts.SyntaxKind.FunctionExpression: return this.transpileFunctionExpression(node as ts.ArrowFunction, "self"); case ts.SyntaxKind.ArrowFunction: - return this.transpileFunctionExpression(node as ts.ArrowFunction, "_"); + return this.transpileFunctionExpression(node as ts.ArrowFunction, "____"); case ts.SyntaxKind.NewExpression: return this.transpileNewExpression(node as ts.NewExpression); case ts.SyntaxKind.ComputedPropertyName: @@ -1447,7 +1447,7 @@ export abstract class LuaTranspiler { const toContext = tsHelper.getFunctionContextType(toType, this.checker); if (fromContext === ContextType.Mixed || toContext === ContextType.Mixed) { throw TSTLErrors.UnsupportedOverloadAssignment(node, toName); - } else if (fromContext !== toContext) { + } else if (fromContext !== toContext && fromContext !== ContextType.None && toContext !== ContextType.None) { if (toContext === ContextType.Void) { throw TSTLErrors.UnsupportedFunctionConversion(node, toName); } else { @@ -1469,10 +1469,12 @@ export abstract class LuaTranspiler { toType.symbol.members.forEach( (toMember, memberName) => { const fromMember = fromType.symbol.members.get(memberName); - const toMemberType = this.checker.getTypeOfSymbolAtLocation(toMember, node); - const fromMemberType = this.checker.getTypeOfSymbolAtLocation(fromMember, node); - this.validateAssignment(node, fromMemberType, toMemberType, - toName ? `${toName}.${memberName}` : memberName.toString()); + if (fromMember) { + const toMemberType = this.checker.getTypeOfSymbolAtLocation(toMember, node); + const fromMemberType = this.checker.getTypeOfSymbolAtLocation(fromMember, node); + this.validateAssignment(node, fromMemberType, toMemberType, + toName ? `${toName}.${memberName}` : memberName.toString()); + } } ); } diff --git a/test/translation/lua/namespaceMerge.lua b/test/translation/lua/namespaceMerge.lua index 3259d6488..ab110d69c 100644 --- a/test/translation/lua/namespaceMerge.lua +++ b/test/translation/lua/namespaceMerge.lua @@ -2,7 +2,7 @@ MergedClass = MergedClass or {} MergedClass.__index = MergedClass function MergedClass.new(construct, ...) local self = setmetatable({}, MergedClass) - self.propertyFunc = function(_) + self.propertyFunc = function(____) end if construct and MergedClass.constructor then MergedClass.constructor(self, ...) end return self diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index f02a6cbde..c688d5e9f 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -263,6 +263,7 @@ export class AssignmentTests { @TestCase("foo.voidLambdaProp", "Foo.staticMethod", "foo+staticMethod") @TestCase("foo.voidLambdaProp", "Foo.staticLambdaProp", "foo+staticLambdaProp") @TestCase("foo.voidLambdaProp", "foo.voidMethod", "foo+voidMethod") + @TestCase("func", "(func as (string | ((s: string) => string)))", "foo+func") @Test("Valid function assignment") public validFunctionAssignment(func: string, assignTo: string, expectResult: string): void { const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo}; return ${func}("foo");`; @@ -279,10 +280,17 @@ export class AssignmentTests { @TestCase("s => s", "foo") @TestCase("function(s) { return s; }", "foo") @TestCase("function(this: void, s: string) { return s; }", "foo") + @TestCase("func", "foo+func", "string | ((s: string) => string)") + @TestCase("func", "foo+func", "T") @Test("Valid function argument") - public validFunctionArgument(func: string, expectResult: string): void { + public validFunctionArgument(func: string, expectResult: string, funcType?: string): void { + if (!funcType) { + funcType = "(s: string) => s"; + } const code = `${AssignmentTests.funcAssignTestCode} - function takesFunc(fn: (s: string) => s) { return fn("foo"); } + function takesFunc string)>(fn: ${funcType}) { + return (fn as any)("foo"); + } return takesFunc(${func});`; const result = util.transpileAndExecute(code); Expect(result).toBe(expectResult); @@ -297,10 +305,17 @@ export class AssignmentTests { @TestCase("s => s", "foo") @TestCase("function(s) { return s; }", "foo") @TestCase("function(this: void, s: string) { return s; }", "foo") + @TestCase("func", "foo+func", "string | ((s: string) => string)") + @TestCase("func", "foo+func", "T") @Test("Valid function return") - public validFunctionReturn(func: string, expectResult: string): void { + public validFunctionReturn(func: string, expectResult: string, funcType?: string): void { + if (!funcType) { + funcType = "(s: string) => s"; + } const code = `${AssignmentTests.funcAssignTestCode} - function returnsFunc(): (s: string) => string { return ${func}; } + function returnsFunc string)>(): ${funcType} { + return ${func}; + } const fn = returnsFunc(); return fn("foo");`; const result = util.transpileAndExecute(code); @@ -367,6 +382,7 @@ export class AssignmentTests { @TestCase("thisLambda", "Foo.thisStaticMethod", "foo+thisStaticMethod") @TestCase("thisLambda", "Foo.thisStaticLambdaProp", "foo+thisStaticLambdaProp") @TestCase("thisLambda", "thisFunc", "foo+thisFunc") + @TestCase("foo.method", "(foo.method as (string | ((this: Foo, s: string) => string))", "foo+method") @Test("Valid method assignment") public validMethodAssignment(func: string, assignTo: string, expectResult: string): void { const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo}; return ${func}("foo");`; @@ -383,10 +399,17 @@ export class AssignmentTests { @TestCase("s => s", "foo") @TestCase("function(s) { return s; }", "foo") @TestCase("function(this: Foo, s: string) { return s; }", "foo") + @TestCase("foo.method", "foo+method", "string | ((this: Foo, s: string) => string)") + @TestCase("foo.method", "foo+method", "T") @Test("Valid method argument") - public validMethodArgument(func: string, expectResult: string): void { + public validMethodArgument(func: string, expectResult: string, funcType?: string): void { + if (!funcType) { + funcType = "(this: Foo, s: string) => string"; + } const code = `${AssignmentTests.funcAssignTestCode} - function takesMethod(meth: (this: Foo, s: string) => string) { foo.method = meth; } + function takesMethod string)>(meth: ${funcType}) { + foo.method = meth as any; + } takesMethod(${func}); return foo.method("foo");`; const result = util.transpileAndExecute(code); @@ -402,10 +425,17 @@ export class AssignmentTests { @TestCase("s => s", "foo") @TestCase("function(s) { return s; }", "foo") @TestCase("function(this: Foo, s: string) { return s; }", "foo") + @TestCase("foo.method", "foo+method", "string | ((this: Foo, s: string) => string)") + @TestCase("foo.method", "foo+method", "T") @Test("Valid method return") - public validMethodReturn(func: string, expectResult: string): void { + public validMethodReturn(func: string, expectResult: string, funcType?: string): void { + if (!funcType) { + funcType = "(this: Foo, s: string) => string"; + } const code = `${AssignmentTests.funcAssignTestCode} - function returnMethod(): (this: Foo, s: string) => string { return ${func}; } + function returnMethod string)>(): ${funcType} { + return ${func}; + } foo.method = returnMethod(); return foo.method("foo");`; const result = util.transpileAndExecute(code); @@ -442,6 +472,7 @@ export class AssignmentTests { @TestCase("Foo.staticLambdaProp", "Foo.thisStaticMethod") @TestCase("Foo.staticLambdaProp", "Foo.thisStaticLambdaProp") @TestCase("Foo.staticLambdaProp", "function(this: Foo, s: string) { return s; }") + @TestCase("func", "(foo.method as (string | ((this: Foo, s: string) => string)))") @Test("Invalid function assignment") public invalidFunctionAssignment(func: string, assignTo: string): void { const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo};`; @@ -457,10 +488,15 @@ export class AssignmentTests { @TestCase("thisFunc") @TestCase("thisLambda") @TestCase("function(this: Foo, s: string) { return s; }") + @TestCase("foo.method", "string | ((s: string) => string)") + @TestCase("foo.method", "T") @Test("Invalid function argument") - public invalidFunctionArgument(func: string): void { + public invalidFunctionArgument(func: string, funcType?: string): void { + if (!funcType) { + funcType = "(s: string) => s"; + } const code = `${AssignmentTests.funcAssignTestCode} - declare function takesFunc(fn: (s: string) => s); + declare function takesFunc string)>(fn: ${funcType}); takesFunc(${func});`; Expect(() => util.transpileString(code)).toThrowError( TranspileError, @@ -474,10 +510,17 @@ export class AssignmentTests { @TestCase("thisFunc") @TestCase("thisLambda") @TestCase("function(this: Foo, s: string) { return s; }") + @TestCase("foo.method", "string | ((s: string) => string)") + @TestCase("foo.method", "T") @Test("Invalid function return") - public invalidFunctionReturn(func: string): void { + public invalidFunctionReturn(func: string, funcType?: string): void { + if (!funcType) { + funcType = "(s: string) => s"; + } const code = `${AssignmentTests.funcAssignTestCode} - function returnsFunc(): (s: string) => string { return ${func}; }`; + function returnsFunc string)>(): ${funcType} { + return ${func}; + }`; Expect(() => util.transpileString(code)).toThrowError( TranspileError, "Unsupported conversion from method to function. To fix, wrap the method in an arrow function."); @@ -525,6 +568,7 @@ export class AssignmentTests { @TestCase("thisLambda", "foo.voidMethod") @TestCase("thisLambda", "foo.voidLambdaProp") @TestCase("thisLambda", "function(this: void, s: string) { return s; }") + @TestCase("foo.method", "(func as string | ((s: string) => string))") @Test("Invalid method assignment") public invalidMethodAssignment(func: string, assignTo: string): void { const code = `${AssignmentTests.funcAssignTestCode} ${func} = ${assignTo};`; @@ -541,10 +585,15 @@ export class AssignmentTests { @TestCase("foo.voidMethod") @TestCase("foo.voidLambdaProp") @TestCase("function(this: void, s: string) { return s; }") + @TestCase("func", "string | ((this: Foo, s: string) => string)") + @TestCase("func", "T") @Test("Invalid method argument") - public invalidMethodArgument(func: string): void { + public invalidMethodArgument(func: string, funcType?: string): void { + if (!funcType) { + funcType = "(this: Foo, s: string) => string"; + } const code = `${AssignmentTests.funcAssignTestCode} - declare function takesMethod(meth: (this: Foo, s: string) => s); + declare function takesMethod string)>(meth: ${funcType}); takesMethod(${func});`; Expect(() => util.transpileString(code)).toThrowError( TranspileError, @@ -559,10 +608,17 @@ export class AssignmentTests { @TestCase("foo.voidMethod") @TestCase("foo.voidLambdaProp") @TestCase("function(this: void, s: string) { return s; }") + @TestCase("func", "string | ((this: Foo, s: string) => string)") + @TestCase("func", "T") @Test("Invalid method return") - public invalidMethodReturn(func: string): void { + public invalidMethodReturn(func: string, funcType?: string): void { + if (!funcType) { + funcType = "(this: Foo, s: string) => string"; + } const code = `${AssignmentTests.funcAssignTestCode} - function returnsMethod(): (this: Foo, s: string) => s { return ${func}; }`; + function returnsMethod string)>(): ${funcType} { + return ${func}; + }`; Expect(() => util.transpileString(code)).toThrowError( TranspileError, "Unsupported conversion from function to method. To fix, wrap the function in an arrow function " @@ -635,7 +691,7 @@ export class AssignmentTests { public validInterfaceMethodAssignment(): void { const code = `interface A { fn(this: void, s: string): string; } interface B { fn(this: void, s: string): string; } - const a: A = { fn: s => s }; + const a: A = { fn(this: void, s) { return s; } }; const b: B = a; return b.fn("foo");`; const result = util.transpileAndExecute(code); diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 5cafafe7d..e5c1ca9c6 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -340,4 +340,23 @@ export class FunctionTests { const result = util.transpileAndExecute(code); Expect(result).toBe(expectResult); } + + @Test("Nested Function") + public nestedFunction(): void { + const code = `class C { + private prop = "bar"; + public outer() { + const o = { + prop: "foo", + innerFunc: function() { return this.prop; }, + innerArrow: () => this.prop + }; + return o.innerFunc() + o.innerArrow(); + } + } + let c = new C(); + return c.outer();`; + const result = util.transpileAndExecute(code); + Expect(result).toBe("foobar"); + } }