X Tutup
Skip to content

Commit 44fdccf

Browse files
tomblindPerryvw
authored andcommitted
Fix for property access on string and array literals (#650)
fixes #644 Also addressed a couple other things which came up: - Element call expressions were not adding one to array index - Indexing strings and arrays with types other than numbers created invalid lua code when attempting to add one
1 parent 3d056c8 commit 44fdccf

File tree

6 files changed

+85
-26
lines changed

6 files changed

+85
-26
lines changed

src/LuaTransformer.ts

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3118,12 +3118,7 @@ export class LuaTransformer {
31183118
indexExpression = tstl.createStringLiteral(expression.left.name.text);
31193119
} else {
31203120
// Element access
3121-
indexExpression = this.transformExpression(expression.left.argumentExpression);
3122-
const argType = this.checker.getTypeAtLocation(expression.left.expression);
3123-
if (tsHelper.isArrayType(argType, this.checker, this.program)) {
3124-
// Array access needs a +1
3125-
indexExpression = this.expressionPlusOne(indexExpression);
3126-
}
3121+
indexExpression = this.transformElementAccessArgument(expression.left);
31273122
}
31283123

31293124
const args = [objExpression, indexExpression, this.transformExpression(expression.right)];
@@ -3922,7 +3917,10 @@ export class LuaTransformer {
39223917
);
39233918
} else {
39243919
const parameters = this.transformArguments(node.arguments, signature);
3925-
const table = this.transformExpression(node.expression.expression);
3920+
let table = this.transformExpression(node.expression.expression);
3921+
if (tstl.isTableExpression(table)) {
3922+
table = tstl.createParenthesizedExpression(table);
3923+
}
39263924
const signatureDeclaration = signature && signature.getDeclaration();
39273925
if (
39283926
!signatureDeclaration ||
@@ -3959,7 +3957,10 @@ export class LuaTransformer {
39593957
return this.transformContextualCallExpression(node, parameters);
39603958
} else {
39613959
// No context
3962-
const expression = this.transformExpression(node.expression);
3960+
let expression = this.transformExpression(node.expression);
3961+
if (tstl.isTableExpression(expression)) {
3962+
expression = tstl.createParenthesizedExpression(expression);
3963+
}
39633964
return tstl.createCallExpression(expression, parameters);
39643965
}
39653966
}
@@ -3976,7 +3977,10 @@ export class LuaTransformer {
39763977
tsHelper.isValidLuaIdentifier(left.name.text)
39773978
) {
39783979
// table:name()
3979-
const table = this.transformExpression(left.expression);
3980+
let table = this.transformExpression(left.expression);
3981+
if (tstl.isTableExpression(table)) {
3982+
table = tstl.createParenthesizedExpression(table);
3983+
}
39803984
return tstl.createMethodCallExpression(
39813985
table,
39823986
this.transformIdentifier(left.name),
@@ -3991,10 +3995,9 @@ export class LuaTransformer {
39913995

39923996
// Cache left-side if it has effects
39933997
//(function() local ____TS_self = context; return ____TS_self[argument](parameters); end)()
3994-
const argumentExpression = ts.isElementAccessExpression(left)
3995-
? left.argumentExpression
3996-
: ts.createStringLiteral(left.name.text);
3997-
const argument = this.transformExpression(argumentExpression);
3998+
const argument = ts.isElementAccessExpression(left)
3999+
? this.transformElementAccessArgument(left)
4000+
: tstl.createStringLiteral(left.name.text);
39984001
const selfIdentifier = tstl.createIdentifier("____TS_self");
39994002
const selfAssignment = tstl.createVariableDeclarationStatement(selfIdentifier, context);
40004003
const index = tstl.createTableIndexExpression(selfIdentifier, argument);
@@ -4084,7 +4087,10 @@ export class LuaTransformer {
40844087
}
40854088
}
40864089

4087-
const callPath = this.transformExpression(expression.expression);
4090+
let callPath = this.transformExpression(expression.expression);
4091+
if (tstl.isTableExpression(callPath)) {
4092+
callPath = tstl.createParenthesizedExpression(callPath);
4093+
}
40884094
return tstl.createTableIndexExpression(callPath, tstl.createStringLiteral(property), expression);
40894095
}
40904096

@@ -4188,7 +4194,10 @@ export class LuaTransformer {
41884194
protected transformStringProperty(node: ts.PropertyAccessExpression): tstl.UnaryExpression {
41894195
switch (node.name.escapedText) {
41904196
case "length":
4191-
const expression = this.transformExpression(node.expression);
4197+
let expression = this.transformExpression(node.expression);
4198+
if (ts.isTemplateExpression(node.expression)) {
4199+
expression = tstl.createParenthesizedExpression(expression);
4200+
}
41924201
return tstl.createUnaryExpression(expression, tstl.SyntaxKind.LengthOperator, node);
41934202
default:
41944203
throw TSTLErrors.UnsupportedProperty("string", node.name.escapedText as string, node);
@@ -4199,7 +4208,10 @@ export class LuaTransformer {
41994208
protected transformArrayProperty(node: ts.PropertyAccessExpression): tstl.UnaryExpression | undefined {
42004209
switch (node.name.escapedText) {
42014210
case "length":
4202-
const expression = this.transformExpression(node.expression);
4211+
let expression = this.transformExpression(node.expression);
4212+
if (tstl.isTableExpression(expression)) {
4213+
expression = tstl.createParenthesizedExpression(expression);
4214+
}
42034215
return tstl.createUnaryExpression(expression, tstl.SyntaxKind.LengthOperator, node);
42044216
default:
42054217
return undefined;
@@ -4216,27 +4228,40 @@ export class LuaTransformer {
42164228
}
42174229
}
42184230

4219-
public transformElementAccessExpression(expression: ts.ElementAccessExpression): ExpressionVisitResult {
4220-
const table = this.transformExpression(expression.expression);
4231+
protected transformElementAccessArgument(expression: ts.ElementAccessExpression): tstl.Expression {
42214232
const index = this.transformExpression(expression.argumentExpression);
4233+
const argumentType = this.checker.getTypeAtLocation(expression.argumentExpression);
4234+
const type = this.checker.getTypeAtLocation(expression.expression);
4235+
if (tsHelper.isNumberType(argumentType) && tsHelper.isArrayType(type, this.checker, this.program)) {
4236+
return this.expressionPlusOne(index);
4237+
} else {
4238+
return index;
4239+
}
4240+
}
4241+
4242+
public transformElementAccessExpression(expression: ts.ElementAccessExpression): ExpressionVisitResult {
4243+
let table = this.transformExpression(expression.expression);
4244+
if (tstl.isTableExpression(table)) {
4245+
table = tstl.createParenthesizedExpression(table);
4246+
}
42224247

42234248
const constEnumValue = this.tryGetConstEnumValue(expression);
42244249
if (constEnumValue) {
42254250
return constEnumValue;
42264251
}
42274252

4253+
const argumentType = this.checker.getTypeAtLocation(expression.argumentExpression);
42284254
const type = this.checker.getTypeAtLocation(expression.expression);
4229-
if (tsHelper.isArrayType(type, this.checker, this.program)) {
4230-
return tstl.createTableIndexExpression(table, this.expressionPlusOne(index), expression);
4231-
} else if (tsHelper.isStringType(type)) {
4255+
if (tsHelper.isNumberType(argumentType) && tsHelper.isStringType(type)) {
4256+
const index = this.transformExpression(expression.argumentExpression);
42324257
return tstl.createCallExpression(
42334258
tstl.createTableIndexExpression(tstl.createIdentifier("string"), tstl.createStringLiteral("sub")),
42344259
[table, this.expressionPlusOne(index), this.expressionPlusOne(index)],
42354260
expression
42364261
);
4237-
} else {
4238-
return tstl.createTableIndexExpression(table, index, expression);
42394262
}
4263+
4264+
return tstl.createTableIndexExpression(table, this.transformElementAccessArgument(expression), expression);
42404265
}
42414266

42424267
private tryGetConstEnumValue(
@@ -4369,7 +4394,7 @@ export class LuaTransformer {
43694394
case "upper":
43704395
// Allow lua's string instance methods
43714396
let stringVariable = this.transformExpression(expression.expression);
4372-
if (ts.isStringLiteral(expression.expression)) {
4397+
if (ts.isStringLiteralLike(expression.expression)) {
43734398
// "foo":method() needs to be ("foo"):method()
43744399
stringVariable = tstl.createParenthesizedExpression(stringVariable);
43754400
}

src/lualib/ArrayIndexOf.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function __TS__ArrayIndexOf<T>(this: void, arr: T[], searchElement: T, fromIndex
1313
return -1;
1414
}
1515

16-
let k;
16+
let k: number;
1717
if (n >= 0) {
1818
k = n;
1919
} else {

src/lualib/ArraySplice.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
function __TS__ArraySplice<T>(this: void, list: T[], start: number, deleteCount: number, ...items: T[]): T[] {
22
const len = list.length;
33

4-
let actualStart;
4+
let actualStart: number;
55

66
if (start < 0) {
77
actualStart = Math.max(len + start, 0);

test/unit/array.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,19 @@ test("OmittedExpression in Array Binding Assignment Statement", () => {
210210

211211
expect(result).toBe(4);
212212
});
213+
214+
test("array access call", () => {
215+
const code = `
216+
const arr = [() => "foo", () => "bar"];
217+
return arr[1]();`;
218+
expect(util.transpileAndExecute(code)).toBe("bar");
219+
});
220+
221+
test.each([`["foo", "bar"].length`, `["foo", "bar"][0]`, `[() => "foo", () => "bar"][0]()`])(
222+
"array literal property access (%p)",
223+
expression => {
224+
const code = `return ${expression}`;
225+
const expectResult = eval(expression);
226+
expect(util.transpileAndExecute(code)).toBe(expectResult);
227+
}
228+
);

test/unit/objectLiteral.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,12 @@ test("undefined as object key", () => {
5555
return foo.undefined;`;
5656
expect(util.transpileAndExecute(code)).toBe("foo");
5757
});
58+
59+
test.each([`({x: "foobar"}.x)`, `({x: "foobar"}["x"])`, `({x: () => "foobar"}.x())`, `({x: () => "foobar"}["x"]())`])(
60+
"object literal property access (%p)",
61+
expression => {
62+
const code = `return ${expression}`;
63+
const expectResult = eval(expression);
64+
expect(util.transpileAndExecute(code)).toBe(expectResult);
65+
}
66+
);

test/unit/string.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,12 @@ test.each(padCases)("string.padEnd (%p)", ({ inp, maxLength, fillString }) => {
339339

340340
expect(result).toBe(inp.padEnd(maxLength, fillString));
341341
});
342+
343+
test.each([`"foobar".length`, `"foobar".repeat(2)`, "`foo${'bar'}`.length", "`foo${'bar'}`.repeat(2)"])(
344+
"string literal property access (%p)",
345+
expression => {
346+
const code = `return ${expression}`;
347+
const expectResult = eval(expression);
348+
expect(util.transpileAndExecute(code)).toBe(expectResult);
349+
}
350+
);

0 commit comments

Comments
 (0)
X Tutup