From 2a73071feb22dcb10d14a4d76a5e043f070fc708 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 1 Jun 2020 01:10:47 +0000 Subject: [PATCH 1/4] Implement `function.length` --- src/transformation/builtins/function.ts | 25 +++++++++++++++++++++++++ src/transformation/builtins/index.ts | 6 +++++- test/unit/functions/functions.spec.ts | 16 ++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/transformation/builtins/function.ts b/src/transformation/builtins/function.ts index 0d84e48a8..25299b09c 100644 --- a/src/transformation/builtins/function.ts +++ b/src/transformation/builtins/function.ts @@ -1,3 +1,4 @@ +import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics"; @@ -35,3 +36,27 @@ export function transformFunctionPrototypeCall( context.diagnostics.push(unsupportedProperty(expression.name, "function", expressionName)); } } + +export function transformFunctionProperty( + context: TransformationContext, + node: ts.PropertyAccessExpression +): lua.Expression | undefined { + switch (node.name.text) { + case "length": + // debug.getinfo(fn) + const getInfoCall = lua.createCallExpression( + lua.createTableIndexExpression(lua.createIdentifier("debug"), lua.createStringLiteral("getinfo")), + [context.transformExpression(node.expression)] + ); + + const nparams = lua.createTableIndexExpression(getInfoCall, lua.createStringLiteral("nparams")); + + const contextType = getFunctionContextType(context, context.checker.getTypeAtLocation(node.expression)); + return contextType === ContextType.NonVoid + ? lua.createBinaryExpression(nparams, lua.createNumericLiteral(1), lua.SyntaxKind.SubtractionOperator) + : nparams; + + default: + context.diagnostics.push(unsupportedProperty(node.name, "function", node.name.text)); + } +} diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index f6be3c0f2..352ac9c2f 100644 --- a/src/transformation/builtins/index.ts +++ b/src/transformation/builtins/index.ts @@ -16,7 +16,7 @@ import { PropertyCallExpression } from "../visitors/call"; import { checkForLuaLibType } from "../visitors/class/new"; import { transformArrayProperty, transformArrayPrototypeCall } from "./array"; import { transformConsoleCall } from "./console"; -import { transformFunctionPrototypeCall } from "./function"; +import { transformFunctionPrototypeCall, transformFunctionProperty } from "./function"; import { transformGlobalCall } from "./global"; import { transformMathCall, transformMathProperty } from "./math"; import { transformNumberConstructorCall, transformNumberPrototypeCall } from "./number"; @@ -38,6 +38,10 @@ export function transformBuiltinPropertyAccessExpression( return transformArrayProperty(context, node); } + if (isFunctionType(context, ownerType)) { + return transformFunctionProperty(context, node); + } + if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, ownerType, undefined)) { switch (node.expression.text) { case "Math": diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index 7e4aa6a08..f5fc96e52 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -185,6 +185,22 @@ test("Function call", () => { `.expectToMatchJsResult(); }); +test.each([ + "function fn() {}", + "function fn(x, y, z) {}", + "function fn(x, y, z, ...args) {}", + "function fn(...args) {}", + "function fn(this: void) {}", + "function fn(this: void, x, y, z) {}", + "function fnReference(x, y, z) {} const fn = fnReference;", + "const wrap = (fn: (...args: any[]) => any) => (...args: any[]) => fn(...args); const fn = wrap((x, y, z) => {});", +])("function.length (%p)", declaration => { + util.testFunction` + ${declaration} + return fn.length; + `.expectToMatchJsResult(); +}); + test("Recursive function definition", () => { util.testFunction` function f() { return typeof f; }; From 822ffb957cb181cde795922da86528d3ab794412 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 1 Jun 2020 01:50:40 +0000 Subject: [PATCH 2/4] Add diagnostic on Lua 5.1 target --- src/transformation/builtins/function.ts | 7 ++- .../__snapshots__/functions.spec.ts.snap | 48 +++++++++++++++++++ test/unit/functions/functions.spec.ts | 11 +++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/transformation/builtins/function.ts b/src/transformation/builtins/function.ts index 25299b09c..f6cb8a6a0 100644 --- a/src/transformation/builtins/function.ts +++ b/src/transformation/builtins/function.ts @@ -1,7 +1,8 @@ import * as ts from "typescript"; +import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; -import { unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics"; +import { unsupportedForTarget, unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics"; import { ContextType, getFunctionContextType } from "../utils/function-context"; import { createUnpackCall } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; @@ -43,6 +44,10 @@ export function transformFunctionProperty( ): lua.Expression | undefined { switch (node.name.text) { case "length": + if (context.luaTarget === LuaTarget.Lua51 || context.luaTarget === LuaTarget.Universal) { + context.diagnostics.push(unsupportedForTarget(node, "function.length", LuaTarget.Lua51)); + } + // debug.getinfo(fn) const getInfoCall = lua.createCallExpression( lua.createTableIndexExpression(lua.createIdentifier("debug"), lua.createStringLiteral("getinfo")), diff --git a/test/unit/functions/__snapshots__/functions.spec.ts.snap b/test/unit/functions/__snapshots__/functions.spec.ts.snap index e1e5b49d2..a011675a4 100644 --- a/test/unit/functions/__snapshots__/functions.spec.ts.snap +++ b/test/unit/functions/__snapshots__/functions.spec.ts.snap @@ -1,5 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`function.length unsupported ("5.1"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function fn(self) + end + return debug.getinfo(fn).nparams - 1 +end +return ____exports" +`; + +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."`; + +exports[`function.length unsupported ("universal"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function fn(self) + end + return debug.getinfo(fn).nparams - 1 +end +return ____exports" +`; + +exports[`function.length unsupported ("universal"): diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; + +exports[`function.length unsupported on 5.1: code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function fn(self) + end + return debug.getinfo(fn).nparams - 1 +end +return ____exports" +`; + +exports[`function.length unsupported on 5.1: diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; + +exports[`function.length unsupported on universal: code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function fn(self) + end + return debug.getinfo(fn).nparams - 1 +end +return ____exports" +`; + +exports[`function.length unsupported on universal: diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; + exports[`missing declaration name: code 1`] = ` "function ____(self) end" diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index f5fc96e52..20f76e273 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -1,4 +1,6 @@ +import * as tstl from "../../../src"; import * as util from "../../util"; +import { unsupportedForTarget } from "../../../src/transformation/utils/diagnostics"; test("Arrow Function Expression", () => { util.testFunction` @@ -201,6 +203,15 @@ test.each([ `.expectToMatchJsResult(); }); +test.each([tstl.LuaTarget.Lua51, tstl.LuaTarget.Universal])("function.length unsupported (%p)", luaTarget => { + util.testFunction` + function fn() {} + return fn.length; + ` + .setOptions({ luaTarget }) + .expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]); +}); + test("Recursive function definition", () => { util.testFunction` function f() { return typeof f; }; From 77f8276c626e0389a6649536416011ee35da124e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 1 Jun 2020 01:53:38 +0000 Subject: [PATCH 3/4] Add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a5842b0a..5ebfb096c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- `Function.length` is supported now + ## 0.34.0 - Added new `"luaTarget"` option value - `"universal"`. Choosing this target makes TypeScriptToLua generate code compatible with all supported Lua targets. From 6801f1b38baba97984fadb026541b64fee7507ba Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 1 Jun 2020 01:56:50 +0000 Subject: [PATCH 4/4] Remove outdated snapshots --- .../__snapshots__/functions.spec.ts.snap | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/test/unit/functions/__snapshots__/functions.spec.ts.snap b/test/unit/functions/__snapshots__/functions.spec.ts.snap index a011675a4..345e31731 100644 --- a/test/unit/functions/__snapshots__/functions.spec.ts.snap +++ b/test/unit/functions/__snapshots__/functions.spec.ts.snap @@ -24,30 +24,6 @@ return ____exports" exports[`function.length unsupported ("universal"): diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; -exports[`function.length unsupported on 5.1: code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function fn(self) - end - return debug.getinfo(fn).nparams - 1 -end -return ____exports" -`; - -exports[`function.length unsupported on 5.1: diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; - -exports[`function.length unsupported on universal: code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function fn(self) - end - return debug.getinfo(fn).nparams - 1 -end -return ____exports" -`; - -exports[`function.length unsupported on universal: diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; - exports[`missing declaration name: code 1`] = ` "function ____(self) end"