typescript-to-lua 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/LuaLib.js +1 -1
  3. package/dist/LuaPrinter.js +4 -4
  4. package/dist/lualib/Await.lua +7 -3
  5. package/dist/lualib/CloneDescriptor.lua +6 -6
  6. package/dist/lualib/Promise.lua +10 -2
  7. package/dist/lualib/lualib_bundle.lua +217 -205
  8. package/dist/transformation/builtins/function.js +5 -2
  9. package/dist/transformation/builtins/index.js +2 -2
  10. package/dist/transformation/utils/diagnostics.d.ts +3 -0
  11. package/dist/transformation/utils/diagnostics.js +2 -1
  12. package/dist/transformation/utils/language-extensions.d.ts +2 -0
  13. package/dist/transformation/utils/language-extensions.js +7 -1
  14. package/dist/transformation/utils/lua-ast.js +5 -5
  15. package/dist/transformation/utils/safe-names.d.ts +4 -2
  16. package/dist/transformation/utils/safe-names.js +11 -5
  17. package/dist/transformation/utils/scope.d.ts +2 -1
  18. package/dist/transformation/utils/scope.js +1 -0
  19. package/dist/transformation/utils/typescript/types.d.ts +3 -1
  20. package/dist/transformation/utils/typescript/types.js +40 -13
  21. package/dist/transformation/visitors/binary-expression/index.js +1 -3
  22. package/dist/transformation/visitors/call.js +1 -1
  23. package/dist/transformation/visitors/class/index.js +1 -1
  24. package/dist/transformation/visitors/conditional.js +2 -23
  25. package/dist/transformation/visitors/errors.js +11 -9
  26. package/dist/transformation/visitors/function.d.ts +2 -0
  27. package/dist/transformation/visitors/function.js +53 -4
  28. package/dist/transformation/visitors/language-extensions/pairsIterable.d.ts +5 -0
  29. package/dist/transformation/visitors/language-extensions/pairsIterable.js +53 -0
  30. package/dist/transformation/visitors/loops/for-of.js +4 -0
  31. package/dist/transformation/visitors/loops/utils.js +6 -1
  32. package/dist/transformation/visitors/namespace.js +1 -1
  33. package/dist/transformation/visitors/spread.js +14 -7
  34. package/dist/transformation/visitors/variable-declaration.js +10 -1
  35. package/language-extensions/index.d.ts +11 -1
  36. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isExtensionValue = exports.isExtensionType = exports.ExtensionKind = void 0;
3
+ exports.isExtensionValue = exports.getExtensionKinds = exports.isExtensionType = exports.ExtensionKind = void 0;
4
4
  var ExtensionKind;
5
5
  (function (ExtensionKind) {
6
6
  ExtensionKind["MultiFunction"] = "MultiFunction";
@@ -8,6 +8,7 @@ var ExtensionKind;
8
8
  ExtensionKind["RangeFunction"] = "RangeFunction";
9
9
  ExtensionKind["VarargConstant"] = "VarargConstant";
10
10
  ExtensionKind["IterableType"] = "IterableType";
11
+ ExtensionKind["PairsIterableType"] = "PairsIterableType";
11
12
  ExtensionKind["AdditionOperatorType"] = "AdditionOperatorType";
12
13
  ExtensionKind["AdditionOperatorMethodType"] = "AdditionOperatorMethodType";
13
14
  ExtensionKind["SubtractionOperatorType"] = "SubtractionOperatorType";
@@ -65,6 +66,7 @@ const extensionKindToTypeBrand = {
65
66
  [ExtensionKind.RangeFunction]: "__luaRangeFunctionBrand",
66
67
  [ExtensionKind.VarargConstant]: "__luaVarargConstantBrand",
67
68
  [ExtensionKind.IterableType]: "__luaIterableBrand",
69
+ [ExtensionKind.PairsIterableType]: "__luaPairsIterableBrand",
68
70
  [ExtensionKind.AdditionOperatorType]: "__luaAdditionBrand",
69
71
  [ExtensionKind.AdditionOperatorMethodType]: "__luaAdditionMethodBrand",
70
72
  [ExtensionKind.SubtractionOperatorType]: "__luaSubtractionBrand",
@@ -116,6 +118,10 @@ function isExtensionType(type, extensionKind) {
116
118
  return typeBrand !== undefined && type.getProperty(typeBrand) !== undefined;
117
119
  }
118
120
  exports.isExtensionType = isExtensionType;
121
+ function getExtensionKinds(type) {
122
+ return Object.keys(extensionKindToTypeBrand).filter(e => type.getProperty(extensionKindToTypeBrand[e]) !== undefined);
123
+ }
124
+ exports.getExtensionKinds = getExtensionKinds;
119
125
  function isExtensionValue(context, symbol, extensionKind) {
120
126
  var _a;
121
127
  return (symbol.getName() === extensionKindToValueName[extensionKind] &&
@@ -119,7 +119,9 @@ function createLocalOrExportedOrGlobalDeclaration(context, lhs, rhs, tsOriginal,
119
119
  const scope = (0, scope_1.peekScope)(context);
120
120
  const isTopLevelVariable = scope.type === scope_1.ScopeType.File;
121
121
  if (context.isModule || !isTopLevelVariable) {
122
- if (!isFunctionDeclaration && hasMultipleReferences(scope, lhs)) {
122
+ const isLuaFunctionExpression = rhs && !Array.isArray(rhs) && lua.isFunctionExpression(rhs);
123
+ const isSafeRecursiveFunctionDeclaration = isFunctionDeclaration && isLuaFunctionExpression;
124
+ if (!isSafeRecursiveFunctionDeclaration && hasMultipleReferences(scope, lhs)) {
123
125
  // Split declaration and assignment of identifiers that reference themselves in their declaration.
124
126
  // Put declaration above preceding statements in case the identifier is referenced in those.
125
127
  const precedingDeclaration = lua.createVariableDeclarationStatement(lhs, undefined, tsOriginal);
@@ -127,10 +129,8 @@ function createLocalOrExportedOrGlobalDeclaration(context, lhs, rhs, tsOriginal,
127
129
  if (rhs) {
128
130
  assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal);
129
131
  }
130
- if (!isFunctionDeclaration) {
131
- // Remember local variable declarations for hoisting later
132
- (0, scope_1.addScopeVariableDeclaration)(scope, precedingDeclaration);
133
- }
132
+ // Remember local variable declarations for hoisting later
133
+ (0, scope_1.addScopeVariableDeclaration)(scope, precedingDeclaration);
134
134
  }
135
135
  else {
136
136
  declaration = lua.createVariableDeclarationStatement(lhs, rhs, tsOriginal);
@@ -1,8 +1,10 @@
1
1
  import * as ts from "typescript";
2
+ import { CompilerOptions } from "../..";
2
3
  import { TransformationContext } from "../context";
3
- export declare const isValidLuaIdentifier: (name: string) => boolean;
4
+ export declare const shouldAllowUnicode: (options: CompilerOptions) => boolean;
5
+ export declare const isValidLuaIdentifier: (name: string, options: CompilerOptions) => boolean;
4
6
  export declare const luaKeywords: ReadonlySet<string>;
5
- export declare const isUnsafeName: (name: string) => boolean;
7
+ export declare const isUnsafeName: (name: string, options: CompilerOptions) => boolean;
6
8
  export declare function hasUnsafeSymbolName(context: TransformationContext, symbol: ts.Symbol, tsOriginal: ts.Identifier): boolean;
7
9
  export declare function hasUnsafeIdentifierName(context: TransformationContext, identifier: ts.Identifier, checkSymbol?: boolean): boolean;
8
10
  export declare const createSafeName: (name: string) => string;
@@ -1,10 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSafeName = exports.hasUnsafeIdentifierName = exports.hasUnsafeSymbolName = exports.isUnsafeName = exports.luaKeywords = exports.isValidLuaIdentifier = void 0;
3
+ exports.createSafeName = exports.hasUnsafeIdentifierName = exports.hasUnsafeSymbolName = exports.isUnsafeName = exports.luaKeywords = exports.isValidLuaIdentifier = exports.shouldAllowUnicode = void 0;
4
+ const __1 = require("../..");
4
5
  const diagnostics_1 = require("./diagnostics");
5
6
  const export_1 = require("./export");
6
7
  const typescript_1 = require("./typescript");
7
- const isValidLuaIdentifier = (name) => !exports.luaKeywords.has(name) && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name);
8
+ const shouldAllowUnicode = (options) => options.luaTarget === __1.LuaTarget.LuaJIT;
9
+ exports.shouldAllowUnicode = shouldAllowUnicode;
10
+ const isValidLuaIdentifier = (name, options) => !exports.luaKeywords.has(name) &&
11
+ ((0, exports.shouldAllowUnicode)(options)
12
+ ? /^[a-zA-Z_\u007F-\uFFFD][a-zA-Z0-9_\u007F-\uFFFD]*$/
13
+ : /^[a-zA-Z_][a-zA-Z0-9_]*$/).test(name);
8
14
  exports.isValidLuaIdentifier = isValidLuaIdentifier;
9
15
  exports.luaKeywords = new Set([
10
16
  "and",
@@ -51,10 +57,10 @@ const luaBuiltins = new Set([
51
57
  "type",
52
58
  "unpack",
53
59
  ]);
54
- const isUnsafeName = (name) => !(0, exports.isValidLuaIdentifier)(name) || luaBuiltins.has(name);
60
+ const isUnsafeName = (name, options) => !(0, exports.isValidLuaIdentifier)(name, options) || luaBuiltins.has(name);
55
61
  exports.isUnsafeName = isUnsafeName;
56
62
  function checkName(context, name, node) {
57
- const isInvalid = !(0, exports.isValidLuaIdentifier)(name);
63
+ const isInvalid = !(0, exports.isValidLuaIdentifier)(name, context.options);
58
64
  if (isInvalid) {
59
65
  // Empty identifier is a TypeScript error
60
66
  if (name !== "") {
@@ -71,7 +77,7 @@ function hasUnsafeSymbolName(context, symbol, tsOriginal) {
71
77
  return true;
72
78
  }
73
79
  // only unsafe when non-ambient and not exported
74
- return (0, exports.isUnsafeName)(symbol.name) && !isAmbient && !(0, export_1.isSymbolExported)(context, symbol);
80
+ return (0, exports.isUnsafeName)(symbol.name, context.options) && !isAmbient && !(0, export_1.isSymbolExported)(context, symbol);
75
81
  }
76
82
  exports.hasUnsafeSymbolName = hasUnsafeSymbolName;
77
83
  function hasUnsafeIdentifierName(context, identifier, checkSymbol = true) {
@@ -9,7 +9,8 @@ export declare enum ScopeType {
9
9
  Conditional = 16,
10
10
  Block = 32,
11
11
  Try = 64,
12
- Catch = 128
12
+ Catch = 128,
13
+ LoopInitializer = 256
13
14
  }
14
15
  interface FunctionDefinitionInfo {
15
16
  referencedSymbols: Map<lua.SymbolId, ts.Node[]>;
@@ -16,6 +16,7 @@ var ScopeType;
16
16
  ScopeType[ScopeType["Block"] = 32] = "Block";
17
17
  ScopeType[ScopeType["Try"] = 64] = "Try";
18
18
  ScopeType[ScopeType["Catch"] = 128] = "Catch";
19
+ ScopeType[ScopeType["LoopInitializer"] = 256] = "LoopInitializer";
19
20
  })(ScopeType = exports.ScopeType || (exports.ScopeType = {}));
20
21
  const scopeStacks = new WeakMap();
21
22
  function getScopeStack(context) {
@@ -10,4 +10,6 @@ export declare function isNumberType(context: TransformationContext, type: ts.Ty
10
10
  */
11
11
  export declare function forTypeOrAnySupertype(context: TransformationContext, type: ts.Type, predicate: (type: ts.Type) => boolean): boolean;
12
12
  export declare function isArrayType(context: TransformationContext, type: ts.Type): boolean;
13
- export declare function isFunctionType(context: TransformationContext, type: ts.Type): boolean;
13
+ export declare function isFunctionType(type: ts.Type): boolean;
14
+ export declare function canBeFalsy(context: TransformationContext, type: ts.Type): boolean;
15
+ export declare function canBeFalsyWhenNotNull(context: TransformationContext, type: ts.Type): boolean;
@@ -1,21 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isFunctionType = exports.isArrayType = exports.forTypeOrAnySupertype = exports.isNumberType = exports.isStringType = exports.typeCanSatisfy = exports.typeAlwaysSatisfies = exports.isTypeWithFlags = void 0;
3
+ exports.canBeFalsyWhenNotNull = exports.canBeFalsy = exports.isFunctionType = exports.isArrayType = exports.forTypeOrAnySupertype = exports.isNumberType = exports.isStringType = exports.typeCanSatisfy = exports.typeAlwaysSatisfies = exports.isTypeWithFlags = void 0;
4
4
  const ts = require("typescript");
5
5
  function isTypeWithFlags(context, type, flags) {
6
- const predicate = (type) => {
7
- if (type.symbol) {
8
- const baseConstraint = context.checker.getBaseConstraintOfType(type);
9
- if (baseConstraint && baseConstraint !== type) {
10
- return isTypeWithFlags(context, baseConstraint, flags);
11
- }
12
- }
13
- return (type.flags & flags) !== 0;
14
- };
6
+ const predicate = (type) => (type.flags & flags) !== 0;
15
7
  return typeAlwaysSatisfies(context, type, predicate);
16
8
  }
17
9
  exports.isTypeWithFlags = isTypeWithFlags;
18
10
  function typeAlwaysSatisfies(context, type, predicate) {
11
+ const baseConstraint = context.checker.getBaseConstraintOfType(type);
12
+ if (baseConstraint) {
13
+ type = baseConstraint;
14
+ }
19
15
  if (predicate(type)) {
20
16
  return true;
21
17
  }
@@ -29,6 +25,15 @@ function typeAlwaysSatisfies(context, type, predicate) {
29
25
  }
30
26
  exports.typeAlwaysSatisfies = typeAlwaysSatisfies;
31
27
  function typeCanSatisfy(context, type, predicate) {
28
+ const baseConstraint = context.checker.getBaseConstraintOfType(type);
29
+ if (!baseConstraint) {
30
+ // type parameter with no constraint can be anything, assume it might satisfy predicate
31
+ if (type.isTypeParameter())
32
+ return true;
33
+ }
34
+ else {
35
+ type = baseConstraint;
36
+ }
32
37
  if (predicate(type)) {
33
38
  return true;
34
39
  }
@@ -84,9 +89,31 @@ function isArrayType(context, type) {
84
89
  return forTypeOrAnySupertype(context, type, t => isExplicitArrayType(context, t));
85
90
  }
86
91
  exports.isArrayType = isArrayType;
87
- function isFunctionType(context, type) {
88
- const typeNode = context.checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias);
89
- return typeNode !== undefined && ts.isFunctionTypeNode(typeNode);
92
+ function isFunctionType(type) {
93
+ return type.getCallSignatures().length > 0;
90
94
  }
91
95
  exports.isFunctionType = isFunctionType;
96
+ function canBeFalsy(context, type) {
97
+ const strictNullChecks = context.options.strict === true || context.options.strictNullChecks === true;
98
+ const falsyFlags = ts.TypeFlags.Boolean |
99
+ ts.TypeFlags.BooleanLiteral |
100
+ ts.TypeFlags.Never |
101
+ ts.TypeFlags.Void |
102
+ ts.TypeFlags.Unknown |
103
+ ts.TypeFlags.Any |
104
+ ts.TypeFlags.Undefined |
105
+ ts.TypeFlags.Null;
106
+ return typeCanSatisfy(context, type, type => (type.flags & falsyFlags) !== 0 || (!strictNullChecks && !type.isLiteral()));
107
+ }
108
+ exports.canBeFalsy = canBeFalsy;
109
+ function canBeFalsyWhenNotNull(context, type) {
110
+ const falsyFlags = ts.TypeFlags.Boolean |
111
+ ts.TypeFlags.BooleanLiteral |
112
+ ts.TypeFlags.Never |
113
+ ts.TypeFlags.Void |
114
+ ts.TypeFlags.Unknown |
115
+ ts.TypeFlags.Any;
116
+ return typeCanSatisfy(context, type, type => (type.flags & falsyFlags) !== 0);
117
+ }
118
+ exports.canBeFalsyWhenNotNull = canBeFalsyWhenNotNull;
92
119
  //# sourceMappingURL=types.js.map
@@ -167,9 +167,7 @@ exports.transformBinaryExpressionStatement = transformBinaryExpressionStatement;
167
167
  function transformNullishCoalescingOperationNoPrecedingStatements(context, node, transformedLeft, transformedRight) {
168
168
  const lhsType = context.checker.getTypeAtLocation(node.left);
169
169
  // Check if we can take a shortcut to 'lhs or rhs' if the left-hand side cannot be 'false'.
170
- const typeCanBeFalse = (type) => (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Boolean)) !== 0 ||
171
- (type.flags & ts.TypeFlags.BooleanLiteral & ts.TypeFlags.PossiblyFalsy) !== 0;
172
- if ((0, typescript_1.typeCanSatisfy)(context, lhsType, typeCanBeFalse)) {
170
+ if ((0, typescript_1.canBeFalsyWhenNotNull)(context, lhsType)) {
173
171
  // reuse logic from case with preceding statements
174
172
  const [precedingStatements, result] = createShortCircuitBinaryExpressionPrecedingStatements(context, transformedLeft, transformedRight, [], ts.SyntaxKind.QuestionQuestionToken, node);
175
173
  context.addPrecedingStatements(precedingStatements);
@@ -86,7 +86,7 @@ function transformContextualCallExpression(context, node, args, signature) {
86
86
  let [argPrecedingStatements, transformedArguments] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => transformArguments(context, args, signature));
87
87
  if (ts.isPropertyAccessExpression(left) &&
88
88
  ts.isIdentifier(left.name) &&
89
- (0, safe_names_1.isValidLuaIdentifier)(left.name.text) &&
89
+ (0, safe_names_1.isValidLuaIdentifier)(left.name.text, context.options) &&
90
90
  argPrecedingStatements.length === 0) {
91
91
  // table:name()
92
92
  const table = context.transformExpression(left.expression);
@@ -70,7 +70,7 @@ function transformClassLikeDeclaration(classDeclaration, context, nameOverride)
70
70
  const instanceFields = properties.filter(prop => !(0, utils_2.isStaticNode)(prop));
71
71
  const result = [];
72
72
  let localClassName;
73
- if ((0, safe_names_1.isUnsafeName)(className.text)) {
73
+ if ((0, safe_names_1.isUnsafeName)(className.text, context.options)) {
74
74
  localClassName = lua.createIdentifier((0, safe_names_1.createSafeName)(className.text), undefined, className.symbolId, className.text);
75
75
  lua.setNodePosition(localClassName, className);
76
76
  }
@@ -6,28 +6,7 @@ const lua = require("../../LuaAST");
6
6
  const preceding_statements_1 = require("../utils/preceding-statements");
7
7
  const scope_1 = require("../utils/scope");
8
8
  const block_1 = require("./block");
9
- function canBeFalsy(context, type) {
10
- const strictNullChecks = context.options.strict === true || context.options.strictNullChecks === true;
11
- const falsyFlags = ts.TypeFlags.Boolean |
12
- ts.TypeFlags.BooleanLiteral |
13
- ts.TypeFlags.Undefined |
14
- ts.TypeFlags.Null |
15
- ts.TypeFlags.Never |
16
- ts.TypeFlags.Void |
17
- ts.TypeFlags.Any;
18
- if (type.flags & falsyFlags) {
19
- return true;
20
- }
21
- else if (!strictNullChecks && !type.isLiteral()) {
22
- return true;
23
- }
24
- else if (type.isUnion()) {
25
- return type.types.some(subType => canBeFalsy(context, subType));
26
- }
27
- else {
28
- return false;
29
- }
30
- }
9
+ const typescript_1 = require("../utils/typescript");
31
10
  function transformProtectedConditionalExpression(context, expression) {
32
11
  const tempVar = context.createTempNameForNode(expression.condition);
33
12
  const condition = context.transformExpression(expression.condition);
@@ -42,7 +21,7 @@ function transformProtectedConditionalExpression(context, expression) {
42
21
  return lua.cloneIdentifier(tempVar);
43
22
  }
44
23
  const transformConditionalExpression = (expression, context) => {
45
- if (canBeFalsy(context, context.checker.getTypeAtLocation(expression.whenTrue))) {
24
+ if ((0, typescript_1.canBeFalsy)(context, context.checker.getTypeAtLocation(expression.whenTrue))) {
46
25
  return transformProtectedConditionalExpression(context, expression);
47
26
  }
48
27
  const condition = context.transformExpression(expression.condition);
@@ -12,6 +12,7 @@ const identifier_1 = require("./identifier");
12
12
  const multi_1 = require("./language-extensions/multi");
13
13
  const return_1 = require("./return");
14
14
  const transformTryStatement = (statement, context) => {
15
+ var _a;
15
16
  const [tryBlock, tryScope] = (0, block_1.transformScopeBlock)(context, statement.tryBlock, scope_1.ScopeType.Try);
16
17
  if (context.options.luaTarget === __1.LuaTarget.Lua51 && (0, typescript_1.isInAsyncFunction)(statement)) {
17
18
  context.diagnostics.push((0, diagnostics_1.unsupportedForTarget)(statement, "try/catch inside async functions", __1.LuaTarget.Lua51));
@@ -34,24 +35,25 @@ const transformTryStatement = (statement, context) => {
34
35
  const catchParameter = statement.catchClause.variableDeclaration
35
36
  ? (0, identifier_1.transformIdentifier)(context, statement.catchClause.variableDeclaration.name)
36
37
  : undefined;
37
- const catchParameters = () => (catchParameter ? [lua.cloneIdentifier(catchParameter)] : []);
38
+ const catchFunction = lua.createFunctionExpression(catchBlock, catchParameter ? [lua.cloneIdentifier(catchParameter)] : []);
38
39
  const catchIdentifier = lua.createIdentifier("____catch");
39
- const catchFunction = lua.createFunctionExpression(catchBlock, catchParameters());
40
40
  result.push(lua.createVariableDeclarationStatement(catchIdentifier, catchFunction));
41
+ const hasReturn = (_a = tryScope.functionReturned) !== null && _a !== void 0 ? _a : catchScope.functionReturned;
41
42
  const tryReturnIdentifiers = [tryResultIdentifier]; // ____try
42
- if (returnedIdentifier) {
43
- tryReturnIdentifiers.push(returnedIdentifier); // ____returned or catch variable
44
- if (tryScope.functionReturned || catchScope.functionReturned) {
43
+ if (hasReturn || statement.catchClause.variableDeclaration) {
44
+ tryReturnIdentifiers.push(returnedIdentifier); // ____returned
45
+ if (hasReturn) {
45
46
  tryReturnIdentifiers.push(returnValueIdentifier); // ____returnValue
46
47
  returnCondition = lua.cloneIdentifier(returnedIdentifier);
47
48
  }
48
49
  }
49
50
  result.push(lua.createVariableDeclarationStatement(tryReturnIdentifiers, tryCall));
50
- // Wrap catch in function if try or catch has return
51
- const catchCall = lua.createCallExpression(catchIdentifier, [lua.cloneIdentifier(returnedIdentifier)]);
52
- const catchAssign = lua.createAssignmentStatement([lua.cloneIdentifier(returnedIdentifier), lua.cloneIdentifier(returnValueIdentifier)], catchCall);
51
+ const catchCall = lua.createCallExpression(catchIdentifier, statement.catchClause.variableDeclaration ? [lua.cloneIdentifier(returnedIdentifier)] : []);
52
+ const catchCallStatement = hasReturn
53
+ ? lua.createAssignmentStatement([lua.cloneIdentifier(returnedIdentifier), lua.cloneIdentifier(returnValueIdentifier)], catchCall)
54
+ : lua.createExpressionStatement(catchCall);
53
55
  const notTryCondition = lua.createUnaryExpression(tryResultIdentifier, lua.SyntaxKind.NotOperator);
54
- result.push(lua.createIfStatement(notTryCondition, lua.createBlock([catchAssign])));
56
+ result.push(lua.createIfStatement(notTryCondition, lua.createBlock([catchCallStatement])));
55
57
  }
56
58
  else if (tryScope.functionReturned) {
57
59
  // try with return, but no catch
@@ -2,6 +2,8 @@ import * as ts from "typescript";
2
2
  import * as lua from "../../LuaAST";
3
3
  import { FunctionVisitor, TransformationContext } from "../context";
4
4
  import { Scope } from "../utils/scope";
5
+ export declare function createCallableTable(functionExpression: lua.Expression): lua.Expression;
6
+ export declare function isFunctionTypeWithProperties(functionType: ts.Type): boolean;
5
7
  export declare function transformFunctionBodyContent(context: TransformationContext, body: ts.ConciseBody): lua.Statement[];
6
8
  export declare function transformFunctionBodyHeader(context: TransformationContext, bodyScope: Scope, parameters: ts.NodeArray<ts.ParameterDeclaration>, spreadIdentifier?: lua.Identifier): lua.Statement[];
7
9
  export declare function transformFunctionBody(context: TransformationContext, parameters: ts.NodeArray<ts.ParameterDeclaration>, body: ts.ConciseBody, spreadIdentifier?: lua.Identifier, node?: ts.FunctionLikeDeclaration): [lua.Statement[], Scope];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformYieldExpression = exports.transformFunctionDeclaration = exports.transformFunctionLikeDeclaration = exports.transformFunctionToExpression = exports.transformParameters = exports.transformFunctionBody = exports.transformFunctionBodyHeader = exports.transformFunctionBodyContent = void 0;
3
+ exports.transformYieldExpression = exports.transformFunctionDeclaration = exports.transformFunctionLikeDeclaration = exports.transformFunctionToExpression = exports.transformParameters = exports.transformFunctionBody = exports.transformFunctionBodyHeader = exports.transformFunctionBodyContent = exports.isFunctionTypeWithProperties = exports.createCallableTable = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../LuaAST");
6
6
  const utils_1 = require("../../utils");
@@ -8,10 +8,12 @@ const annotations_1 = require("../utils/annotations");
8
8
  const diagnostics_1 = require("../utils/diagnostics");
9
9
  const export_1 = require("../utils/export");
10
10
  const function_context_1 = require("../utils/function-context");
11
+ const language_extensions_1 = require("../utils/language-extensions");
11
12
  const lua_ast_1 = require("../utils/lua-ast");
12
13
  const lualib_1 = require("../utils/lualib");
13
14
  const preceding_statements_1 = require("../utils/preceding-statements");
14
15
  const scope_1 = require("../utils/scope");
16
+ const typescript_1 = require("../utils/typescript");
15
17
  const async_await_1 = require("./async-await");
16
18
  const identifier_1 = require("./identifier");
17
19
  const return_1 = require("./return");
@@ -33,6 +35,39 @@ function isRestParameterReferenced(identifier, scope) {
33
35
  const references = scope.referencedSymbols.get(identifier.symbolId);
34
36
  return references !== undefined && references.length > 0;
35
37
  }
38
+ function createCallableTable(functionExpression) {
39
+ var _a;
40
+ // __call metamethod receives the table as the first argument, so we need to add a dummy parameter
41
+ if (lua.isFunctionExpression(functionExpression)) {
42
+ (_a = functionExpression.params) === null || _a === void 0 ? void 0 : _a.unshift(lua.createAnonymousIdentifier());
43
+ }
44
+ else {
45
+ // functionExpression may have been replaced (lib functions, etc...),
46
+ // so we create a forwarding function to eat the extra argument
47
+ functionExpression = lua.createFunctionExpression(lua.createBlock([
48
+ lua.createReturnStatement([lua.createCallExpression(functionExpression, [lua.createDotsLiteral()])]),
49
+ ]), [lua.createAnonymousIdentifier()], lua.createDotsLiteral(), lua.FunctionExpressionFlags.Inline);
50
+ }
51
+ return lua.createCallExpression(lua.createIdentifier("setmetatable"), [
52
+ lua.createTableExpression(),
53
+ lua.createTableExpression([
54
+ lua.createTableFieldExpression(functionExpression, lua.createStringLiteral("__call")),
55
+ ]),
56
+ ]);
57
+ }
58
+ exports.createCallableTable = createCallableTable;
59
+ function isFunctionTypeWithProperties(functionType) {
60
+ if (functionType.isUnion()) {
61
+ return functionType.types.some(isFunctionTypeWithProperties);
62
+ }
63
+ else {
64
+ return ((0, typescript_1.isFunctionType)(functionType) &&
65
+ functionType.getProperties().length > 0 &&
66
+ (0, language_extensions_1.getExtensionKinds)(functionType).length === 0 // ignore TSTL extension functions like $range
67
+ );
68
+ }
69
+ }
70
+ exports.isFunctionTypeWithProperties = isFunctionTypeWithProperties;
36
71
  function transformFunctionBodyContent(context, body) {
37
72
  if (!ts.isBlock(body)) {
38
73
  const [precedingStatements, returnStatement] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, return_1.transformExpressionBodyToReturnStatement)(context, body));
@@ -55,7 +90,9 @@ function transformFunctionBodyHeader(context, bodyScope, parameters, spreadIdent
55
90
  headerStatements.push(transformParameterDefaultValueDeclaration(context, identifier, declaration.initializer));
56
91
  }
57
92
  // Binding pattern
58
- bindingPatternDeclarations.push(...(0, variable_declaration_1.transformBindingPattern)(context, declaration.name, identifier));
93
+ const name = declaration.name;
94
+ const [precedingStatements, bindings] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, variable_declaration_1.transformBindingPattern)(context, name, identifier));
95
+ bindingPatternDeclarations.push(...precedingStatements, ...bindings);
59
96
  }
60
97
  else if (declaration.initializer !== undefined) {
61
98
  // Default parameter
@@ -167,7 +204,15 @@ function transformFunctionLikeDeclaration(node, context) {
167
204
  // Only handle if the name is actually referenced inside the function
168
205
  if (isReferenced) {
169
206
  const nameIdentifier = (0, identifier_1.transformIdentifier)(context, node.name);
170
- context.addPrecedingStatements(lua.createVariableDeclarationStatement(nameIdentifier, functionExpression));
207
+ if (isFunctionTypeWithProperties(context.checker.getTypeAtLocation(node))) {
208
+ context.addPrecedingStatements([
209
+ lua.createVariableDeclarationStatement(nameIdentifier),
210
+ lua.createAssignmentStatement(nameIdentifier, createCallableTable(functionExpression)),
211
+ ]);
212
+ }
213
+ else {
214
+ context.addPrecedingStatements(lua.createVariableDeclarationStatement(nameIdentifier, functionExpression));
215
+ }
171
216
  return lua.cloneIdentifier(nameIdentifier);
172
217
  }
173
218
  }
@@ -199,7 +244,11 @@ const transformFunctionDeclaration = (node, context) => {
199
244
  const functionInfo = { referencedSymbols: (_a = functionScope.referencedSymbols) !== null && _a !== void 0 ? _a : new Map() };
200
245
  scope.functionDefinitions.set(name.symbolId, functionInfo);
201
246
  }
202
- return (0, lua_ast_1.createLocalOrExportedOrGlobalDeclaration)(context, name, functionExpression, node);
247
+ // Wrap functions with properties into a callable table
248
+ const wrappedFunction = node.name && isFunctionTypeWithProperties(context.checker.getTypeAtLocation(node.name))
249
+ ? createCallableTable(functionExpression)
250
+ : functionExpression;
251
+ return (0, lua_ast_1.createLocalOrExportedOrGlobalDeclaration)(context, name, wrappedFunction, node);
203
252
  };
204
253
  exports.transformFunctionDeclaration = transformFunctionDeclaration;
205
254
  const transformYieldExpression = (expression, context) => {
@@ -0,0 +1,5 @@
1
+ import * as ts from "typescript";
2
+ import * as lua from "../../../LuaAST";
3
+ import { TransformationContext } from "../../context";
4
+ export declare function isPairsIterableExpression(context: TransformationContext, expression: ts.Expression): boolean;
5
+ export declare function transformForOfPairsIterableStatement(context: TransformationContext, statement: ts.ForOfStatement, block: lua.Block): lua.Statement;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformForOfPairsIterableStatement = exports.isPairsIterableExpression = void 0;
4
+ const ts = require("typescript");
5
+ const lua = require("../../../LuaAST");
6
+ const utils_1 = require("../../../utils");
7
+ const diagnostics_1 = require("../../utils/diagnostics");
8
+ const extensions = require("../../utils/language-extensions");
9
+ const utils_2 = require("../loops/utils");
10
+ const variable_declaration_1 = require("../variable-declaration");
11
+ function isPairsIterableType(type) {
12
+ return extensions.isExtensionType(type, extensions.ExtensionKind.PairsIterableType);
13
+ }
14
+ function isPairsIterableExpression(context, expression) {
15
+ const type = context.checker.getTypeAtLocation(expression);
16
+ return isPairsIterableType(type);
17
+ }
18
+ exports.isPairsIterableExpression = isPairsIterableExpression;
19
+ function transformForOfPairsIterableStatement(context, statement, block) {
20
+ const pairsCall = lua.createCallExpression(lua.createIdentifier("pairs"), [
21
+ context.transformExpression(statement.expression),
22
+ ]);
23
+ let identifiers = [];
24
+ if (ts.isVariableDeclarationList(statement.initializer)) {
25
+ // Variables declared in for loop
26
+ // for key, value in iterable do
27
+ const binding = (0, utils_2.getVariableDeclarationBinding)(context, statement.initializer);
28
+ if (ts.isArrayBindingPattern(binding)) {
29
+ identifiers = binding.elements.map(e => (0, variable_declaration_1.transformArrayBindingElement)(context, e));
30
+ }
31
+ else {
32
+ context.diagnostics.push((0, diagnostics_1.invalidPairsIterableWithoutDestructuring)(binding));
33
+ }
34
+ }
35
+ else if (ts.isArrayLiteralExpression(statement.initializer)) {
36
+ // Variables NOT declared in for loop - catch iterator values in temps and assign
37
+ // for ____key, ____value in iterable do
38
+ // key, value = ____key, ____value
39
+ identifiers = statement.initializer.elements.map(e => context.createTempNameForNode(e));
40
+ if (identifiers.length > 0) {
41
+ block.statements.unshift(lua.createAssignmentStatement(statement.initializer.elements.map(e => (0, utils_1.cast)(context.transformExpression(e), lua.isAssignmentLeftHandSideExpression)), identifiers));
42
+ }
43
+ }
44
+ else {
45
+ context.diagnostics.push((0, diagnostics_1.invalidPairsIterableWithoutDestructuring)(statement.initializer));
46
+ }
47
+ if (identifiers.length === 0) {
48
+ identifiers.push(lua.createAnonymousIdentifier());
49
+ }
50
+ return lua.createForInStatement(block, identifiers, [pairsCall], statement);
51
+ }
52
+ exports.transformForOfPairsIterableStatement = transformForOfPairsIterableStatement;
53
+ //# sourceMappingURL=pairsIterable.js.map
@@ -8,6 +8,7 @@ const diagnostics_1 = require("../../utils/diagnostics");
8
8
  const lualib_1 = require("../../utils/lualib");
9
9
  const typescript_1 = require("../../utils/typescript");
10
10
  const iterable_1 = require("../language-extensions/iterable");
11
+ const pairsIterable_1 = require("../language-extensions/pairsIterable");
11
12
  const range_1 = require("../language-extensions/range");
12
13
  const utils_1 = require("./utils");
13
14
  function transformForOfArrayStatement(context, statement, block) {
@@ -33,6 +34,9 @@ const transformForOfStatement = (node, context) => {
33
34
  else if ((0, iterable_1.isIterableExpression)(context, node.expression)) {
34
35
  return (0, iterable_1.transformForOfIterableStatement)(context, node, body);
35
36
  }
37
+ else if ((0, pairsIterable_1.isPairsIterableExpression)(context, node.expression)) {
38
+ return (0, pairsIterable_1.transformForOfPairsIterableStatement)(context, node, body);
39
+ }
36
40
  else if ((0, annotations_1.isLuaIteratorType)(context, node.expression)) {
37
41
  context.diagnostics.push((0, diagnostics_1.annotationRemoved)(node.expression, annotations_1.AnnotationKind.LuaIterator));
38
42
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.invertCondition = exports.transformForInitializer = exports.getVariableDeclarationBinding = exports.transformLoopBody = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../../LuaAST");
6
+ const preceding_statements_1 = require("../../utils/preceding-statements");
6
7
  const scope_1 = require("../../utils/scope");
7
8
  const typescript_1 = require("../../utils/typescript");
8
9
  const assignments_1 = require("../binary-expression/assignments");
@@ -34,14 +35,17 @@ function getVariableDeclarationBinding(context, node) {
34
35
  exports.getVariableDeclarationBinding = getVariableDeclarationBinding;
35
36
  function transformForInitializer(context, initializer, block) {
36
37
  const valueVariable = lua.createIdentifier("____value");
38
+ (0, scope_1.pushScope)(context, scope_1.ScopeType.LoopInitializer);
37
39
  if (ts.isVariableDeclarationList(initializer)) {
38
40
  // Declaration of new variable
39
41
  const binding = getVariableDeclarationBinding(context, initializer);
40
42
  if (ts.isArrayBindingPattern(binding) || ts.isObjectBindingPattern(binding)) {
41
- block.statements.unshift(...(0, variable_declaration_1.transformBindingPattern)(context, binding, valueVariable));
43
+ const [precedingStatements, bindings] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, variable_declaration_1.transformBindingPattern)(context, binding, valueVariable));
44
+ block.statements.unshift(...precedingStatements, ...bindings);
42
45
  }
43
46
  else {
44
47
  // Single variable declared in for loop
48
+ (0, scope_1.popScope)(context);
45
49
  return (0, identifier_1.transformIdentifier)(context, binding);
46
50
  }
47
51
  }
@@ -51,6 +55,7 @@ function transformForInitializer(context, initializer, block) {
51
55
  ? (0, destructuring_assignments_1.transformAssignmentPattern)(context, initializer, valueVariable, false)
52
56
  : (0, assignments_1.transformAssignment)(context, initializer, valueVariable)));
53
57
  }
58
+ (0, scope_1.popScope)(context);
54
59
  return valueVariable;
55
60
  }
56
61
  exports.transformForInitializer = transformForInitializer;
@@ -13,7 +13,7 @@ const symbols_1 = require("../utils/symbols");
13
13
  const identifier_1 = require("./identifier");
14
14
  function createModuleLocalNameIdentifier(context, declaration) {
15
15
  const moduleSymbol = context.checker.getSymbolAtLocation(declaration.name);
16
- if (moduleSymbol !== undefined && (0, safe_names_1.isUnsafeName)(moduleSymbol.name)) {
16
+ if (moduleSymbol !== undefined && (0, safe_names_1.isUnsafeName)(moduleSymbol.name, context.options)) {
17
17
  return lua.createIdentifier((0, safe_names_1.createSafeName)(declaration.name.text), declaration.name, moduleSymbol && (0, symbols_1.getSymbolIdOfSymbol)(context, moduleSymbol), declaration.name.text);
18
18
  }
19
19
  // TODO: Should synthetic name nodes be escaped as well?
@@ -11,9 +11,15 @@ const typescript_1 = require("../utils/typescript");
11
11
  const multi_1 = require("./language-extensions/multi");
12
12
  const diagnostics_1 = require("../utils/diagnostics");
13
13
  const vararg_1 = require("./language-extensions/vararg");
14
+ function skipOuterExpressionParents(node) {
15
+ while (ts.isOuterExpression(node)) {
16
+ node = node.parent;
17
+ }
18
+ return node;
19
+ }
14
20
  function isOptimizedVarArgSpread(context, symbol, identifier) {
15
21
  var _a;
16
- if (!ts.isSpreadElement(identifier.parent)) {
22
+ if (!ts.isSpreadElement(skipOuterExpressionParents(identifier.parent))) {
17
23
  return false;
18
24
  }
19
25
  // Walk up, stopping at any scope types which could stop optimization
@@ -51,19 +57,20 @@ function isOptimizedVarArgSpread(context, symbol, identifier) {
51
57
  exports.isOptimizedVarArgSpread = isOptimizedVarArgSpread;
52
58
  // TODO: Currently it's also used as an array member
53
59
  const transformSpreadElement = (node, context) => {
54
- if (ts.isIdentifier(node.expression)) {
55
- if ((0, annotations_1.isVarargType)(context, node.expression)) {
60
+ const tsInnerExpression = ts.skipOuterExpressions(node.expression);
61
+ if (ts.isIdentifier(tsInnerExpression)) {
62
+ if ((0, annotations_1.isVarargType)(context, tsInnerExpression)) {
56
63
  context.diagnostics.push((0, diagnostics_1.annotationRemoved)(node, annotations_1.AnnotationKind.Vararg));
57
64
  }
58
- const symbol = context.checker.getSymbolAtLocation(node.expression);
59
- if (symbol && isOptimizedVarArgSpread(context, symbol, node.expression)) {
65
+ const symbol = context.checker.getSymbolAtLocation(tsInnerExpression);
66
+ if (symbol && isOptimizedVarArgSpread(context, symbol, tsInnerExpression)) {
60
67
  return lua.createDotsLiteral(node);
61
68
  }
62
69
  }
63
70
  const innerExpression = context.transformExpression(node.expression);
64
- if ((0, multi_1.isMultiReturnCall)(context, node.expression))
71
+ if ((0, multi_1.isMultiReturnCall)(context, tsInnerExpression))
65
72
  return innerExpression;
66
- const type = context.checker.getTypeAtLocation(node.expression);
73
+ const type = context.checker.getTypeAtLocation(node.expression); // not ts-inner expression, in case of casts
67
74
  if ((0, typescript_1.isArrayType)(context, type)) {
68
75
  return (0, lua_ast_1.createUnpackCall)(context, innerExpression, node);
69
76
  }