Skip to content

Commit 1ab86da

Browse files
hazzard993Perryvw
authored andcommitted
Fix LuaTable (#702)
* Fix LuaTable * Change getLuaTablePropertyName to if/else statements * Use getCustomDecorators for LuaTable * Change LuaTableExpression tableAccessExpression -> leftHandSideExpression * Revert LuaTable ExpressionStatement conditional change * Use luaTable to identify table access expressions * Remove unusable getLuaTablePropertyName Identifier case * Change transformLuaTableProperty flow * Prevent luaTable methods from being isolated * Avoid double transformation in validateLuaTableCall * Remove unnecessary luaTable set expression check and specific exception * Remove two unneeded line changes * Revert "Remove two unneeded line changes" This reverts commit cd5324c.
1 parent 2c271d7 commit 1ab86da

File tree

3 files changed

+89
-70
lines changed

3 files changed

+89
-70
lines changed

src/LuaTransformer.ts

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,13 +2186,7 @@ export class LuaTransformer {
21862186
const ownerType = this.checker.getTypeAtLocation(expression.expression.expression);
21872187
const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker);
21882188
if (classDecorators.has(DecoratorKind.LuaTable)) {
2189-
this.validateLuaTableCall(
2190-
expression as ts.CallExpression & { expression: ts.PropertyAccessExpression },
2191-
true
2192-
);
2193-
return this.transformLuaTableExpressionStatement(statement as ts.ExpressionStatement & {
2194-
expression: ts.CallExpression;
2195-
} & { expression: { expression: ts.PropertyAccessExpression } });
2189+
return this.transformLuaTableExpressionAsExpressionStatement(expression);
21962190
}
21972191
}
21982192

@@ -4283,10 +4277,7 @@ export class LuaTransformer {
42834277
const classDecorators = tsHelper.getCustomDecorators(ownerType, this.checker);
42844278

42854279
if (classDecorators.has(DecoratorKind.LuaTable)) {
4286-
this.validateLuaTableCall(node as ts.CallExpression & { expression: ts.PropertyAccessExpression }, false);
4287-
return this.transformLuaTableCallExpression(node as ts.CallExpression & {
4288-
expression: ts.PropertyAccessExpression;
4289-
});
4280+
return this.transformLuaTableCallExpression(node);
42904281
}
42914282

42924283
if (tsHelper.isStringType(ownerType, this.checker, this.program)) {
@@ -4370,6 +4361,13 @@ export class LuaTransformer {
43704361
throw TSTLErrors.InvalidElementCall(node);
43714362
}
43724363

4364+
const ownerType = this.checker.getTypeAtLocation(node.expression.expression);
4365+
const ownerDecorators = tsHelper.getCustomDecorators(ownerType, this.checker);
4366+
4367+
if (ownerDecorators.has(DecoratorKind.LuaTable)) {
4368+
return this.transformLuaTableCallExpression(node);
4369+
}
4370+
43734371
const signature = this.checker.getResolvedSignature(node);
43744372
const signatureDeclaration = signature && signature.getDeclaration();
43754373
const parameters = this.transformArguments(node.arguments, signature);
@@ -4648,13 +4646,14 @@ export class LuaTransformer {
46484646
}
46494647
}
46504648

4651-
protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.UnaryExpression {
4652-
switch (node.name.text) {
4649+
protected transformLuaTableProperty(node: ts.PropertyAccessExpression): tstl.Expression {
4650+
const [luaTable, propertyName] = this.parseLuaTableExpression(node);
4651+
switch (propertyName) {
46534652
case "length":
4654-
const propertyAccessExpression = this.transformExpression(node.expression);
4655-
return tstl.createUnaryExpression(propertyAccessExpression, tstl.SyntaxKind.LengthOperator, node);
4653+
const unaryExpression = tstl.createUnaryExpression(luaTable, tstl.SyntaxKind.LengthOperator, node);
4654+
return unaryExpression;
46564655
default:
4657-
throw TSTLErrors.UnsupportedProperty("LuaTable", node.name.text, node);
4656+
throw TSTLErrors.UnsupportedProperty("LuaTable", propertyName, node);
46584657
}
46594658
}
46604659

@@ -4685,6 +4684,11 @@ export class LuaTransformer {
46854684

46864685
const argumentType = this.checker.getTypeAtLocation(expression.argumentExpression);
46874686
const type = this.checker.getTypeAtLocation(expression.expression);
4687+
const decorators = tsHelper.getCustomDecorators(type, this.checker);
4688+
if (decorators.has(DecoratorKind.LuaTable)) {
4689+
throw TSTLErrors.UnsupportedKind("LuaTable access expression", expression.kind, expression);
4690+
}
4691+
46884692
if (
46894693
tsHelper.isNumberType(argumentType, this.checker, this.program) &&
46904694
tsHelper.isStringType(type, this.checker, this.program)
@@ -5003,78 +5007,63 @@ export class LuaTransformer {
50035007
}
50045008

50055009
protected validateLuaTableCall(
5006-
expression: ts.CallExpression & { expression: ts.PropertyAccessExpression },
5007-
isWithinExpressionStatement: boolean
5010+
methodName: string,
5011+
callArguments: ts.NodeArray<ts.Expression>,
5012+
original: ts.Node
50085013
): void {
5009-
const methodName = expression.expression.name.text;
5010-
if (expression.arguments.some(argument => ts.isSpreadElement(argument))) {
5011-
throw TSTLErrors.ForbiddenLuaTableUseException("Arguments cannot be spread.", expression);
5014+
if (callArguments.some(argument => ts.isSpreadElement(argument))) {
5015+
throw TSTLErrors.ForbiddenLuaTableUseException("Arguments cannot be spread.", original);
50125016
}
50135017

50145018
switch (methodName) {
50155019
case "get":
5016-
if (expression.arguments.length !== 1) {
5017-
throw TSTLErrors.ForbiddenLuaTableUseException("One parameter is required for get().", expression);
5020+
if (callArguments.length !== 1) {
5021+
throw TSTLErrors.ForbiddenLuaTableUseException("One parameter is required for get().", original);
50185022
}
50195023
break;
50205024
case "set":
5021-
if (expression.arguments.length !== 2) {
5022-
throw TSTLErrors.ForbiddenLuaTableUseException(
5023-
"Two parameters are required for set().",
5024-
expression
5025-
);
5026-
}
5027-
if (!isWithinExpressionStatement) {
5028-
throw TSTLErrors.ForbiddenLuaTableSetExpression(expression);
5025+
if (callArguments.length !== 2) {
5026+
throw TSTLErrors.ForbiddenLuaTableUseException("Two parameters are required for set().", original);
50295027
}
50305028
break;
50315029
}
50325030
}
50335031

5034-
protected transformLuaTableExpressionStatement(
5035-
node: ts.ExpressionStatement & { expression: ts.CallExpression } & {
5036-
expression: { expression: ts.PropertyAccessExpression };
5037-
}
5038-
): tstl.VariableDeclarationStatement | tstl.AssignmentStatement {
5039-
const methodName = node.expression.expression.name.text;
5040-
const signature = this.checker.getResolvedSignature(node.expression);
5041-
const tableName = (node.expression.expression.expression as ts.Identifier).text;
5042-
const luaTable = tstl.createIdentifier(tableName);
5043-
const params = this.transformArguments((node.expression as ts.CallExpression).arguments, signature);
5032+
protected transformLuaTableExpressionAsExpressionStatement(expression: ts.CallExpression): tstl.Statement {
5033+
const [luaTable, methodName] = this.parseLuaTableExpression(expression.expression);
5034+
this.validateLuaTableCall(methodName, expression.arguments, expression);
5035+
const signature = this.checker.getResolvedSignature(expression);
5036+
const params = this.transformArguments(expression.arguments, signature);
50445037

50455038
switch (methodName) {
50465039
case "get":
50475040
return tstl.createVariableDeclarationStatement(
5048-
tstl.createAnonymousIdentifier(node.expression),
5049-
tstl.createTableIndexExpression(luaTable, params[0], node.expression),
5050-
node.expression
5041+
tstl.createAnonymousIdentifier(expression),
5042+
tstl.createTableIndexExpression(luaTable, params[0], expression),
5043+
expression
50515044
);
50525045
case "set":
50535046
return tstl.createAssignmentStatement(
5054-
tstl.createTableIndexExpression(luaTable, params[0], node.expression),
5047+
tstl.createTableIndexExpression(luaTable, params[0], expression),
50555048
params.splice(1),
5056-
node.expression
5049+
expression
50575050
);
50585051
default:
5059-
throw TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", node.expression);
5052+
throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, expression);
50605053
}
50615054
}
50625055

5063-
protected transformLuaTableCallExpression(
5064-
expression: ts.CallExpression & { expression: ts.PropertyAccessExpression }
5065-
): tstl.Expression {
5066-
const method = expression.expression;
5067-
const methodName = method.name.text;
5056+
protected transformLuaTableCallExpression(expression: ts.CallExpression): tstl.Expression {
5057+
const [luaTable, methodName] = this.parseLuaTableExpression(expression.expression);
5058+
this.validateLuaTableCall(methodName, expression.arguments, expression);
50685059
const signature = this.checker.getResolvedSignature(expression);
5069-
const tableName = (method.expression as ts.Identifier).text;
5070-
const luaTable = tstl.createIdentifier(tableName);
50715060
const params = this.transformArguments(expression.arguments, signature);
50725061

50735062
switch (methodName) {
50745063
case "get":
50755064
return tstl.createTableIndexExpression(luaTable, params[0], expression);
50765065
default:
5077-
throw TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", expression);
5066+
throw TSTLErrors.UnsupportedProperty("LuaTable", methodName, expression);
50785067
}
50795068
}
50805069

@@ -5473,6 +5462,14 @@ export class LuaTransformer {
54735462
return scope;
54745463
}
54755464

5465+
protected parseLuaTableExpression(node: ts.LeftHandSideExpression): [tstl.Expression, string] {
5466+
if (ts.isPropertyAccessExpression(node)) {
5467+
return [this.transformExpression(node.expression), node.name.text];
5468+
} else {
5469+
throw TSTLErrors.UnsupportedKind("LuaTable access expression", node.kind, node);
5470+
}
5471+
}
5472+
54765473
protected transformLuaLibFunction(
54775474
func: LuaLibFeature,
54785475
tsParent?: ts.Expression,

src/TSTLErrors.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ export const CouldNotCast = (castName: string) =>
1010
export const ForbiddenForIn = (node: ts.Node) =>
1111
new TranspileError(`Iterating over arrays with 'for ... in' is not allowed.`, node);
1212

13-
export const ForbiddenLuaTableSetExpression = (node: ts.Node) =>
14-
new TranspileError(
15-
`A '@luaTable' object's 'set()' method can only be used as a Statement, not an Expression.`,
16-
node
17-
);
18-
1913
export const ForbiddenLuaTableNonDeclaration = (node: ts.Node) =>
2014
new TranspileError(`Classes with the '@luaTable' decorator must be declared.`, node);
2115

test/unit/decorators/luaTable.spec.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as ts from "typescript";
12
import * as TSTLErrors from "../../../src/TSTLErrors";
23
import * as util from "../../util";
34

@@ -34,21 +35,24 @@ test.each([tableLibClass])("LuaTables cannot be constructed with arguments", tab
3435
);
3536
});
3637

37-
test.each([tableLibClass, tableLibInterface])("LuaTable set() cannot be used in an expression position", tableLib => {
38-
expect(() => util.transpileString(tableLib + `const exp = tbl.set("value", 5)`)).toThrowExactError(
39-
TSTLErrors.ForbiddenLuaTableSetExpression(util.nodeStub)
40-
);
41-
});
38+
test.each([tableLibClass, tableLibInterface])(
39+
"LuaTable set() cannot be used in a LuaTable call expression",
40+
tableLib => {
41+
expect(() => util.transpileString(tableLib + `const exp = tbl.set("value", 5)`)).toThrowExactError(
42+
TSTLErrors.UnsupportedProperty("LuaTable", "set", util.nodeStub)
43+
);
44+
}
45+
);
4246

43-
test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other methods", tableLib => {
47+
test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other members", tableLib => {
4448
expect(() => util.transpileString(tableLib + `tbl.other()`)).toThrowExactError(
45-
TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", util.nodeStub)
49+
TSTLErrors.UnsupportedProperty("LuaTable", "other", util.nodeStub)
4650
);
4751
});
4852

49-
test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other methods", tableLib => {
53+
test.each([tableLibClass, tableLibInterface])("LuaTables cannot have other members", tableLib => {
5054
expect(() => util.transpileString(tableLib + `let x = tbl.other()`)).toThrowExactError(
51-
TSTLErrors.ForbiddenLuaTableUseException("Unsupported method.", util.nodeStub)
55+
TSTLErrors.UnsupportedProperty("LuaTable", "other", util.nodeStub)
5256
);
5357
});
5458

@@ -113,12 +117,36 @@ test.each([tableLibClass])("Cannot extend LuaTable class", tableLib => {
113117
});
114118
});
115119

120+
test.each([tableLibClass, tableLibInterface])("Cannot use ElementAccessExpression on a LuaTable", tableLib => {
121+
test.each([`tbl["get"]("field")`, `tbl["set"]("field")`, `tbl["length"]`])(
122+
"Cannot use ElementAccessExpression on a LuaTable (%p)",
123+
code => {
124+
expect(() => util.transpileString(tableLib + code)).toThrowExactError(
125+
TSTLErrors.UnsupportedKind(
126+
"LuaTable access expression",
127+
ts.SyntaxKind.ElementAccessExpression,
128+
util.nodeStub
129+
)
130+
);
131+
}
132+
);
133+
});
134+
135+
test.each([tableLibClass, tableLibInterface])("Cannot isolate LuaTable methods", tableLib => {
136+
test.each([`set`, `get`])("Cannot isolate LuaTable method (%p)", propertyName => {
137+
expect(() => util.transpileString(`${tableLib} let property = tbl.${propertyName}`)).toThrowExactError(
138+
TSTLErrors.UnsupportedProperty("LuaTable", propertyName, util.nodeStub)
139+
);
140+
});
141+
});
142+
116143
test.each([tableLibClass])("LuaTable functional tests", tableLib => {
117144
test.each<[string, any]>([
118145
[`const t = new Table(); t.set("field", "value"); return t.get("field");`, "value"],
119146
[`const t = new Table(); t.set("field", 0); return t.get("field");`, 0],
120147
[`const t = new Table(); t.set(1, true); return t.length`, 1],
121148
[`const t = new Table(); t.set(t.length + 1, true); t.set(t.length + 1, true); return t.length`, 2],
149+
[`const k = "k"; const t = { data: new Table() }; t.data.set(k, 3); return t.data.get(k);`, 3],
122150
])("LuaTable test (%p)", (code, expectedReturnValue) => {
123151
expect(util.transpileAndExecute(code, undefined, undefined, tableLib)).toBe(expectedReturnValue);
124152
});

0 commit comments

Comments
 (0)