X Tutup
Skip to content

Commit b4366cd

Browse files
tomblindPerryvw
authored andcommitted
correctly passing context to functions accessed as elements (#313)
* correctly passing context to functions accessed as elements * added InvalidElementCall error and refactored transpileElementCall for clarity
1 parent 8597fd4 commit b4366cd

File tree

3 files changed

+91
-11
lines changed

3 files changed

+91
-11
lines changed

src/Errors.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export class TSTLErrors {
4040
public static InvalidPropertyCall = (node: ts.Node) =>
4141
new TranspileError(`Tried to transpile a non-property call as property call.`, node)
4242

43+
public static InvalidElementCall = (node: ts.Node) =>
44+
new TranspileError(`Tried to transpile a non-element call as an element call.`, node)
45+
4346
public static InvalidThrowExpression = (node: ts.Node) =>
4447
new TranspileError(`Invalid throw expression, only strings can be thrown.`, node)
4548

src/Transpiler.ts

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,11 +1171,16 @@ export abstract class LuaTranspiler {
11711171
&& tsHelper.isInTupleReturnFunction(node, this.checker);
11721172
const isInDestructingAssignment = tsHelper.isInDestructingAssignment(node);
11731173
const returnValueIsUsed = node.parent && !ts.isExpressionStatement(node.parent);
1174+
const wrapResult = isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment && returnValueIsUsed;
11741175

11751176
if (ts.isPropertyAccessExpression(node.expression)) {
11761177
const result = this.transpilePropertyCall(node);
1177-
return isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment && returnValueIsUsed
1178-
? `({ ${result} })` : result;
1178+
return wrapResult ? `({ ${result} })` : result;
1179+
}
1180+
1181+
if (ts.isElementAccessExpression(node.expression)) {
1182+
const result = this.transpileElementCall(node);
1183+
return wrapResult ? `({ ${result} })` : result;
11791184
}
11801185

11811186
const signature = this.checker.getResolvedSignature(node);
@@ -1198,8 +1203,7 @@ export abstract class LuaTranspiler {
11981203
} else {
11991204
parameters = this.transpileArguments(node.arguments, signature);
12001205
}
1201-
return isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment && returnValueIsUsed
1202-
? `({ ${callPath}(${parameters}) })` : `${callPath}(${parameters})`;
1206+
return wrapResult ? `({ ${callPath}(${parameters}) })` : `${callPath}(${parameters})`;
12031207
}
12041208

12051209
public transpilePropertyCall(node: ts.CallExpression): string {
@@ -1218,7 +1222,7 @@ export abstract class LuaTranspiler {
12181222
return this.transpileMathExpression(node.expression.name) + `(${parameters})`;
12191223
}
12201224

1221-
if (this.transpileExpression((node.expression as ts.PropertyAccessExpression).expression) === "String") {
1225+
if (this.transpileExpression(node.expression.expression) === "String") {
12221226
parameters = this.transpileArguments(node.arguments);
12231227
return this.transpileStringExpression(node.expression.name) + `(${parameters})`;
12241228
}
@@ -1241,12 +1245,6 @@ export abstract class LuaTranspiler {
12411245
return this.transpileArrayCallExpression(node);
12421246
}
12431247

1244-
// if ownerType inherits from an array, use array calls where appropriate
1245-
if (tsHelper.isArrayType(ownerType, this.checker)
1246-
&& tsHelper.isDefaultArrayCallMethodName(this.transpileIdentifier(node.expression.name))) {
1247-
return this.transpileArrayCallExpression(node);
1248-
}
1249-
12501248
if (tsHelper.isFunctionType(ownerType, this.checker)) {
12511249
return this.transpileFunctionCallExpression(node);
12521250
}
@@ -1279,6 +1277,47 @@ export abstract class LuaTranspiler {
12791277
}
12801278
}
12811279

1280+
public transpileElementCall(node: ts.CallExpression): string {
1281+
if (!ts.isElementAccessExpression(node.expression)) {
1282+
throw TSTLErrors.InvalidElementCall(node);
1283+
}
1284+
1285+
const signature = this.checker.getResolvedSignature(node);
1286+
let parameters = this.transpileArguments(node.arguments, signature);
1287+
1288+
const signatureDeclaration = signature.getDeclaration();
1289+
if (!signatureDeclaration
1290+
|| tsHelper.getDeclarationContextType(signatureDeclaration, this.checker) !== ContextType.Void) {
1291+
// Pass left-side as context
1292+
1293+
// Inject context parameter
1294+
if (node.arguments.length > 0) {
1295+
parameters = "____TS_self, " + parameters;
1296+
} else {
1297+
parameters = "____TS_self";
1298+
}
1299+
1300+
const context = this.transpileExpression(node.expression.expression);
1301+
if (tsHelper.isExpressionWithEvaluationEffect(node.expression.expression)) {
1302+
// Cache left-side if it has effects
1303+
const argument = this.transpileExpression(node.expression.argumentExpression);
1304+
if (tsHelper.isExpressionStatement(node)) {
1305+
// Statement version
1306+
return `do local ____TS_self = ${context}; ____TS_self[${argument}](${parameters}); end`;
1307+
} else {
1308+
// Expression version
1309+
return `(function() local ____TS_self = ${context}; `
1310+
+ `return ____TS_self[${argument}](${parameters}); end)()`;
1311+
}
1312+
} else {
1313+
return `${this.transpileExpression(node.expression)}(${context}${parameters})`;
1314+
}
1315+
} else {
1316+
// No context
1317+
return `${this.transpileExpression(node.expression)}(${parameters})`;
1318+
}
1319+
}
1320+
12821321
public transpileStringCallExpression(node: ts.CallExpression): string {
12831322
const expression = node.expression as ts.PropertyAccessExpression;
12841323
const params = this.transpileArguments(node.arguments);

test/unit/functions.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,4 +381,42 @@ export class FunctionTests {
381381

382382
Expect(result).toBe(s1 === s2);
383383
}
384+
385+
@Test("Element access call")
386+
public elementAccessCall(): void {
387+
const code = `class C {
388+
method(s: string) { return s; }
389+
}
390+
const c = new C();
391+
return c['method']("foo");
392+
`;
393+
const result = util.transpileAndExecute(code);
394+
Expect(result).toBe("foo");
395+
}
396+
397+
@Test("Complex element access call")
398+
public elementAccessCallComplex(): void {
399+
const code = `class C {
400+
method(s: string) { return s; }
401+
}
402+
function getC() { return new C(); }
403+
return getC()['method']("foo");
404+
`;
405+
const result = util.transpileAndExecute(code);
406+
Expect(result).toBe("foo");
407+
}
408+
409+
@Test("Complex element access call statement")
410+
public elementAccessCallComplexStatement(): void {
411+
const code = `let foo: string;
412+
class C {
413+
method(s: string) { foo = s; }
414+
}
415+
function getC() { return new C(); }
416+
getC()['method']("foo");
417+
return foo;
418+
`;
419+
const result = util.transpileAndExecute(code);
420+
Expect(result).toBe("foo");
421+
}
384422
}

0 commit comments

Comments
 (0)
X Tutup