Skip to content

Commit 03fae35

Browse files
authored
Implement function.length (#889)
* Implement `function.length` * Add diagnostic on Lua 5.1 target * Add changelog * Remove outdated snapshots
1 parent 45aaac6 commit 03fae35

File tree

5 files changed

+91
-2
lines changed

5 files changed

+91
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- `Function.length` is supported now
6+
37
## 0.34.0
48

59
- Added new `"luaTarget"` option value - `"universal"`. Choosing this target makes TypeScriptToLua generate code compatible with all supported Lua targets.

src/transformation/builtins/function.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import * as ts from "typescript";
2+
import { LuaTarget } from "../../CompilerOptions";
13
import * as lua from "../../LuaAST";
24
import { TransformationContext } from "../context";
3-
import { unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics";
5+
import { unsupportedForTarget, unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics";
46
import { ContextType, getFunctionContextType } from "../utils/function-context";
57
import { createUnpackCall } from "../utils/lua-ast";
68
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
@@ -35,3 +37,31 @@ export function transformFunctionPrototypeCall(
3537
context.diagnostics.push(unsupportedProperty(expression.name, "function", expressionName));
3638
}
3739
}
40+
41+
export function transformFunctionProperty(
42+
context: TransformationContext,
43+
node: ts.PropertyAccessExpression
44+
): lua.Expression | undefined {
45+
switch (node.name.text) {
46+
case "length":
47+
if (context.luaTarget === LuaTarget.Lua51 || context.luaTarget === LuaTarget.Universal) {
48+
context.diagnostics.push(unsupportedForTarget(node, "function.length", LuaTarget.Lua51));
49+
}
50+
51+
// debug.getinfo(fn)
52+
const getInfoCall = lua.createCallExpression(
53+
lua.createTableIndexExpression(lua.createIdentifier("debug"), lua.createStringLiteral("getinfo")),
54+
[context.transformExpression(node.expression)]
55+
);
56+
57+
const nparams = lua.createTableIndexExpression(getInfoCall, lua.createStringLiteral("nparams"));
58+
59+
const contextType = getFunctionContextType(context, context.checker.getTypeAtLocation(node.expression));
60+
return contextType === ContextType.NonVoid
61+
? lua.createBinaryExpression(nparams, lua.createNumericLiteral(1), lua.SyntaxKind.SubtractionOperator)
62+
: nparams;
63+
64+
default:
65+
context.diagnostics.push(unsupportedProperty(node.name, "function", node.name.text));
66+
}
67+
}

src/transformation/builtins/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { PropertyCallExpression } from "../visitors/call";
1616
import { checkForLuaLibType } from "../visitors/class/new";
1717
import { transformArrayProperty, transformArrayPrototypeCall } from "./array";
1818
import { transformConsoleCall } from "./console";
19-
import { transformFunctionPrototypeCall } from "./function";
19+
import { transformFunctionPrototypeCall, transformFunctionProperty } from "./function";
2020
import { transformGlobalCall } from "./global";
2121
import { transformMathCall, transformMathProperty } from "./math";
2222
import { transformNumberConstructorCall, transformNumberPrototypeCall } from "./number";
@@ -38,6 +38,10 @@ export function transformBuiltinPropertyAccessExpression(
3838
return transformArrayProperty(context, node);
3939
}
4040

41+
if (isFunctionType(context, ownerType)) {
42+
return transformFunctionProperty(context, node);
43+
}
44+
4145
if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, ownerType, undefined)) {
4246
switch (node.expression.text) {
4347
case "Math":

test/unit/functions/__snapshots__/functions.spec.ts.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`function.length unsupported ("5.1"): code 1`] = `
4+
"local ____exports = {}
5+
function ____exports.__main(self)
6+
local function fn(self)
7+
end
8+
return debug.getinfo(fn).nparams - 1
9+
end
10+
return ____exports"
11+
`;
12+
13+
exports[`function.length unsupported ("5.1"): diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`;
14+
15+
exports[`function.length unsupported ("universal"): code 1`] = `
16+
"local ____exports = {}
17+
function ____exports.__main(self)
18+
local function fn(self)
19+
end
20+
return debug.getinfo(fn).nparams - 1
21+
end
22+
return ____exports"
23+
`;
24+
25+
exports[`function.length unsupported ("universal"): diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`;
26+
327
exports[`missing declaration name: code 1`] = `
428
"function ____(self)
529
end"

test/unit/functions/functions.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import * as tstl from "../../../src";
12
import * as util from "../../util";
3+
import { unsupportedForTarget } from "../../../src/transformation/utils/diagnostics";
24

35
test("Arrow Function Expression", () => {
46
util.testFunction`
@@ -185,6 +187,31 @@ test("Function call", () => {
185187
`.expectToMatchJsResult();
186188
});
187189

190+
test.each([
191+
"function fn() {}",
192+
"function fn(x, y, z) {}",
193+
"function fn(x, y, z, ...args) {}",
194+
"function fn(...args) {}",
195+
"function fn(this: void) {}",
196+
"function fn(this: void, x, y, z) {}",
197+
"function fnReference(x, y, z) {} const fn = fnReference;",
198+
"const wrap = (fn: (...args: any[]) => any) => (...args: any[]) => fn(...args); const fn = wrap((x, y, z) => {});",
199+
])("function.length (%p)", declaration => {
200+
util.testFunction`
201+
${declaration}
202+
return fn.length;
203+
`.expectToMatchJsResult();
204+
});
205+
206+
test.each([tstl.LuaTarget.Lua51, tstl.LuaTarget.Universal])("function.length unsupported (%p)", luaTarget => {
207+
util.testFunction`
208+
function fn() {}
209+
return fn.length;
210+
`
211+
.setOptions({ luaTarget })
212+
.expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]);
213+
});
214+
188215
test("Recursive function definition", () => {
189216
util.testFunction`
190217
function f() { return typeof f; };

0 commit comments

Comments
 (0)