Skip to content

Commit 29047b5

Browse files
authored
ArraySetLength lib function (#521)
* added ArrayLengthSet lib function * Disabled tslint error in ArraySetLength * Handling NaN, Infinity, and non-integers * Removed unneeded -Infinity check
1 parent 3df8dfb commit 29047b5

File tree

5 files changed

+103
-0
lines changed

5 files changed

+103
-0
lines changed

src/LuaLib.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export enum LuaLibFeature {
1919
ArraySplice = "ArraySplice",
2020
ArrayFlat = "ArrayFlat",
2121
ArrayFlatMap = "ArrayFlatMap",
22+
ArraySetLength = "ArraySetLength",
2223
ClassIndex = "ClassIndex",
2324
ClassNewIndex = "ClassNewIndex",
2425
FunctionApply = "FunctionApply",

src/LuaTransformer.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,18 @@ export class LuaTransformer {
24942494
const leftType = this.checker.getTypeAtLocation(expression.left);
24952495
this.validateFunctionAssignment(expression.right, rightType, leftType);
24962496

2497+
if (tsHelper.isArrayLengthAssignment(expression, this.checker, this.program)) {
2498+
// array.length = x
2499+
return tstl.createExpressionStatement(
2500+
this.transformLuaLibFunction(
2501+
LuaLibFeature.ArraySetLength,
2502+
expression,
2503+
this.transformExpression(expression.left.expression),
2504+
this.transformExpression(expression.right)
2505+
)
2506+
);
2507+
}
2508+
24972509
if (ts.isArrayLiteralExpression(expression.left)) {
24982510
// Destructuring assignment
24992511
const left = expression.left.elements.length > 0
@@ -2528,6 +2540,16 @@ export class LuaTransformer {
25282540
const leftType = this.checker.getTypeAtLocation(expression.left);
25292541
this.validateFunctionAssignment(expression.right, rightType, leftType);
25302542

2543+
if (tsHelper.isArrayLengthAssignment(expression, this.checker, this.program)) {
2544+
// array.length = x
2545+
return this.transformLuaLibFunction(
2546+
LuaLibFeature.ArraySetLength,
2547+
expression,
2548+
this.transformExpression(expression.left.expression),
2549+
this.transformExpression(expression.right)
2550+
);
2551+
}
2552+
25312553
if (ts.isArrayLiteralExpression(expression.left)) {
25322554
// Destructuring assignment
25332555
// (function() local ${tmps} = ${right}; ${left} = ${tmps}; return {${tmps}} end)()

src/TSHelper.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,4 +752,30 @@ export class TSHelper {
752752
}
753753
return false;
754754
}
755+
756+
public static isArrayLengthAssignment(
757+
expression: ts.BinaryExpression,
758+
checker: ts.TypeChecker,
759+
program: ts.Program
760+
): expression is ts.BinaryExpression & { left: ts.PropertyAccessExpression | ts.ElementAccessExpression; }
761+
{
762+
if (expression.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
763+
return false;
764+
}
765+
766+
if (!ts.isPropertyAccessExpression(expression.left) && !ts.isElementAccessExpression(expression.left)) {
767+
return false;
768+
}
769+
770+
const type = checker.getTypeAtLocation(expression.left.expression);
771+
if (!TSHelper.isArrayType(type, checker, program)) {
772+
return false;
773+
}
774+
775+
const name = ts.isPropertyAccessExpression(expression.left)
776+
? expression.left.name.escapedText as string
777+
: ts.isStringLiteral(expression.left.argumentExpression) && expression.left.argumentExpression.text;
778+
779+
return name === "length";
780+
}
755781
}

src/lualib/ArraySetLength.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
function __TS__ArraySetLength<T>(this: void, arr: T[], length: number): number {
2+
if (length < 0
3+
|| length !== length // NaN
4+
|| length === (1 / 0) // Infinity
5+
|| Math.floor(length) !== length) // non-integer
6+
{
7+
// tslint:disable-next-line:no-string-throw
8+
throw `invalid array length: ${length}`;
9+
}
10+
for (let i = arr.length - 1; i >= length; --i) {
11+
arr[i] = undefined;
12+
}
13+
return length;
14+
}

test/unit/array.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,43 @@ test("Array property access", () => {
136136
`;
137137
expect(util.transpileAndExecute(code)).toBe("bar123");
138138
});
139+
140+
test.each([{ length: 0, result: 0 }, { length: 1, result: 1 }, { length: 7, result: 3 }])(
141+
"Array length set",
142+
({ length, result }) => {
143+
const code = `
144+
const arr = [1, 2, 3];
145+
arr.length = ${length};
146+
return arr.length;
147+
`;
148+
expect(util.transpileAndExecute(code)).toBe(result);
149+
},
150+
);
151+
152+
test.each([
153+
{ length: 0, result: "0/0" },
154+
{ length: 1, result: "1/1" },
155+
{ length: 7, result: "7/3" },
156+
])("Array length set as expression", ({ length, result }) => {
157+
const code = `
158+
const arr = [1, 2, 3];
159+
const l = arr.length = ${length};
160+
return \`\${l}/\${arr.length}\`;
161+
`;
162+
expect(util.transpileAndExecute(code)).toBe(result);
163+
});
164+
165+
test.each([
166+
{ length: -1, result: -1 },
167+
{ length: -7, result: -7 },
168+
{ length: 0.1, result: 0.1 },
169+
{ length: "0 / 0", result: "NaN" },
170+
{ length: "1 / 0", result: "Infinity" },
171+
{ length: "-1 / 0", result: "-Infinity" },
172+
])("Invalid array length set", ({ length, result }) => {
173+
const code = `
174+
const arr = [1, 2, 3];
175+
arr.length = ${length};
176+
`;
177+
expect(() => util.transpileAndExecute(code)).toThrowError(`invalid array length: ${result}`);
178+
});

0 commit comments

Comments
 (0)