From 485d91e59d1e5de1b6edec49f1b01a77e6301de7 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Thu, 15 Aug 2019 16:58:36 +1000 Subject: [PATCH 01/13] Fix LuaTable --- src/LuaTransformer.ts | 110 +++++++++++++++----------- test/unit/decorators/luaTable.spec.ts | 25 +++++- 2 files changed, 86 insertions(+), 49 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index d8aee1a01..b69a1b48d 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -2165,17 +2165,16 @@ export class LuaTransformer { ); } - if (ts.isCallExpression(expression) && ts.isPropertyAccessExpression(expression.expression)) { - const ownerType = this.checker.getTypeAtLocation(expression.expression.expression); + if ( + ts.isExpressionStatement(statement) && + ts.isCallExpression(statement.expression) && + ts.isPropertyAccessExpression(statement.expression.expression) + ) { + const ownerType = this.checker.getTypeAtLocation(statement.expression.expression.expression); const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker); if (classDecorators.has(DecoratorKind.LuaTable)) { - this.validateLuaTableCall( - expression as ts.CallExpression & { expression: ts.PropertyAccessExpression }, - true - ); - return this.transformLuaTableExpressionStatement(statement as ts.ExpressionStatement & { - expression: ts.CallExpression; - } & { expression: { expression: ts.PropertyAccessExpression } }); + this.validateLuaTableCall(statement.expression); + return this.transformLuaTableExpressionAsExpressionStatement(statement.expression); } } @@ -4254,10 +4253,8 @@ export class LuaTransformer { const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker); if (classDecorators.has(DecoratorKind.LuaTable)) { - this.validateLuaTableCall(node as ts.CallExpression & { expression: ts.PropertyAccessExpression }, false); - return this.transformLuaTableCallExpression(node as ts.CallExpression & { - expression: ts.PropertyAccessExpression; - }); + this.validateLuaTableCall(node); + return this.transformLuaTableCallExpression(node); } if (tsHelper.isStringType(ownerType, this.checker, this.program)) { @@ -4341,6 +4338,14 @@ export class LuaTransformer { throw TSTLErrors.InvalidElementCall(node); } + const ownerType = this.checker.getTypeAtLocation(node.expression.expression); + const ownerDecorators = tsHelper.getCustomDecorators(ownerType, this.checker); + + if (ownerDecorators.has(DecoratorKind.LuaTable)) { + this.validateLuaTableCall(node); + return this.transformLuaTableCallExpression(node); + } + const signature = this.checker.getResolvedSignature(node); const signatureDeclaration = signature && signature.getDeclaration(); const parameters = this.transformArguments(node.arguments, signature); @@ -4619,8 +4624,12 @@ export class LuaTransformer { } } - protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.UnaryExpression { + protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.Expression { switch (node.name.text) { + case "set": + return this.transformExpression(node.expression); + case "get": + return this.transformExpression(node.expression); case "length": const propertyAccessExpression = this.transformExpression(node.expression); return tstl.createUnaryExpression(propertyAccessExpression, tstl.SyntaxKind.LengthOperator, node); @@ -4656,6 +4665,14 @@ export class LuaTransformer { const argumentType = this.checker.getTypeAtLocation(expression.argumentExpression); const type = this.checker.getTypeAtLocation(expression.expression); + const decorators = new Map(); + if (type && type.symbol) { + tsHelper.collectCustomDecorators(type.symbol, this.checker, decorators); + if (decorators.has(DecoratorKind.LuaTable)) { + throw TSTLErrors.UnsupportedKind("LuaTable access expression", expression.kind, expression); + } + } + if ( tsHelper.isNumberType(argumentType, this.checker, this.program) && tsHelper.isStringType(type, this.checker, this.program) @@ -4973,11 +4990,8 @@ export class LuaTransformer { } } - protected validateLuaTableCall( - expression: ts.CallExpression & { expression: ts.PropertyAccessExpression }, - isWithinExpressionStatement: boolean - ): void { - const methodName = expression.expression.name.text; + protected validateLuaTableCall(expression: ts.CallExpression): void { + const methodName = this.getLuaTablePropertyName(expression.expression); if (expression.arguments.some(argument => ts.isSpreadElement(argument))) { throw TSTLErrors.ForbiddenLuaTableUseException("Arguments cannot be spread.", expression); } @@ -4995,57 +5009,48 @@ export class LuaTransformer { expression ); } - if (!isWithinExpressionStatement) { + if (expression.parent.kind !== ts.SyntaxKind.ExpressionStatement) { throw TSTLErrors.ForbiddenLuaTableSetExpression(expression); } break; } } - protected transformLuaTableExpressionStatement( - node: ts.ExpressionStatement & { expression: ts.CallExpression } & { - expression: { expression: ts.PropertyAccessExpression }; - } - ): tstl.VariableDeclarationStatement | tstl.AssignmentStatement { - const methodName = node.expression.expression.name.text; - const signature = this.checker.getResolvedSignature(node.expression); - const tableName = (node.expression.expression.expression as ts.Identifier).text; - const luaTable = tstl.createIdentifier(tableName); - const params = this.transformArguments((node.expression as ts.CallExpression).arguments, signature); + protected transformLuaTableExpressionAsExpressionStatement(expression: ts.CallExpression): tstl.Statement { + const methodName = this.getLuaTablePropertyName(expression.expression); + const signature = this.checker.getResolvedSignature(expression); + const tableAccessExpression = this.transformExpression(expression.expression); + const params = this.transformArguments(expression.arguments, signature); switch (methodName) { case "get": return tstl.createVariableDeclarationStatement( - tstl.createAnonymousIdentifier(node.expression), - tstl.createTableIndexExpression(luaTable, params[0], node.expression), - node.expression + tstl.createAnonymousIdentifier(expression), + tstl.createTableIndexExpression(tableAccessExpression, params[0], expression), + expression ); case "set": return tstl.createAssignmentStatement( - tstl.createTableIndexExpression(luaTable, params[0], node.expression), + tstl.createTableIndexExpression(tableAccessExpression, params[0], expression), params.splice(1), - node.expression + expression ); default: - throw TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", node.expression); + throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, expression); } } - protected transformLuaTableCallExpression( - expression: ts.CallExpression & { expression: ts.PropertyAccessExpression } - ): tstl.Expression { - const method = expression.expression; - const methodName = method.name.text; + protected transformLuaTableCallExpression(expression: ts.CallExpression): tstl.Expression { + const methodName = this.getLuaTablePropertyName(expression.expression); const signature = this.checker.getResolvedSignature(expression); - const tableName = (method.expression as ts.Identifier).text; - const luaTable = tstl.createIdentifier(tableName); + const tableAccessExpression = this.transformExpression(expression.expression); const params = this.transformArguments(expression.arguments, signature); switch (methodName) { case "get": - return tstl.createTableIndexExpression(luaTable, params[0], expression); + return tstl.createTableIndexExpression(tableAccessExpression, params[0], expression); default: - throw TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", expression); + throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, expression); } } @@ -5444,6 +5449,21 @@ export class LuaTransformer { return scope; } + protected getLuaTablePropertyName(node: ts.LeftHandSideExpression): string { + let methodName: string; + switch (node.kind) { + case ts.SyntaxKind.PropertyAccessExpression: + methodName = (node as ts.PropertyAccessExpression).name.text; + break; + case ts.SyntaxKind.Identifier: + methodName = (node as ts.Identifier).text; + break; + default: + throw TSTLErrors.UnsupportedKind("LuaTable access expression", node.kind, node); + } + return methodName; + } + protected transformLuaLibFunction( func: LuaLibFeature, tsParent?: ts.Expression, diff --git a/test/unit/decorators/luaTable.spec.ts b/test/unit/decorators/luaTable.spec.ts index 6419cc735..ef229f10b 100644 --- a/test/unit/decorators/luaTable.spec.ts +++ b/test/unit/decorators/luaTable.spec.ts @@ -1,3 +1,4 @@ +import * as ts from "typescript"; import * as TSTLErrors from "../../../src/TSTLErrors"; import * as util from "../../util"; @@ -40,15 +41,15 @@ test.each([tableLibClass, tableLibInterface])("LuaTable set() cannot be used in ); }); -test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other methods", tableLib => { +test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other members", tableLib => { expect(() => util.transpileString(tableLib + `tbl.other()`)).toThrowExactError( - TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", util.nodeStub) + TSTLErrors.UnsupportedProperty("LuaTable", "other", util.nodeStub) ); }); -test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other methods", tableLib => { +test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other members", tableLib => { expect(() => util.transpileString(tableLib + `let x = tbl.other()`)).toThrowExactError( - TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", util.nodeStub) + TSTLErrors.UnsupportedProperty("LuaTable", "other", util.nodeStub) ); }); @@ -113,12 +114,28 @@ test.each([tableLibClass])("Cannot extend LuaTable class", tableLib => { }); }); +test.each([tableLibClass, tableLibInterface])("Cannot use ElementAccessExpression on a LuaTable", tableLib => { + test.each([`tbl["get"]("field")`, `tbl["set"]("field")`, `tbl["length"]`])( + "Cannot use ElementAccessExpression on a LuaTable (%p)", + code => { + expect(() => util.transpileString(tableLib + code)).toThrowExactError( + TSTLErrors.UnsupportedKind( + "LuaTable access expression", + ts.SyntaxKind.ElementAccessExpression, + util.nodeStub + ) + ); + } + ); +}); + test.each([tableLibClass])("LuaTable functional tests", tableLib => { test.each<[string, any]>([ [`const t = new Table(); t.set("field", "value"); return t.get("field");`, "value"], [`const t = new Table(); t.set("field", 0); return t.get("field");`, 0], [`const t = new Table(); t.set(1, true); return t.length`, 1], [`const t = new Table(); t.set(t.length + 1, true); t.set(t.length + 1, true); return t.length`, 2], + [`const k = "k"; const t = { data: new Table() }; t.data.set(k, 3); return t.data.get(k);`, 3], ])("LuaTable test (%p)", (code, expectedReturnValue) => { expect(util.transpileAndExecute(code, undefined, undefined, tableLib)).toBe(expectedReturnValue); }); From e2bd6610c8f137ea325c0fe7cbd9ed780ce7100f Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Fri, 16 Aug 2019 10:32:48 +1000 Subject: [PATCH 02/13] Change getLuaTablePropertyName to if/else statements --- src/LuaTransformer.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index b69a1b48d..efa2e0ebc 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -5450,18 +5450,13 @@ export class LuaTransformer { } protected getLuaTablePropertyName(node: ts.LeftHandSideExpression): string { - let methodName: string; - switch (node.kind) { - case ts.SyntaxKind.PropertyAccessExpression: - methodName = (node as ts.PropertyAccessExpression).name.text; - break; - case ts.SyntaxKind.Identifier: - methodName = (node as ts.Identifier).text; - break; - default: - throw TSTLErrors.UnsupportedKind("LuaTable access expression", node.kind, node); + if (ts.isPropertyAccessExpression(node)) { + return node.name.text; + } else if (ts.isIdentifier(node)) { + return node.text; + } else { + throw TSTLErrors.UnsupportedKind("LuaTable access expression", node.kind, node); } - return methodName; } protected transformLuaLibFunction( From 02051052f2dc68eff6a80ef03757986ff987d038 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Fri, 16 Aug 2019 10:35:15 +1000 Subject: [PATCH 03/13] Use getCustomDecorators for LuaTable --- src/LuaTransformer.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index efa2e0ebc..2ffb38825 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -4665,12 +4665,9 @@ export class LuaTransformer { const argumentType = this.checker.getTypeAtLocation(expression.argumentExpression); const type = this.checker.getTypeAtLocation(expression.expression); - const decorators = new Map(); - if (type && type.symbol) { - tsHelper.collectCustomDecorators(type.symbol, this.checker, decorators); - if (decorators.has(DecoratorKind.LuaTable)) { - throw TSTLErrors.UnsupportedKind("LuaTable access expression", expression.kind, expression); - } + const decorators = tsHelper.getCustomDecorators(type, this.checker); + if (decorators.has(DecoratorKind.LuaTable)) { + throw TSTLErrors.UnsupportedKind("LuaTable access expression", expression.kind, expression); } if ( From 9a214c2ca43c77e975fcaac4a593bb7c3a117b7b Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Fri, 16 Aug 2019 10:36:38 +1000 Subject: [PATCH 04/13] Change LuaTableExpression tableAccessExpression -> leftHandSideExpression --- src/LuaTransformer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 2ffb38825..889503303 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -5016,19 +5016,19 @@ export class LuaTransformer { protected transformLuaTableExpressionAsExpressionStatement(expression: ts.CallExpression): tstl.Statement { const methodName = this.getLuaTablePropertyName(expression.expression); const signature = this.checker.getResolvedSignature(expression); - const tableAccessExpression = this.transformExpression(expression.expression); + const leftHandSideExpression = this.transformExpression(expression.expression); const params = this.transformArguments(expression.arguments, signature); switch (methodName) { case "get": return tstl.createVariableDeclarationStatement( tstl.createAnonymousIdentifier(expression), - tstl.createTableIndexExpression(tableAccessExpression, params[0], expression), + tstl.createTableIndexExpression(leftHandSideExpression, params[0], expression), expression ); case "set": return tstl.createAssignmentStatement( - tstl.createTableIndexExpression(tableAccessExpression, params[0], expression), + tstl.createTableIndexExpression(leftHandSideExpression, params[0], expression), params.splice(1), expression ); From c9c2fb03b3917218cdc7b7273e418123973ad3af Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Fri, 16 Aug 2019 11:56:58 +1000 Subject: [PATCH 05/13] Revert LuaTable ExpressionStatement conditional change --- src/LuaTransformer.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 889503303..40e0428ed 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -2165,16 +2165,12 @@ export class LuaTransformer { ); } - if ( - ts.isExpressionStatement(statement) && - ts.isCallExpression(statement.expression) && - ts.isPropertyAccessExpression(statement.expression.expression) - ) { - const ownerType = this.checker.getTypeAtLocation(statement.expression.expression.expression); + if (ts.isCallExpression(expression) && ts.isPropertyAccessExpression(expression.expression)) { + const ownerType = this.checker.getTypeAtLocation(expression.expression.expression); const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker); if (classDecorators.has(DecoratorKind.LuaTable)) { - this.validateLuaTableCall(statement.expression); - return this.transformLuaTableExpressionAsExpressionStatement(statement.expression); + this.validateLuaTableCall(expression); + return this.transformLuaTableExpressionAsExpressionStatement(expression); } } From 3cc91b517701fccdb81d506f19aa59788274be8e Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 09:34:16 +1000 Subject: [PATCH 06/13] Use luaTable to identify table access expressions --- src/LuaTransformer.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 40e0428ed..3e1914174 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -5012,19 +5012,19 @@ export class LuaTransformer { protected transformLuaTableExpressionAsExpressionStatement(expression: ts.CallExpression): tstl.Statement { const methodName = this.getLuaTablePropertyName(expression.expression); const signature = this.checker.getResolvedSignature(expression); - const leftHandSideExpression = this.transformExpression(expression.expression); + const luaTable = this.transformExpression(expression.expression); const params = this.transformArguments(expression.arguments, signature); switch (methodName) { case "get": return tstl.createVariableDeclarationStatement( tstl.createAnonymousIdentifier(expression), - tstl.createTableIndexExpression(leftHandSideExpression, params[0], expression), + tstl.createTableIndexExpression(luaTable, params[0], expression), expression ); case "set": return tstl.createAssignmentStatement( - tstl.createTableIndexExpression(leftHandSideExpression, params[0], expression), + tstl.createTableIndexExpression(luaTable, params[0], expression), params.splice(1), expression ); @@ -5036,12 +5036,12 @@ export class LuaTransformer { protected transformLuaTableCallExpression(expression: ts.CallExpression): tstl.Expression { const methodName = this.getLuaTablePropertyName(expression.expression); const signature = this.checker.getResolvedSignature(expression); - const tableAccessExpression = this.transformExpression(expression.expression); + const luaTable = this.transformExpression(expression.expression); const params = this.transformArguments(expression.arguments, signature); switch (methodName) { case "get": - return tstl.createTableIndexExpression(tableAccessExpression, params[0], expression); + return tstl.createTableIndexExpression(luaTable, params[0], expression); default: throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, expression); } From db0f0b056e5d36d0e35708ba816e0bd6a4e906c4 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 09:35:00 +1000 Subject: [PATCH 07/13] Remove unusable getLuaTablePropertyName Identifier case --- src/LuaTransformer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 3e1914174..1465d59b8 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -5445,8 +5445,6 @@ export class LuaTransformer { protected getLuaTablePropertyName(node: ts.LeftHandSideExpression): string { if (ts.isPropertyAccessExpression(node)) { return node.name.text; - } else if (ts.isIdentifier(node)) { - return node.text; } else { throw TSTLErrors.UnsupportedKind("LuaTable access expression", node.kind, node); } From b5184e3a6759539a96a64f666d34eb022b747289 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 09:41:55 +1000 Subject: [PATCH 08/13] Change transformLuaTableProperty flow --- src/LuaTransformer.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 1465d59b8..5d5fffe70 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -4477,7 +4477,8 @@ export class LuaTransformer { } if (decorators.has(DecoratorKind.LuaTable)) { - return this.transformLuaTableProperty(expression); + const [luaTable] = this.parseLuaTableExpression(expression); + return luaTable; } // Catch math expressions @@ -4620,17 +4621,21 @@ export class LuaTransformer { } } - protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.Expression { - switch (node.name.text) { + protected parseLuaTableExpression(node: ts.PropertyAccessExpression): [tstl.Expression, string] { + const methodName = node.name.text; + switch (methodName) { case "set": - return this.transformExpression(node.expression); - case "get": - return this.transformExpression(node.expression); - case "length": - const propertyAccessExpression = this.transformExpression(node.expression); - return tstl.createUnaryExpression(propertyAccessExpression, tstl.SyntaxKind.LengthOperator, node); - default: - throw TSTLErrors.UnsupportedProperty("LuaTable", node.name.text, node); + case "get": { + return [this.transformExpression(node.expression), methodName]; + } + case "length": { + const luaTable = this.transformExpression(node.expression); + const unaryExpression = tstl.createUnaryExpression(luaTable, tstl.SyntaxKind.LengthOperator, node); + return [unaryExpression, methodName]; + } + default: { + throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, node); + } } } From d00f8f639e039af16a068b1dbd0aec3afdaae79e Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 10:31:19 +1000 Subject: [PATCH 09/13] Prevent luaTable methods from being isolated --- src/LuaTransformer.ts | 36 ++++++++++----------------- test/unit/decorators/luaTable.spec.ts | 8 ++++++ 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 5d5fffe70..ddca635bc 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -4477,8 +4477,7 @@ export class LuaTransformer { } if (decorators.has(DecoratorKind.LuaTable)) { - const [luaTable] = this.parseLuaTableExpression(expression); - return luaTable; + return this.transformLuaTableProperty(expression); } // Catch math expressions @@ -4621,21 +4620,14 @@ export class LuaTransformer { } } - protected parseLuaTableExpression(node: ts.PropertyAccessExpression): [tstl.Expression, string] { - const methodName = node.name.text; - switch (methodName) { - case "set": - case "get": { - return [this.transformExpression(node.expression), methodName]; - } - case "length": { - const luaTable = this.transformExpression(node.expression); + protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.Expression { + const [luaTable, propertyName] = this.parseLuaTableExpression(node); + switch (propertyName) { + case "length": const unaryExpression = tstl.createUnaryExpression(luaTable, tstl.SyntaxKind.LengthOperator, node); - return [unaryExpression, methodName]; - } - default: { - throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, node); - } + return unaryExpression; + default: + throw TSTLErrors.UnsupportedProperty("LuaTable", propertyName, node); } } @@ -4989,7 +4981,7 @@ export class LuaTransformer { } protected validateLuaTableCall(expression: ts.CallExpression): void { - const methodName = this.getLuaTablePropertyName(expression.expression); + const [, methodName] = this.parseLuaTableExpression(expression.expression); if (expression.arguments.some(argument => ts.isSpreadElement(argument))) { throw TSTLErrors.ForbiddenLuaTableUseException("Arguments cannot be spread.", expression); } @@ -5015,9 +5007,8 @@ export class LuaTransformer { } protected transformLuaTableExpressionAsExpressionStatement(expression: ts.CallExpression): tstl.Statement { - const methodName = this.getLuaTablePropertyName(expression.expression); + const [luaTable, methodName] = this.parseLuaTableExpression(expression.expression); const signature = this.checker.getResolvedSignature(expression); - const luaTable = this.transformExpression(expression.expression); const params = this.transformArguments(expression.arguments, signature); switch (methodName) { @@ -5039,9 +5030,8 @@ export class LuaTransformer { } protected transformLuaTableCallExpression(expression: ts.CallExpression): tstl.Expression { - const methodName = this.getLuaTablePropertyName(expression.expression); + const [luaTable, methodName] = this.parseLuaTableExpression(expression.expression); const signature = this.checker.getResolvedSignature(expression); - const luaTable = this.transformExpression(expression.expression); const params = this.transformArguments(expression.arguments, signature); switch (methodName) { @@ -5447,9 +5437,9 @@ export class LuaTransformer { return scope; } - protected getLuaTablePropertyName(node: ts.LeftHandSideExpression): string { + protected parseLuaTableExpression(node: ts.LeftHandSideExpression): [tstl.Expression, string] { if (ts.isPropertyAccessExpression(node)) { - return node.name.text; + return [this.transformExpression(node.expression), node.name.text]; } else { throw TSTLErrors.UnsupportedKind("LuaTable access expression", node.kind, node); } diff --git a/test/unit/decorators/luaTable.spec.ts b/test/unit/decorators/luaTable.spec.ts index ef229f10b..cc7c3ee39 100644 --- a/test/unit/decorators/luaTable.spec.ts +++ b/test/unit/decorators/luaTable.spec.ts @@ -129,6 +129,14 @@ test.each([tableLibClass, tableLibInterface])("Cannot use ElementAccessExpressio ); }); +test.each([tableLibClass, tableLibInterface])("Cannot isolate LuaTable methods", tableLib => { + test.each([`set`, `get`])("Cannot isolate LuaTable method (%p)", propertyName => { + expect(() => util.transpileString(`${tableLib} let property = tbl.${propertyName}`)).toThrowExactError( + TSTLErrors.UnsupportedProperty("LuaTable", propertyName, util.nodeStub) + ); + }); +}); + test.each([tableLibClass])("LuaTable functional tests", tableLib => { test.each<[string, any]>([ [`const t = new Table(); t.set("field", "value"); return t.get("field");`, "value"], From 9860fcc7781f8bf2190e8081e61c577f6c8d834c Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 10:38:11 +1000 Subject: [PATCH 10/13] Avoid double transformation in validateLuaTableCall --- src/LuaTransformer.ts | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index ddca635bc..4bfde542a 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -2169,7 +2169,6 @@ export class LuaTransformer { const ownerType = this.checker.getTypeAtLocation(expression.expression.expression); const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker); if (classDecorators.has(DecoratorKind.LuaTable)) { - this.validateLuaTableCall(expression); return this.transformLuaTableExpressionAsExpressionStatement(expression); } } @@ -4249,7 +4248,6 @@ export class LuaTransformer { const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker); if (classDecorators.has(DecoratorKind.LuaTable)) { - this.validateLuaTableCall(node); return this.transformLuaTableCallExpression(node); } @@ -4338,7 +4336,6 @@ export class LuaTransformer { const ownerDecorators = tsHelper.getCustomDecorators(ownerType, this.checker); if (ownerDecorators.has(DecoratorKind.LuaTable)) { - this.validateLuaTableCall(node); return this.transformLuaTableCallExpression(node); } @@ -4980,27 +4977,27 @@ export class LuaTransformer { } } - protected validateLuaTableCall(expression: ts.CallExpression): void { - const [, methodName] = this.parseLuaTableExpression(expression.expression); - if (expression.arguments.some(argument => ts.isSpreadElement(argument))) { - throw TSTLErrors.ForbiddenLuaTableUseException("Arguments cannot be spread.", expression); + protected validateLuaTableCall( + methodName: string, + callArguments: ts.NodeArray, + original: ts.Node + ): void { + if (callArguments.some(argument => ts.isSpreadElement(argument))) { + throw TSTLErrors.ForbiddenLuaTableUseException("Arguments cannot be spread.", original); } switch (methodName) { case "get": - if (expression.arguments.length !== 1) { - throw TSTLErrors.ForbiddenLuaTableUseException("One parameter is required for get().", expression); + if (callArguments.length !== 1) { + throw TSTLErrors.ForbiddenLuaTableUseException("One parameter is required for get().", original); } break; case "set": - if (expression.arguments.length !== 2) { - throw TSTLErrors.ForbiddenLuaTableUseException( - "Two parameters are required for set().", - expression - ); + if (callArguments.length !== 2) { + throw TSTLErrors.ForbiddenLuaTableUseException("Two parameters are required for set().", original); } - if (expression.parent.kind !== ts.SyntaxKind.ExpressionStatement) { - throw TSTLErrors.ForbiddenLuaTableSetExpression(expression); + if (original.parent.kind !== ts.SyntaxKind.ExpressionStatement) { + throw TSTLErrors.ForbiddenLuaTableSetExpression(original); } break; } @@ -5008,6 +5005,7 @@ export class LuaTransformer { protected transformLuaTableExpressionAsExpressionStatement(expression: ts.CallExpression): tstl.Statement { const [luaTable, methodName] = this.parseLuaTableExpression(expression.expression); + this.validateLuaTableCall(methodName, expression.arguments, expression); const signature = this.checker.getResolvedSignature(expression); const params = this.transformArguments(expression.arguments, signature); @@ -5031,6 +5029,7 @@ export class LuaTransformer { protected transformLuaTableCallExpression(expression: ts.CallExpression): tstl.Expression { const [luaTable, methodName] = this.parseLuaTableExpression(expression.expression); + this.validateLuaTableCall(methodName, expression.arguments, expression); const signature = this.checker.getResolvedSignature(expression); const params = this.transformArguments(expression.arguments, signature); From 4a0f0229f29292804c6afed520d1e2f3ec6d787a Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 11:02:01 +1000 Subject: [PATCH 11/13] Remove unnecessary luaTable set expression check and specific exception --- src/LuaTransformer.ts | 3 --- src/TSTLErrors.ts | 6 ------ test/unit/decorators/luaTable.spec.ts | 13 ++++++++----- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 4bfde542a..180985bdd 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -4996,9 +4996,6 @@ export class LuaTransformer { if (callArguments.length !== 2) { throw TSTLErrors.ForbiddenLuaTableUseException("Two parameters are required for set().", original); } - if (original.parent.kind !== ts.SyntaxKind.ExpressionStatement) { - throw TSTLErrors.ForbiddenLuaTableSetExpression(original); - } break; } } diff --git a/src/TSTLErrors.ts b/src/TSTLErrors.ts index 8d9966c0b..279e86782 100644 --- a/src/TSTLErrors.ts +++ b/src/TSTLErrors.ts @@ -13,12 +13,6 @@ export const ForbiddenEllipsisDestruction = (node: ts.Node) => export const ForbiddenForIn = (node: ts.Node) => new TranspileError(`Iterating over arrays with 'for ... in' is not allowed.`, node); -export const ForbiddenLuaTableSetExpression = (node: ts.Node) => - new TranspileError( - `A '@luaTable' object's 'set()' method can only be used as a Statement, not an Expression.`, - node - ); - export const ForbiddenLuaTableNonDeclaration = (node: ts.Node) => new TranspileError(`Classes with the '@luaTable' decorator must be declared.`, node); diff --git a/test/unit/decorators/luaTable.spec.ts b/test/unit/decorators/luaTable.spec.ts index cc7c3ee39..e7559f7c0 100644 --- a/test/unit/decorators/luaTable.spec.ts +++ b/test/unit/decorators/luaTable.spec.ts @@ -35,11 +35,14 @@ test.each([tableLibClass])("LuaTables cannot be constructed with arguments", tab ); }); -test.each([tableLibClass, tableLibInterface])("LuaTable set() cannot be used in an expression position", tableLib => { - expect(() => util.transpileString(tableLib + `const exp = tbl.set("value", 5)`)).toThrowExactError( - TSTLErrors.ForbiddenLuaTableSetExpression(util.nodeStub) - ); -}); +test.each([tableLibClass, tableLibInterface])( + "LuaTable set() cannot be used in a LuaTable call expression", + tableLib => { + expect(() => util.transpileString(tableLib + `const exp = tbl.set("value", 5)`)).toThrowExactError( + TSTLErrors.UnsupportedProperty("LuaTable", "set", util.nodeStub) + ); + } +); test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other members", tableLib => { expect(() => util.transpileString(tableLib + `tbl.other()`)).toThrowExactError( From cd5324c776ae7c9512a791f5ad708fe11bc799c5 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 11:17:10 +1000 Subject: [PATCH 12/13] Remove two unneeded line changes --- src/LuaTransformer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 180985bdd..305c9ada0 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -4617,7 +4617,7 @@ export class LuaTransformer { } } - protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.Expression { + protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.UnaryExpression { const [luaTable, propertyName] = this.parseLuaTableExpression(node); switch (propertyName) { case "length": @@ -5034,7 +5034,7 @@ export class LuaTransformer { case "get": return tstl.createTableIndexExpression(luaTable, params[0], expression); default: - throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, expression); + throw TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", methodName, expression); } } From 6da3f69f85fd8214e699c65b1782c3aad654d38d Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 19 Aug 2019 11:17:59 +1000 Subject: [PATCH 13/13] Revert "Remove two unneeded line changes" This reverts commit cd5324c776ae7c9512a791f5ad708fe11bc799c5. --- src/LuaTransformer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 305c9ada0..180985bdd 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -4617,7 +4617,7 @@ export class LuaTransformer { } } - protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.UnaryExpression { + protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.Expression { const [luaTable, propertyName] = this.parseLuaTableExpression(node); switch (propertyName) { case "length": @@ -5034,7 +5034,7 @@ export class LuaTransformer { case "get": return tstl.createTableIndexExpression(luaTable, params[0], expression); default: - throw TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", methodName, expression); + throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, expression); } }