diff --git a/src/Decorator.ts b/src/Decorator.ts index e0b3b6a00..c73dff6b2 100644 --- a/src/Decorator.ts +++ b/src/Decorator.ts @@ -21,4 +21,5 @@ export enum DecoratorKind { Phantom = "Phantom", TupleReturn = "TupleReturn", NoClassOr = "NoClassOr", + NoContext = "NoContext", } diff --git a/src/TSHelper.ts b/src/TSHelper.ts index d35973f32..03db7e577 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -79,6 +79,11 @@ export class TSHelper { return typeNode && this.isArrayTypeNode(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 { if (ts.isCallExpression(node)) { const type = checker.getTypeAtLocation(node.expression); @@ -104,22 +109,29 @@ export class TSHelper { } } + public static collectCustomDecorators(symbol: ts.Symbol, checker: ts.TypeChecker, + decMap: Map): void { + const comments = symbol.getDocumentationComment(checker); + const decorators = + comments.filter(comment => comment.kind === "text") + .map(comment => comment.text.trim().split("\n")) + .reduce((a, b) => a.concat(b), []) + .filter(comment => comment[0] === "!"); + decorators.forEach(decStr => { + const dec = new Decorator(decStr); + decMap.set(dec.kind, dec); + }); + } + public static getCustomDecorators(type: ts.Type, checker: ts.TypeChecker): Map { + const decMap = new Map(); if (type.symbol) { - const comments = type.symbol.getDocumentationComment(checker); - const decorators = - comments.filter(comment => comment.kind === "text") - .map(comment => comment.text.trim().split("\n")) - .reduce((a, b) => a.concat(b), []) - .filter(comment => comment[0] === "!"); - const decMap = new Map(); - decorators.forEach(decStr => { - const dec = new Decorator(decStr); - decMap.set(dec.kind, dec); - }); - return decMap; + this.collectCustomDecorators(type.symbol, checker, decMap); + } + if (type.aliasSymbol) { + this.collectCustomDecorators(type.aliasSymbol, checker, decMap); } - return new Map(); + return decMap; } // Search up until finding a node satisfying the callback diff --git a/src/Transpiler.ts b/src/Transpiler.ts index bfbd58a6e..06df32d2f 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -33,6 +33,9 @@ export enum LuaLibFeature { ArraySlice = "ArraySlice", ArraySome = "ArraySome", ArraySplice = "ArraySplice", + FunctionApply = "FunctionApply", + FunctionBind = "FunctionBind", + FunctionCall = "FunctionCall", InstanceOf = "InstanceOf", Map = "Map", Set = "Set", @@ -784,8 +787,9 @@ export abstract class LuaTranspiler { case ts.SyntaxKind.DeleteExpression: return this.transpileExpression((node as ts.DeleteExpression).expression) + "=nil"; 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: @@ -1136,7 +1140,7 @@ export abstract class LuaTranspiler { public transpileNewExpression(node: ts.NewExpression): string { const name = this.transpileExpression(node.expression); - const params = node.arguments ? this.transpileArguments(node.arguments, ts.createTrue()) : "true"; + let params = node.arguments ? this.transpileArguments(node.arguments, ts.createTrue()) : "true"; const type = this.checker.getTypeAtLocation(node); const classDecorators = tsHelper.getCustomDecorators(type, this.checker); @@ -1151,7 +1155,14 @@ export abstract class LuaTranspiler { if (!customDecorator.args[0]) { throw TSTLErrors.InvalidDecoratorArgumentNumber("!CustomConstructor", 0, 1, node); } - return `${customDecorator.args[0]}(${this.transpileArguments(node.arguments)})`; + 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")); + } else { + params = this.transpileArguments(node.arguments); + } + return `${customDecorator.args[0]}(${params})`; } return `${name}.new(${params})`; @@ -1182,7 +1193,14 @@ export abstract class LuaTranspiler { } callPath = this.transpileExpression(node.expression); - params = this.transpileArguments(node.arguments); + const type = this.checker.getTypeAtLocation(node.expression); + 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")); + } else { + params = this.transpileArguments(node.arguments); + } return isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment && returnValueIsUsed ? `({ ${callPath}(${params}) })` : `${callPath}(${params})`; } @@ -1220,16 +1238,12 @@ export abstract class LuaTranspiler { return this.transpileArrayCallExpression(node); } + if (tsHelper.isFunctionType(ownerType, this.checker)) { + return this.transpileFunctionCallExpression(node); + } + // Get the type of the function - const functionType = this.checker.getTypeAtLocation(node.expression); - // Don't replace . with : for namespaces or functions defined as properties with lambdas - if ((functionType.symbol && !(functionType.symbol.flags & ts.SymbolFlags.Method)) - // Check explicitly for method calls on 'this', since they don't have the Method flag set - || (node.expression.expression.kind === ts.SyntaxKind.ThisType)) { - callPath = this.transpileExpression(node.expression); - params = this.transpileArguments(node.arguments); - return `${callPath}(${params})`; - } else if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) { + 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); return `${this.transpileExpression(node.expression)}(${params})`; @@ -1243,8 +1257,9 @@ export abstract class LuaTranspiler { params = this.transpileArguments(node.arguments); return `(rawget(${expr}, ${params} )~=nil)`; } else { - callPath = - `${this.transpileExpression(node.expression.expression)}:${name}`; + 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); return `${callPath}(${params})`; } @@ -1364,6 +1379,23 @@ export abstract class LuaTranspiler { } } + public transpileFunctionCallExpression(node: ts.CallExpression): string { + const expression = node.expression as ts.PropertyAccessExpression; + const params = this.transpileArguments(node.arguments); + const caller = this.transpileExpression(expression.expression); + const expressionName = this.transpileIdentifier(expression.name); + switch (expressionName) { + case "apply": + return this.transpileLuaLibFunction(LuaLibFeature.FunctionApply, caller, params); + case "bind": + return this.transpileLuaLibFunction(LuaLibFeature.FunctionBind, caller, params); + case "call": + return this.transpileLuaLibFunction(LuaLibFeature.FunctionCall, caller, params); + default: + throw TSTLErrors.UnsupportedProperty("function", expressionName, node); + } + } + public transpileArguments(params: ts.NodeArray, context?: ts.Expression): string { const parameters: string[] = []; @@ -1596,7 +1628,9 @@ export abstract class LuaTranspiler { let result = ""; const methodName = this.transpileIdentifier(node.name); - const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters); + const type = this.checker.getTypeAtLocation(node); + const context = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? null : "self"; + const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); let prefix = this.accessPrefix(node); @@ -1620,9 +1654,13 @@ export abstract class LuaTranspiler { } // Transpile a list of parameters, returns a list of transpiled parameters and an optional spread identifier - public transpileParameters(parameters: ts.NodeArray): [string[], string] { + public transpileParameters(parameters: ts.NodeArray, context: string | null) + : [string[], string] { // Build parameter string const paramNames: string[] = []; + if (context) { + paramNames.push(context); + } let spreadIdentifier = ""; @@ -1673,12 +1711,12 @@ export abstract class LuaTranspiler { methodName = "__tostring"; } - const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters); - - const selfParamNames = ["self"].concat(paramNames); + const type = this.checker.getTypeAtLocation(node); + const context = tsHelper.getCustomDecorators(type, this.checker).has(DecoratorKind.NoContext) ? null : "self"; + const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); // Build function header - result += this.indent + `function ${callPath}${methodName}(${selfParamNames.join(",")})\n`; + result += this.indent + `function ${callPath}${methodName}(${paramNames.join(",")})\n`; this.pushIndent(); result += this.transpileFunctionBody(node.parameters, node.body, spreadIdentifier); @@ -1932,7 +1970,7 @@ export abstract class LuaTranspiler { } else if (ts.isShorthandPropertyAssignment(element)) { properties.push(`${name} = ${name}`); } else if (ts.isMethodDeclaration(element)) { - const expression = this.transpileFunctionExpression(element); + const expression = this.transpileFunctionExpression(element, "self"); properties.push(`${name} = ${expression}`); } else { throw TSTLErrors.UnsupportedKind("object literal element", element.kind, node); @@ -1942,30 +1980,15 @@ export abstract class LuaTranspiler { return "{" + properties.join(",") + "}"; } - public transpileFunctionExpression(node: ts.FunctionLikeDeclaration): string { + public transpileFunctionExpression(node: ts.FunctionLikeDeclaration, context: string | null): string { // Build parameter string - const paramNames: string[] = []; - if (ts.isMethodDeclaration(node)) { - paramNames.push("self"); - } - node.parameters.forEach(param => { - paramNames.push(this.transpileIdentifier(param.name as ts.Identifier)); - }); - - const defaultValueParams = node.parameters.filter(declaration => declaration.initializer !== undefined); - - if (ts.isBlock(node.body) || defaultValueParams.length > 0) { - let result = `function(${paramNames.join(",")})\n`; - this.pushIndent(); - result += this.transpileParameterDefaultValues(defaultValueParams); - result += this.transpileBlock(node.body as ts.Block); - this.popIndent(); - return result + this.indent + "end\n"; - } else { - // Transpile as return value - const returnVal = this.transpileReturn(ts.createReturn(node.body)); - return `function(${paramNames.join(",")}) ${returnVal} end`; - } + const [paramNames, spreadIdentifier] = this.transpileParameters(node.parameters, context); + let result = `function(${paramNames.join(",")})\n`; + this.pushIndent(); + const body = ts.isBlock(node.body) ? node.body : ts.createBlock([ts.createReturn(node.body)]); + result += this.transpileFunctionBody(node.parameters, body, spreadIdentifier); + this.popIndent(); + return result + this.indent + "end"; } public transpileParameterDefaultValues(params: ts.ParameterDeclaration[]): string { diff --git a/src/lualib/ArrayConcat.ts b/src/lualib/ArrayConcat.ts index 06d5206b3..145673ab1 100644 --- a/src/lualib/ArrayConcat.ts +++ b/src/lualib/ArrayConcat.ts @@ -1,6 +1,9 @@ +/** !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 e8434175a..7774eb911 100644 --- a/src/lualib/ArrayEvery.ts +++ b/src/lualib/ArrayEvery.ts @@ -1,3 +1,4 @@ +/** !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 8f0ac2fde..d0495c399 100644 --- a/src/lualib/ArrayFilter.ts +++ b/src/lualib/ArrayFilter.ts @@ -1,3 +1,4 @@ +/** !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 3001915f0..28cd54b8b 100644 --- a/src/lualib/ArrayForEach.ts +++ b/src/lualib/ArrayForEach.ts @@ -1,3 +1,4 @@ +/** !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 c50ce5ded..fd2e19da5 100644 --- a/src/lualib/ArrayIndexOf.ts +++ b/src/lualib/ArrayIndexOf.ts @@ -1,3 +1,4 @@ +/** !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 f7ea7aa50..dd595b329 100644 --- a/src/lualib/ArrayMap.ts +++ b/src/lualib/ArrayMap.ts @@ -1,3 +1,4 @@ +/** !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 8e1d8e324..104663375 100644 --- a/src/lualib/ArrayPush.ts +++ b/src/lualib/ArrayPush.ts @@ -1,3 +1,4 @@ +/** !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 3c4839417..65349a905 100644 --- a/src/lualib/ArrayReverse.ts +++ b/src/lualib/ArrayReverse.ts @@ -1,3 +1,4 @@ +/** !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 a95df1a49..11a5b9a89 100644 --- a/src/lualib/ArrayShift.ts +++ b/src/lualib/ArrayShift.ts @@ -1,6 +1,8 @@ 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 f3eb5b3d6..bfb0d021c 100644 --- a/src/lualib/ArraySlice.ts +++ b/src/lualib/ArraySlice.ts @@ -1,4 +1,5 @@ // 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 d03e7a9fe..ad2817eac 100644 --- a/src/lualib/ArraySome.ts +++ b/src/lualib/ArraySome.ts @@ -1,3 +1,4 @@ +/** !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 18745695c..2e02d2572 100644 --- a/src/lualib/ArraySort.ts +++ b/src/lualib/ArraySort.ts @@ -1,6 +1,8 @@ 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 b875d706c..2c20c98e0 100644 --- a/src/lualib/ArraySplice.ts +++ b/src/lualib/ArraySplice.ts @@ -1,3 +1,4 @@ +/** !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 cb0031100..3c5508b02 100644 --- a/src/lualib/ArrayUnshift.ts +++ b/src/lualib/ArrayUnshift.ts @@ -1,6 +1,8 @@ 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 new file mode 100644 index 000000000..c58691959 --- /dev/null +++ b/src/lualib/FunctionApply.ts @@ -0,0 +1,19 @@ +/** !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)); + } else { + return fn(thisArg); + } +} diff --git a/src/lualib/FunctionBind.ts b/src/lualib/FunctionBind.ts new file mode 100644 index 000000000..fad7c70ae --- /dev/null +++ b/src/lualib/FunctionBind.ts @@ -0,0 +1,23 @@ +/** !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) { + table.insert(argArray, i + 1, boundArgs[i]); + } + return fn(thisArg, (unpack || table.unpack)(argArray)); + }; +} diff --git a/src/lualib/FunctionCall.ts b/src/lualib/FunctionCall.ts new file mode 100644 index 000000000..5f7acd3d7 --- /dev/null +++ b/src/lualib/FunctionCall.ts @@ -0,0 +1,15 @@ +/** !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 3b6a12dab..eb013aaaf 100644 --- a/src/lualib/InstanceOf.ts +++ b/src/lualib/InstanceOf.ts @@ -3,6 +3,7 @@ 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 7a5df979c..f5b6fd4d3 100644 --- a/src/lualib/StringReplace.ts +++ b/src/lualib/StringReplace.ts @@ -1,8 +1,10 @@ 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 005f57436..862e71ddc 100644 --- a/src/lualib/StringSplit.ts +++ b/src/lualib/StringSplit.ts @@ -1,3 +1,4 @@ +/** !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 f1331f4f4..4e2201d7d 100644 --- a/src/lualib/Ternary.ts +++ b/src/lualib/Ternary.ts @@ -1,3 +1,4 @@ +/** !NoContext */ function __TS__Ternary(condition: boolean, cb1: () => T, cb2: () => T): T { if (condition) { return cb1(); diff --git a/test/translation/lua/callNamespace.lua b/test/translation/lua/callNamespace.lua index 6d3add272..40b907163 100644 --- a/test/translation/lua/callNamespace.lua +++ b/test/translation/lua/callNamespace.lua @@ -1 +1 @@ -Namespace.myFunction(); \ No newline at end of file +Namespace:myFunction(); diff --git a/test/translation/lua/dotColonFunctionCalls.lua b/test/translation/lua/dotColonFunctionCalls.lua index 8ffa06577..5deaffabb 100644 --- a/test/translation/lua/dotColonFunctionCalls.lua +++ b/test/translation/lua/dotColonFunctionCalls.lua @@ -1,6 +1,6 @@ classInstance:colonMethod(); -classInstance.dotMethod(); +classInstance:dotMethod(); interfaceInstance:colonMethod(); -interfaceInstance.dotMethod(); -TestNameSpace.dotMethod(); -TestNameSpace.dotMethod2(); +interfaceInstance:dotMethod(); +TestNameSpace:dotMethod(); +TestNameSpace:dotMethod2(); diff --git a/test/translation/lua/functionRestArguments.lua b/test/translation/lua/functionRestArguments.lua index 78d60a18e..f2db4825d 100644 --- a/test/translation/lua/functionRestArguments.lua +++ b/test/translation/lua/functionRestArguments.lua @@ -1,3 +1,3 @@ -function varargsFunction(a,...) +function varargsFunction(self,a,...) local b = { ... } end diff --git a/test/translation/lua/modulesFunctionExport.lua b/test/translation/lua/modulesFunctionExport.lua index 5b447e5b2..6fce9cb9e 100644 --- a/test/translation/lua/modulesFunctionExport.lua +++ b/test/translation/lua/modulesFunctionExport.lua @@ -1,5 +1,5 @@ local exports = exports or {} -local function publicFunc() +local function publicFunc(self) end exports.publicFunc = publicFunc return exports diff --git a/test/translation/lua/modulesFunctionNoExport.lua b/test/translation/lua/modulesFunctionNoExport.lua index e0e619215..855900df8 100644 --- a/test/translation/lua/modulesFunctionNoExport.lua +++ b/test/translation/lua/modulesFunctionNoExport.lua @@ -1,2 +1,2 @@ -function publicFunc() +function publicFunc(self) end diff --git a/test/translation/lua/modulesNamespaceNestedWithMemberExport.lua b/test/translation/lua/modulesNamespaceNestedWithMemberExport.lua index 326a00294..1c9884b12 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() + local function innerFunc(self) end TestNestedSpace.innerFunc = innerFunc end diff --git a/test/translation/lua/modulesNamespaceWithMemberExport.lua b/test/translation/lua/modulesNamespaceWithMemberExport.lua index 7e9204160..fe2a50e3c 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() + local function innerFunc(self) end TestSpace.innerFunc = innerFunc end diff --git a/test/translation/lua/modulesNamespaceWithMemberNoExport.lua b/test/translation/lua/modulesNamespaceWithMemberNoExport.lua index a2afe84fe..752f8edfe 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() + local function innerFunc(self) end end exports.TestSpace = TestSpace diff --git a/test/translation/lua/namespace.lua b/test/translation/lua/namespace.lua index c5a266c7f..dd63c6b89 100644 --- a/test/translation/lua/namespace.lua +++ b/test/translation/lua/namespace.lua @@ -1,5 +1,5 @@ myNamespace = myNamespace or {} do - local function nsMember() + local function nsMember(self) end end diff --git a/test/translation/lua/namespaceMerge.lua b/test/translation/lua/namespaceMerge.lua index c3dcc8794..cb5157727 100644 --- a/test/translation/lua/namespaceMerge.lua +++ b/test/translation/lua/namespaceMerge.lua @@ -2,9 +2,8 @@ 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 end @@ -19,16 +18,16 @@ function MergedClass.methodA(self) end function MergedClass.methodB(self) self:methodA(); - self.propertyFunc(); + self:propertyFunc(); end MergedClass = MergedClass or {} do - local function namespaceFunc() + local function namespaceFunc(self) end MergedClass.namespaceFunc = namespaceFunc end local mergedClass = MergedClass.new(true); mergedClass:methodB(); -mergedClass.propertyFunc(); +mergedClass:propertyFunc(); MergedClass:staticMethodB(); -MergedClass.namespaceFunc(); +MergedClass:namespaceFunc(); diff --git a/test/translation/lua/namespaceNested.lua b/test/translation/lua/namespaceNested.lua index bff87b32e..a8ec92f30 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() + local function nsMember(self) end end end diff --git a/test/translation/lua/namespacePhantom.lua b/test/translation/lua/namespacePhantom.lua index 6cd3c8ac2..2f3381780 100644 --- a/test/translation/lua/namespacePhantom.lua +++ b/test/translation/lua/namespacePhantom.lua @@ -1,2 +1,2 @@ -function nsMember() -end \ No newline at end of file +function nsMember(self) +end diff --git a/test/translation/lua/returnDefault.lua b/test/translation/lua/returnDefault.lua index 52c2123e3..436b8ed62 100644 --- a/test/translation/lua/returnDefault.lua +++ b/test/translation/lua/returnDefault.lua @@ -1,3 +1,3 @@ -function myFunc() +function myFunc(self) return end diff --git a/test/translation/lua/shorthandPropertyAssignment.lua b/test/translation/lua/shorthandPropertyAssignment.lua index 5246c7435..3514556fe 100644 --- a/test/translation/lua/shorthandPropertyAssignment.lua +++ b/test/translation/lua/shorthandPropertyAssignment.lua @@ -1 +1,3 @@ -local f; f = function(x) return ({x = x}) end; +local f; f = function(_,x) + return ({x = x}) +end; diff --git a/test/translation/lua/tupleReturn.lua b/test/translation/lua/tupleReturn.lua index 7d33868fc..30816b99a 100644 --- a/test/translation/lua/tupleReturn.lua +++ b/test/translation/lua/tupleReturn.lua @@ -1,28 +1,28 @@ -function tupleReturn() +function tupleReturn(self) return 0,"foobar" end -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() +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) local r = {1,"baz"}; return table.unpack(r) end -function tupleReturnForward() - return tupleReturn() +function tupleReturnForward(self) + return tupleReturn(_G) end -function tupleNoForward() - return ({ tupleReturn() }) +function tupleNoForward(self) + return ({ tupleReturn(_G) }) end -function tupleReturnUnpack() - return table.unpack(tupleNoForward()) +function tupleReturnUnpack(self) + return table.unpack(tupleNoForward(_G)) end diff --git a/test/translation/ts/assignments.ts b/test/translation/ts/assignments.ts index a8e51e59a..e124a3019 100644 --- a/test/translation/ts/assignments.ts +++ b/test/translation/ts/assignments.ts @@ -2,14 +2,19 @@ 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; diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index 650e13024..7c7241d8e 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());`); + Expect(lua).toBe(`local a,b=unpack(myFunc(_G));`); } @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());`); + Expect(lua).toBe(`local a,b=table.unpack(myFunc(_G));`); } } diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index e3ccb60c0..5f41581ba 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();"); + Expect(lua).toBe("local a,b=abc(_G);"); } @Test("TupleReturn Single assignment") @@ -92,7 +92,7 @@ export class AssignmentTests { + `a = abc();`; const lua = util.transpileString(code); - Expect(lua).toBe("local a = ({ abc() });\na = ({ abc() });"); + Expect(lua).toBe("local a = ({ abc(_G) });\na = ({ abc(_G) });"); } @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 23cd4577f..e73a7a813 100644 --- a/test/unit/curry.spec.ts +++ b/test/unit/curry.spec.ts @@ -10,7 +10,11 @@ export class LuaCurryTests { `(x: number) => (y: number) => x + y;` ); // Assert - Expect(lua).toBe(`function(x) return function(y) return x+y end end;`); + Expect(lua).toBe(`function(_,x) + return function(_,y) + return x+y + end +end;`); } @Test("curryingAdd") diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 0c06b5e00..4e5748824 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -233,18 +233,28 @@ export class FunctionTests { @Test("Function bind") public functionBind(): void { - const source = `const abc = function (a: string) { console.log(this.a + a); } - abc.bind({ a: 4 })("b");`; + const source = `const abc = function (a: string, b: string) { return this.a + a + b; } + return abc.bind({ a: 4 }, "b")("c");`; const result = util.transpileAndExecute(source); - Expect(result).toBe("4b"); + Expect(result).toBe("4bc"); } @Test("Function apply") public functionApply(): void { - const source = `const abc = function (a: string) { console.log(this.a + a); } - abc.apply({ a: 4 }, ["b"]);`; + const source = `const abc = function (a: string) { return this.a + a; } + return abc.apply({ a: 4 }, ["b"]);`; + + const result = util.transpileAndExecute(source); + + Expect(result).toBe("4b"); + } + + @Test("Function call") + public functionCall(): void { + const source = `const abc = function (a: string) { return this.a + a; } + return abc.call({ a: 4 }, "b");`; const result = util.transpileAndExecute(source); diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 007127d33..b515d6749 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -18,6 +18,7 @@ export class LuaLoopTests { arrTest[i] = arrTest[i] + 1; i++; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -52,6 +53,7 @@ export class LuaLoopTests { i++; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -86,6 +88,7 @@ export class LuaLoopTests { i++; } while (i < arrTest.length) + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -105,6 +108,7 @@ export class LuaLoopTests { for (let i = 0; i < arrTest.length; ++i) { arrTest[i] = arrTest[i] + 1; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -133,6 +137,7 @@ export class LuaLoopTests { arrTest[i] = j; } } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest); ` ); @@ -153,6 +158,7 @@ export class LuaLoopTests { for (let i = 0; arrTest.length > i; i++) { arrTest[i] = arrTest[i] + 1; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -173,6 +179,7 @@ export class LuaLoopTests { break; arrTest[i] = arrTest[i] + 1; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -199,6 +206,7 @@ export class LuaLoopTests { for (${header}) { arrTest[i] = arrTest[i] + 1; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -218,6 +226,7 @@ export class LuaLoopTests { for (let key in objTest) { objTest[key] = objTest[key] + 1; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(objTest);` ); @@ -255,6 +264,7 @@ export class LuaLoopTests { obj[i] = 0; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(obj); ` ); @@ -276,6 +286,7 @@ export class LuaLoopTests { for (let value of objTest) { arrResultTest.push(value + 1) } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrResultTest);` ); @@ -307,6 +318,7 @@ export class LuaLoopTests { } a++; } + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(testArr);` ); diff --git a/test/unit/lualib/lualib.spec.ts b/test/unit/lualib/lualib.spec.ts index b81f83c90..5a8b1bb39 100644 --- a/test/unit/lualib/lualib.spec.ts +++ b/test/unit/lualib/lualib.spec.ts @@ -12,6 +12,7 @@ export class LuaLibArrayTests { arrTest.forEach((elem, index) => { arrTest[index] = arrTest[index] + 1; }) + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(arrTest);` ); @@ -31,7 +32,8 @@ export class LuaLibArrayTests { @Test("array.map") public map(inp: T[], func: string) { // Transpile - const lua = util.transpileString(`return JSONStringify([${inp.toString()}].map(${func}))`); + const lua = util.transpileString(`/** !NoContext */ declare function JSONStringify(t: any): string; + return JSONStringify([${inp.toString()}].map(${func}))`); // Execute const result = util.executeLua(lua); @@ -50,7 +52,8 @@ export class LuaLibArrayTests { @Test("array.filter") public filter(inp: T[], func: string) { // Transpile - const lua = util.transpileString(`return JSONStringify([${inp.toString()}].filter(${func}))`); + const lua = util.transpileString(`/** !NoContext */ declare function JSONStringify(t: any): string; + return JSONStringify([${inp.toString()}].filter(${func}))`); // Execute const result = util.executeLua(lua); @@ -66,7 +69,8 @@ export class LuaLibArrayTests { @Test("array.every") public every(inp: T[], func: string) { // Transpile - const lua = util.transpileString(`return JSONStringify([${inp.toString()}].every(${func})))`); + const lua = util.transpileString(`/** !NoContext */ declare function JSONStringify(t: any): string; + return JSONStringify([${inp.toString()}].every(${func})))`); // Execute const result = util.executeLua(lua); @@ -82,7 +86,8 @@ export class LuaLibArrayTests { @Test("array.some") public some(inp: T[], func: string) { // Transpile - const lua = util.transpileString(`return JSONStringify([${inp.toString()}].some(${func})))`); + const lua = util.transpileString(`/** !NoContext */ declare function JSONStringify(t: any): string; + return JSONStringify([${inp.toString()}].some(${func})))`); // Execute const result = util.executeLua(lua); @@ -101,7 +106,8 @@ export class LuaLibArrayTests { @Test("array.slice") public slice(inp: T[], start: number, end?: number) { // Transpile - const lua = util.transpileString(`return JSONStringify([${inp.toString()}].slice(${start}, ${end}))`); + const lua = util.transpileString(`/** !NoContext */ declare function JSONStringify(t: any): string; + return JSONStringify([${inp.toString()}].slice(${start}, ${end}))`); // Execute const result = util.executeLua(lua); @@ -123,6 +129,7 @@ export class LuaLibArrayTests { const lua = util.transpileString( `let spliceTestTable = [${inp.toString()}]; spliceTestTable.splice(${start}, ${deleteCount}, ${newElements}); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(spliceTestTable);` ); @@ -149,12 +156,14 @@ export class LuaLibArrayTests { lua = util.transpileString( `let spliceTestTable = [${inp.toString()}]; spliceTestTable.splice(${start}, ${deleteCount}, ${newElements}); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(spliceTestTable);` ); } else { lua = util.transpileString( `let spliceTestTable = [${inp.toString()}]; spliceTestTable.splice(${start}); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(spliceTestTable);` ); } @@ -188,6 +197,7 @@ export class LuaLibArrayTests { // Transpile const lua = util.transpileString( `let concatTestTable = ${JSON.stringify(arr)}; + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(concatTestTable.concat(${argStr}));` ); @@ -278,6 +288,7 @@ export class LuaLibArrayTests { const lua = util.transpileString( `let testArray = [0]; testArray.push(${inp.join(", ")}); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(testArray); ` ); @@ -332,6 +343,7 @@ export class LuaLibArrayTests { const lua = util.transpileString( `let testArray = ${array}; let val = testArray.reverse(); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(testArray)`); // Execute @@ -352,6 +364,7 @@ export class LuaLibArrayTests { const lua = util.transpileString( `let testArray = ${array}; let val = testArray.shift(); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(testArray)`); // Execute @@ -385,6 +398,7 @@ export class LuaLibArrayTests { const lua = util.transpileString( `let testArray = ${array}; testArray.unshift(${toUnshift}); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(testArray)`); // Execute const result = util.executeLua(lua); @@ -404,6 +418,7 @@ export class LuaLibArrayTests { const lua = util.transpileString( `let testArray = ${array}; testArray.sort(); + /** !NoContext */ declare function JSONStringify(t: any): string; return JSONStringify(testArray)`); // Execute diff --git a/test/unit/objectLiteral.spec.ts b/test/unit/objectLiteral.spec.ts index 68fa4049d..55d488741 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()] = 3,b = "4"};`) + @TestCase(`{[myFunc()]:3,b:"4"}`, `{[myFunc(_G)] = 3,b = "4"};`) @TestCase(`{x}`, `{x = x};`) @Test("Object Literal") public objectLiteral(inp: string, out: string) { diff --git a/test/unit/spreadElement.spec.ts b/test/unit/spreadElement.spec.ts index 50bf8f2dc..89250bc6c 100644 --- a/test/unit/spreadElement.spec.ts +++ b/test/unit/spreadElement.spec.ts @@ -10,7 +10,8 @@ export class SpreadElementTest { @TestCase([1, "test", 3]) @Test("Spread Element Push") public spreadElementPush(inp: any[]) { - const lua = util.transpileString(`return JSONStringify([].push(...${JSON.stringify(inp)}));`); + const lua = util.transpileString(`/** !NoContext */ declare function JSONStringify(t: any): string; + return JSONStringify([].push(...${JSON.stringify(inp)}));`); const result = util.executeLua(lua); Expect(result).toBe([].push(...inp)); } diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index 367ff0442..f2f67b004 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -274,7 +274,8 @@ export class StringTests { public split(inp: string, separator: string): void { // Transpile const lua = util.transpileString( - `return JSONStringify("${inp}".split("${separator}"))` + `/** !NoContext */ declare function JSONStringify(t: any): string; + return JSONStringify("${inp}".split("${separator}"))` ); // Execute