From 72acfe251e2de453fef332c9c0eca54f37c7edd3 Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Sat, 16 Mar 2019 17:11:22 -0600 Subject: [PATCH 1/3] validating arguments passed to lua lib functions --- src/LuaTransformer.ts | 69 ++++++++++++++++++++--------------- test/unit/assignments.spec.ts | 23 ++++++++++++ test/unit/expressions.spec.ts | 2 +- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index b367495e0..5a8612b69 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -3155,53 +3155,53 @@ export class LuaTransformer { // If the function being called is of type owner.func, get the type of owner const ownerType = this.checker.getTypeAtLocation(node.expression.expression); + const signature = this.checker.getResolvedSignature(node); + if (tsHelper.isStandardLibraryType(ownerType, "Math", this.program)) { - return this.transformMathCallExpression(node); + return this.transformMathCallExpression(node, signature); } if (tsHelper.isStandardLibraryType(ownerType, "Console", this.program)) { - return this.transformConsoleCallExpression(node); + return this.transformConsoleCallExpression(node, signature); } if (tsHelper.isStandardLibraryType(ownerType, "StringConstructor", this.program)) { return tstl.createCallExpression( this.transformStringExpression(node.expression.name), - this.transformArguments(node.arguments), + this.transformArguments(node.arguments, signature), node ); } if (tsHelper.isStandardLibraryType(ownerType, "ObjectConstructor", this.program)) { - return this.transformObjectCallExpression(node); + return this.transformObjectCallExpression(node, signature); } if (tsHelper.isStandardLibraryType(ownerType, "SymbolConstructor", this.program)) { - return this.transformSymbolCallExpression(node); + return this.transformSymbolCallExpression(node, signature); } switch (ownerType.flags) { case ts.TypeFlags.String: case ts.TypeFlags.StringLiteral: - return this.transformStringCallExpression(node); + return this.transformStringCallExpression(node, signature); } // if ownerType is a array, use only supported functions if (tsHelper.isExplicitArrayType(ownerType, this.checker)) { - return this.transformArrayCallExpression(node); + return this.transformArrayCallExpression(node, signature); } // if ownerType inherits from an array, use array calls where appropriate if (tsHelper.isArrayType(ownerType, this.checker) && tsHelper.isDefaultArrayCallMethodName(node.expression.name.escapedText as string)) { - return this.transformArrayCallExpression(node); + return this.transformArrayCallExpression(node, signature); } if (tsHelper.isFunctionType(ownerType, this.checker)) { - return this.transformFunctionCallExpression(node); + return this.transformFunctionCallExpression(node, signature); } - 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,...) @@ -3285,7 +3285,8 @@ export class LuaTransformer { public transformArguments( params: ts.NodeArray, - sig?: ts.Signature, context?: T + sig?: ts.Signature, + context?: T ): tstl.Expression[] { const parameters: tstl.Expression[] = []; @@ -3378,9 +3379,9 @@ export class LuaTransformer { } // Transpile a Math._ property - public transformMathCallExpression(node: ts.CallExpression): tstl.Expression { + public transformMathCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.Expression { const expression = node.expression as ts.PropertyAccessExpression; - const params = this.transformArguments(node.arguments); + const params = this.transformArguments(node.arguments, signature); const expressionName = expression.name.escapedText as string; switch (expressionName) { // math.tan(x / y) @@ -3537,9 +3538,9 @@ export class LuaTransformer { throw TSTLErrors.CouldNotFindEnumMember(enumDeclaration, memberName, tsOriginal); } - public transformStringCallExpression(node: ts.CallExpression): tstl.Expression { + public transformStringCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.Expression { const expression = node.expression as ts.PropertyAccessExpression; - const params = this.transformArguments(node.arguments); + const params = this.transformArguments(node.arguments, signature); const caller = this.transformExpression(expression.expression); const expressionName = expression.name.escapedText as string; @@ -3681,9 +3682,13 @@ export class LuaTransformer { } // Transpile an Object._ property - public transformObjectCallExpression(expression: ts.CallExpression): ExpressionVisitResult { + public transformObjectCallExpression( + expression: ts.CallExpression, + signature: ts.Signature + ): ExpressionVisitResult + { const method = expression.expression as ts.PropertyAccessExpression; - const parameters = this.transformArguments(expression.arguments); + const parameters = this.transformArguments(expression.arguments, signature); const caller = this.transformExpression(expression.expression); const methodName = method.name.escapedText; @@ -3705,7 +3710,11 @@ export class LuaTransformer { } } - public transformConsoleCallExpression(expression: ts.CallExpression): ExpressionVisitResult { + public transformConsoleCallExpression( + expression: ts.CallExpression, + signature: ts.Signature + ): ExpressionVisitResult + { const method = expression.expression as ts.PropertyAccessExpression; const methodName = method.name.escapedText; @@ -3718,7 +3727,7 @@ export class LuaTransformer { tstl.createTableIndexExpression( tstl.createIdentifier("string"), tstl.createStringLiteral("format")), - this.transformArguments(expression.arguments) + this.transformArguments(expression.arguments, signature) ); return tstl.createCallExpression( tstl.createIdentifier("print"), @@ -3728,10 +3737,10 @@ export class LuaTransformer { // print([arguments]) return tstl.createCallExpression( tstl.createIdentifier("print"), - this.transformArguments(expression.arguments) + this.transformArguments(expression.arguments, signature) ); case "assert": - const args = this.transformArguments(expression.arguments); + const args = this.transformArguments(expression.arguments, signature); if (expression.arguments.length > 1 && this.isStringFormatTemplate(expression.arguments[1])) { // assert([condition], string.format([arguments])) @@ -3759,7 +3768,7 @@ export class LuaTransformer { tstl.createTableIndexExpression( tstl.createIdentifier("string"), tstl.createStringLiteral("format")), - this.transformArguments(expression.arguments) + this.transformArguments(expression.arguments, signature) ); const debugTracebackCall = tstl.createCallExpression( tstl.createTableIndexExpression( @@ -3777,7 +3786,7 @@ export class LuaTransformer { tstl.createTableIndexExpression( tstl.createIdentifier("debug"), tstl.createStringLiteral("traceback")), - this.transformArguments(expression.arguments) + this.transformArguments(expression.arguments, signature) ); return tstl.createCallExpression( tstl.createIdentifier("print"), @@ -3797,9 +3806,9 @@ export class LuaTransformer { } // Transpile a Symbol._ property - public transformSymbolCallExpression(expression: ts.CallExpression): tstl.CallExpression { + public transformSymbolCallExpression(expression: ts.CallExpression, signature: ts.Signature): tstl.CallExpression { const method = expression.expression as ts.PropertyAccessExpression; - const parameters = this.transformArguments(expression.arguments); + const parameters = this.transformArguments(expression.arguments, signature); const methodName = method.name.escapedText; switch (methodName) { @@ -3818,9 +3827,9 @@ export class LuaTransformer { } } - public transformArrayCallExpression(node: ts.CallExpression): tstl.CallExpression { + public transformArrayCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.CallExpression { const expression = node.expression as ts.PropertyAccessExpression; - const params = this.transformArguments(node.arguments); + const params = this.transformArguments(node.arguments, signature); const caller = this.transformExpression(expression.expression); const expressionName = expression.name.escapedText; switch (expressionName) { @@ -3875,13 +3884,13 @@ export class LuaTransformer { } } - public transformFunctionCallExpression(node: ts.CallExpression): tstl.CallExpression { + public transformFunctionCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.CallExpression { const expression = node.expression as ts.PropertyAccessExpression; const callerType = this.checker.getTypeAtLocation(expression.expression); if (tsHelper.getFunctionContextType(callerType, this.checker) === ContextType.Void) { throw TSTLErrors.UnsupportedSelfFunctionConversion(node); } - const params = this.transformArguments(node.arguments); + const params = this.transformArguments(node.arguments, signature); const caller = this.transformExpression(expression.expression); const expressionName = expression.name.escapedText; switch (expressionName) { diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index 257dc2b2d..5b27f33d1 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -663,6 +663,29 @@ export class AssignmentTests { Expect(() => util.transpileString(code, undefined, false)).toThrowError(TranspileError, err.message); } + @Test("Valid lua lib function argument") + public validLuaLibFunctionArgument(): void { + const code = + `let result = ""; + function foo(this: any, value: string) { result += value; } + const a = ['foo', 'bar']; + a.forEach(foo); + return result;`; + Expect(util.transpileAndExecute(code)).toBe("foobar"); + } + + @Test("Invalid lua lib function argument") + public invalidLuaLibFunctionArgument(testFunction: TestFunction, functionType: string, isSelfConversion: boolean) + : void + { + const code = + `declare function foo(this: void, value: string): void; + declare const a: string[]; + a.forEach(foo);`; + const err = TSTLErrors.UnsupportedSelfFunctionConversion(undefined, "callbackfn"); + Expect(() => util.transpileString(code, undefined, false)).toThrowError(TranspileError, err.message); + } + @TestCases(validTestFunctionCasts) @Test("Valid function argument with cast") public validFunctionArgumentWithCast(testFunction: TestFunction, castedFunction: string): void { diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index e0d2df5b0..a1cfea7fc 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -498,7 +498,7 @@ export class ExpressionTests { expression: {name: ts.createIdentifier("unknownFunction"), expression: ts.createLiteral(false)}, }; - Expect(() => transformer.transformArrayCallExpression(mockNode as ts.CallExpression)) + Expect(() => transformer.transformArrayCallExpression(mockNode as ts.CallExpression, undefined)) .toThrowError(TranspileError, "Unsupported property on array: unknownFunction"); } From dccb284e24769fbb78e65322ea625f0629836bde Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Sun, 17 Mar 2019 06:42:48 -0600 Subject: [PATCH 2/3] resolving signature in lualib transform functions instead of passing it in --- src/LuaTransformer.ts | 45 ++++++++++++++++++----------------- test/unit/expressions.spec.ts | 3 ++- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 5a8612b69..0fb744030 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -3158,11 +3158,11 @@ export class LuaTransformer { const signature = this.checker.getResolvedSignature(node); if (tsHelper.isStandardLibraryType(ownerType, "Math", this.program)) { - return this.transformMathCallExpression(node, signature); + return this.transformMathCallExpression(node); } if (tsHelper.isStandardLibraryType(ownerType, "Console", this.program)) { - return this.transformConsoleCallExpression(node, signature); + return this.transformConsoleCallExpression(node); } if (tsHelper.isStandardLibraryType(ownerType, "StringConstructor", this.program)) { @@ -3174,32 +3174,32 @@ export class LuaTransformer { } if (tsHelper.isStandardLibraryType(ownerType, "ObjectConstructor", this.program)) { - return this.transformObjectCallExpression(node, signature); + return this.transformObjectCallExpression(node); } if (tsHelper.isStandardLibraryType(ownerType, "SymbolConstructor", this.program)) { - return this.transformSymbolCallExpression(node, signature); + return this.transformSymbolCallExpression(node); } switch (ownerType.flags) { case ts.TypeFlags.String: case ts.TypeFlags.StringLiteral: - return this.transformStringCallExpression(node, signature); + return this.transformStringCallExpression(node); } // if ownerType is a array, use only supported functions if (tsHelper.isExplicitArrayType(ownerType, this.checker)) { - return this.transformArrayCallExpression(node, signature); + return this.transformArrayCallExpression(node); } // if ownerType inherits from an array, use array calls where appropriate if (tsHelper.isArrayType(ownerType, this.checker) && tsHelper.isDefaultArrayCallMethodName(node.expression.name.escapedText as string)) { - return this.transformArrayCallExpression(node, signature); + return this.transformArrayCallExpression(node); } if (tsHelper.isFunctionType(ownerType, this.checker)) { - return this.transformFunctionCallExpression(node, signature); + return this.transformFunctionCallExpression(node); } // Get the type of the function @@ -3379,8 +3379,9 @@ export class LuaTransformer { } // Transpile a Math._ property - public transformMathCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.Expression { + public transformMathCallExpression(node: ts.CallExpression): tstl.Expression { const expression = node.expression as ts.PropertyAccessExpression; + const signature = this.checker.getResolvedSignature(node); const params = this.transformArguments(node.arguments, signature); const expressionName = expression.name.escapedText as string; switch (expressionName) { @@ -3538,8 +3539,9 @@ export class LuaTransformer { throw TSTLErrors.CouldNotFindEnumMember(enumDeclaration, memberName, tsOriginal); } - public transformStringCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.Expression { + public transformStringCallExpression(node: ts.CallExpression): tstl.Expression { const expression = node.expression as ts.PropertyAccessExpression; + const signature = this.checker.getResolvedSignature(node); const params = this.transformArguments(node.arguments, signature); const caller = this.transformExpression(expression.expression); @@ -3682,13 +3684,11 @@ export class LuaTransformer { } // Transpile an Object._ property - public transformObjectCallExpression( - expression: ts.CallExpression, - signature: ts.Signature - ): ExpressionVisitResult + public transformObjectCallExpression(expression: ts.CallExpression,): ExpressionVisitResult { const method = expression.expression as ts.PropertyAccessExpression; - const parameters = this.transformArguments(expression.arguments, signature); + const signature = this.checker.getResolvedSignature(expression); + const parameters = this.transformArguments(expression.arguments); const caller = this.transformExpression(expression.expression); const methodName = method.name.escapedText; @@ -3710,13 +3710,11 @@ export class LuaTransformer { } } - public transformConsoleCallExpression( - expression: ts.CallExpression, - signature: ts.Signature - ): ExpressionVisitResult + public transformConsoleCallExpression(expression: ts.CallExpression): ExpressionVisitResult { const method = expression.expression as ts.PropertyAccessExpression; const methodName = method.name.escapedText; + const signature = this.checker.getResolvedSignature(expression); switch (methodName) { case "log": @@ -3806,8 +3804,9 @@ export class LuaTransformer { } // Transpile a Symbol._ property - public transformSymbolCallExpression(expression: ts.CallExpression, signature: ts.Signature): tstl.CallExpression { + public transformSymbolCallExpression(expression: ts.CallExpression): tstl.CallExpression { const method = expression.expression as ts.PropertyAccessExpression; + const signature = this.checker.getResolvedSignature(expression); const parameters = this.transformArguments(expression.arguments, signature); const methodName = method.name.escapedText; @@ -3827,8 +3826,9 @@ export class LuaTransformer { } } - public transformArrayCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.CallExpression { + public transformArrayCallExpression(node: ts.CallExpression): tstl.CallExpression { const expression = node.expression as ts.PropertyAccessExpression; + const signature = this.checker.getResolvedSignature(node); const params = this.transformArguments(node.arguments, signature); const caller = this.transformExpression(expression.expression); const expressionName = expression.name.escapedText; @@ -3884,12 +3884,13 @@ export class LuaTransformer { } } - public transformFunctionCallExpression(node: ts.CallExpression, signature: ts.Signature): tstl.CallExpression { + public transformFunctionCallExpression(node: ts.CallExpression): tstl.CallExpression { const expression = node.expression as ts.PropertyAccessExpression; const callerType = this.checker.getTypeAtLocation(expression.expression); if (tsHelper.getFunctionContextType(callerType, this.checker) === ContextType.Void) { throw TSTLErrors.UnsupportedSelfFunctionConversion(node); } + const signature = this.checker.getResolvedSignature(node); const params = this.transformArguments(node.arguments, signature); const caller = this.transformExpression(expression.expression); const expressionName = expression.name.escapedText; diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index a1cfea7fc..aa14e0404 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -493,12 +493,13 @@ export class ExpressionTests { const transformer = util.makeTestTransformer(); const mockNode: any = { + kind: ts.SyntaxKind.CallExpression, arguments: [], caller: ts.createLiteral(false), expression: {name: ts.createIdentifier("unknownFunction"), expression: ts.createLiteral(false)}, }; - Expect(() => transformer.transformArrayCallExpression(mockNode as ts.CallExpression, undefined)) + Expect(() => transformer.transformArrayCallExpression(mockNode as ts.CallExpression)) .toThrowError(TranspileError, "Unsupported property on array: unknownFunction"); } From 2347fe398e4213f07e21a3389db5bd66cf4c64e8 Mon Sep 17 00:00:00 2001 From: Tom <26638278+tomblind@users.noreply.github.com> Date: Sun, 17 Mar 2019 06:46:33 -0600 Subject: [PATCH 3/3] formatting fixes --- src/LuaTransformer.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 0206a56c3..66bb194d5 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -3691,8 +3691,7 @@ export class LuaTransformer { } // Transpile an Object._ property - public transformObjectCallExpression(expression: ts.CallExpression,): ExpressionVisitResult - { + public transformObjectCallExpression(expression: ts.CallExpression): ExpressionVisitResult { const method = expression.expression as ts.PropertyAccessExpression; const signature = this.checker.getResolvedSignature(expression); const parameters = this.transformArguments(expression.arguments); @@ -3717,8 +3716,7 @@ export class LuaTransformer { } } - public transformConsoleCallExpression(expression: ts.CallExpression): ExpressionVisitResult - { + public transformConsoleCallExpression(expression: ts.CallExpression): ExpressionVisitResult { const method = expression.expression as ts.PropertyAccessExpression; const methodName = method.name.escapedText; const signature = this.checker.getResolvedSignature(expression);