Skip to content

Commit 00735b7

Browse files
authored
Fix side effects in string index causing issues (#868)
* Fix side effects in string index causing issues * Removed unnecessary variable
1 parent 375e009 commit 00735b7

File tree

2 files changed

+44
-10
lines changed

2 files changed

+44
-10
lines changed

src/transformation/visitors/access.ts

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { transformBuiltinPropertyAccessExpression } from "../builtins";
44
import { FunctionVisitor, TransformationContext } from "../context";
55
import { AnnotationKind, getTypeAnnotations } from "../utils/annotations";
66
import { createExpressionPlusOne } from "../utils/lua-ast";
7-
import { isArrayType, isNumberType, isStringType } from "../utils/typescript";
7+
import { isArrayType, isNumberType, isStringType, isExpressionWithEvaluationEffect } from "../utils/typescript";
88
import { tryGetConstEnumValue } from "./enum";
99
import { transformLuaTablePropertyAccessExpression, validateLuaTableElementAccessExpression } from "./lua-table";
1010

@@ -31,20 +31,17 @@ export const transformElementAccessExpression: FunctionVisitor<ts.ElementAccessE
3131
return constEnumValue;
3232
}
3333

34-
const table = context.transformExpression(expression.expression);
35-
3634
const argumentType = context.checker.getTypeAtLocation(expression.argumentExpression);
3735
const type = context.checker.getTypeAtLocation(expression.expression);
3836
if (isNumberType(context, argumentType) && isStringType(context, type)) {
39-
const index = context.transformExpression(expression.argumentExpression);
40-
return lua.createCallExpression(
41-
lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("sub")),
42-
[table, createExpressionPlusOne(index), createExpressionPlusOne(index)],
43-
expression
44-
);
37+
return transformStringIndex(context, expression);
4538
}
4639

47-
return lua.createTableIndexExpression(table, transformElementAccessArgument(context, expression), expression);
40+
return lua.createTableIndexExpression(
41+
context.transformExpression(expression.expression),
42+
transformElementAccessArgument(context, expression),
43+
expression
44+
);
4845
};
4946

5047
export const transformPropertyAccessExpression: FunctionVisitor<ts.PropertyAccessExpression> = (
@@ -94,3 +91,32 @@ export const transformQualifiedName: FunctionVisitor<ts.QualifiedName> = (node,
9491

9592
return lua.createTableIndexExpression(left, right, node);
9693
};
94+
95+
function transformStringIndex(context: TransformationContext, expression: ts.ElementAccessExpression): lua.Expression {
96+
const string = context.transformExpression(expression.expression);
97+
// Translate to string.sub(str, index, index), cache index in case it has side effects.
98+
if (isExpressionWithEvaluationEffect(expression.argumentExpression)) {
99+
const indexIdentifier = lua.createIdentifier("____index");
100+
// string.sub(stringExpression, ____index, ____index)
101+
const subCall = lua.createCallExpression(
102+
lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("sub")),
103+
[string, lua.cloneIdentifier(indexIdentifier), lua.cloneIdentifier(indexIdentifier)],
104+
expression
105+
);
106+
// function(____index) string.sub(stringExpression, ____index, ____index)
107+
const functionExpression = lua.createFunctionExpression(
108+
lua.createBlock([lua.createReturnStatement([subCall])]),
109+
[lua.cloneIdentifier(indexIdentifier)]
110+
);
111+
// (function(____index) string.sub(stringExpression, ____index, ____index) end)(index + 1)
112+
const indexPlusOne = createExpressionPlusOne(context.transformExpression(expression.argumentExpression));
113+
return lua.createCallExpression(functionExpression, [indexPlusOne]);
114+
} else {
115+
const index = context.transformExpression(expression.argumentExpression);
116+
return lua.createCallExpression(
117+
lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("sub")),
118+
[string, createExpressionPlusOne(index), createExpressionPlusOne(index)],
119+
expression
120+
);
121+
}
122+
}

test/unit/builtins/string.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ test.each([
4141
util.testExpressionTemplate`${input}[${index}]`.expectToMatchJsResult();
4242
});
4343

44+
test("string index with side effect", () => {
45+
util.testFunction`
46+
let i = 0;
47+
const mystring = "abc";
48+
return mystring[i++];
49+
`.expectToMatchJsResult();
50+
});
51+
4452
test.each([
4553
{ inp: "hello test", searchValue: "", replaceValue: "" },
4654
{ inp: "hello test", searchValue: " ", replaceValue: "" },

0 commit comments

Comments
 (0)