From a249fffb131ba7930dde3f2865468adc58dc1457 Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Wed, 17 Jul 2019 07:00:12 -0600 Subject: [PATCH 1/3] Fixed referencing named function expressions fixes #669 --- src/LuaTransformer.ts | 22 +++++++++++++++++++--- test/unit/functions.spec.ts | 9 +++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 027892887..1e11539ab 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -3664,9 +3664,9 @@ export class LuaTransformer { flags |= tstl.FunctionExpressionFlags.Inline; } - const [transformedBody] = this.transformFunctionBody(node.parameters, body, spreadIdentifier); + const [transformedBody, scope] = this.transformFunctionBody(node.parameters, body, spreadIdentifier); - return tstl.createFunctionExpression( + const functionExpression = tstl.createFunctionExpression( tstl.createBlock(transformedBody), paramNames, dotsLiteral, @@ -3674,6 +3674,22 @@ export class LuaTransformer { flags, node ); + + if (ts.isFunctionExpression(node) && node.name && scope.referencedSymbols) { + const symbol = this.checker.getSymbolAtLocation(node.name); + if (symbol) { + const symbolId = this.symbolIds.get(symbol); + if (symbolId && scope.referencedSymbols.has(symbolId)) { + const nameIdentifier = this.transformIdentifier(node.name); + return this.createImmediatelyInvokedFunctionExpression( + [tstl.createVariableDeclarationStatement(nameIdentifier, functionExpression)], + tstl.cloneIdentifier(nameIdentifier) + ); + } + } + } + + return functionExpression; } public transformNewExpression(node: ts.NewExpression): ExpressionVisitResult { @@ -5111,7 +5127,7 @@ export class LuaTransformer { protected createImmediatelyInvokedFunctionExpression( statements: tstl.Statement[], result: tstl.Expression | tstl.Expression[], - tsOriginal: ts.Node + tsOriginal?: ts.Node ): tstl.CallExpression { const body = statements ? statements.slice(0) : []; body.push(tstl.createReturnStatement(Array.isArray(result) ? result : [result])); diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 455b6d70d..41d55e5af 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -610,3 +610,12 @@ test.each([{}, { noHoisting: true }])("@vararg global", compilerOptions => { expect(util.executeLua(lua)).toBe("ABCD"); }); + +test("named function expression reference", () => { + const code = ` + const y = function x(inp: string) { + return inp + typeof x; + }; + return y("foo-");`; + expect(util.transpileAndExecute(code)).toBe("foo-function"); +}); From 6cc5f80e90ebf7a3408649b72bc8a7b3b83e807c Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Wed, 17 Jul 2019 09:50:08 -0600 Subject: [PATCH 2/3] checking symbolId against undefined --- src/LuaTransformer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 1e11539ab..250caee3a 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -3679,7 +3679,7 @@ export class LuaTransformer { const symbol = this.checker.getSymbolAtLocation(node.name); if (symbol) { const symbolId = this.symbolIds.get(symbol); - if (symbolId && scope.referencedSymbols.has(symbolId)) { + if (symbolId !== undefined && scope.referencedSymbols.has(symbolId)) { const nameIdentifier = this.transformIdentifier(node.name); return this.createImmediatelyInvokedFunctionExpression( [tstl.createVariableDeclarationStatement(nameIdentifier, functionExpression)], From 80be7aafb94a9a31b24c6a55884d6fdc278805ce Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Thu, 18 Jul 2019 06:05:54 -0600 Subject: [PATCH 3/3] added some comments --- src/LuaTransformer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 250caee3a..4751f8dbc 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -3675,10 +3675,12 @@ export class LuaTransformer { node ); + //Handle named function expressions which reference themselves if (ts.isFunctionExpression(node) && node.name && scope.referencedSymbols) { const symbol = this.checker.getSymbolAtLocation(node.name); if (symbol) { const symbolId = this.symbolIds.get(symbol); + //Only wrap if the name is actually referenced inside the function if (symbolId !== undefined && scope.referencedSymbols.has(symbolId)) { const nameIdentifier = this.transformIdentifier(node.name); return this.createImmediatelyInvokedFunctionExpression(