typescript-to-lua 1.1.1 → 1.2.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 (113) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/LuaAST.d.ts +1 -0
  3. package/dist/LuaAST.js +10 -2
  4. package/dist/LuaLib.d.ts +3 -3
  5. package/dist/LuaLib.js +4 -4
  6. package/dist/LuaPrinter.d.ts +11 -4
  7. package/dist/LuaPrinter.js +54 -11
  8. package/dist/lualib/ArrayFlat.lua +1 -1
  9. package/dist/lualib/ArrayFlatMap.lua +1 -1
  10. package/dist/lualib/ArrayIsArray.lua +1 -1
  11. package/dist/lualib/ArrayReduce.lua +7 -1
  12. package/dist/lualib/ArrayReduceRight.lua +7 -1
  13. package/dist/lualib/ArraySetLength.lua +1 -1
  14. package/dist/lualib/ArraySplice.lua +5 -5
  15. package/dist/lualib/Await.lua +14 -10
  16. package/dist/lualib/CloneDescriptor.lua +8 -8
  17. package/dist/lualib/Decorate.lua +1 -1
  18. package/dist/lualib/DelegatedYield.lua +1 -3
  19. package/dist/lualib/Delete.lua +6 -6
  20. package/dist/lualib/Error.lua +47 -44
  21. package/dist/lualib/Generator.lua +1 -5
  22. package/dist/lualib/InstanceOf.lua +1 -1
  23. package/dist/lualib/InstanceOfObject.lua +1 -1
  24. package/dist/lualib/Map.lua +130 -134
  25. package/dist/lualib/Number.lua +1 -1
  26. package/dist/lualib/NumberIsFinite.lua +1 -1
  27. package/dist/lualib/NumberToString.lua +3 -5
  28. package/dist/lualib/ObjectDefineProperty.lua +40 -8
  29. package/dist/lualib/ParseFloat.lua +9 -5
  30. package/dist/lualib/ParseInt.lua +15 -3
  31. package/dist/lualib/Promise.lua +18 -22
  32. package/dist/lualib/Set.lua +119 -123
  33. package/dist/lualib/SetDescriptor.lua +7 -1
  34. package/dist/lualib/SourceMapTraceBack.lua +1 -1
  35. package/dist/lualib/SparseArrayNew.lua +5 -0
  36. package/dist/lualib/SparseArrayPush.lua +9 -0
  37. package/dist/lualib/SparseArraySpread.lua +4 -0
  38. package/dist/lualib/StringAccess.lua +1 -1
  39. package/dist/lualib/StringCharCodeAt.lua +1 -1
  40. package/dist/lualib/StringEndsWith.lua +2 -2
  41. package/dist/lualib/StringPadEnd.lua +2 -2
  42. package/dist/lualib/StringPadStart.lua +2 -2
  43. package/dist/lualib/StringReplace.lua +7 -1
  44. package/dist/lualib/StringSlice.lua +2 -2
  45. package/dist/lualib/StringSplit.lua +3 -3
  46. package/dist/lualib/StringStartsWith.lua +1 -1
  47. package/dist/lualib/StringSubstr.lua +1 -1
  48. package/dist/lualib/StringSubstring.lua +2 -2
  49. package/dist/lualib/Symbol.lua +3 -5
  50. package/dist/lualib/WeakMap.lua +40 -42
  51. package/dist/lualib/WeakSet.lua +36 -38
  52. package/dist/lualib/lualib_bundle.lua +603 -555
  53. package/dist/transformation/builtins/array.js +1 -2
  54. package/dist/transformation/builtins/function.js +1 -2
  55. package/dist/transformation/builtins/index.d.ts +1 -1
  56. package/dist/transformation/builtins/index.js +37 -4
  57. package/dist/transformation/builtins/object.d.ts +2 -1
  58. package/dist/transformation/builtins/object.js +1 -2
  59. package/dist/transformation/builtins/string.js +1 -2
  60. package/dist/transformation/context/context.d.ts +12 -0
  61. package/dist/transformation/context/context.js +102 -3
  62. package/dist/transformation/utils/diagnostics.d.ts +6 -0
  63. package/dist/transformation/utils/diagnostics.js +3 -1
  64. package/dist/transformation/utils/lua-ast.d.ts +0 -1
  65. package/dist/transformation/utils/lua-ast.js +13 -22
  66. package/dist/transformation/utils/preceding-statements.d.ts +3 -0
  67. package/dist/transformation/utils/preceding-statements.js +11 -0
  68. package/dist/transformation/utils/scope.d.ts +1 -0
  69. package/dist/transformation/utils/scope.js +8 -1
  70. package/dist/transformation/utils/typescript/index.d.ts +1 -0
  71. package/dist/transformation/utils/typescript/index.js +16 -1
  72. package/dist/transformation/visitors/access.d.ts +3 -0
  73. package/dist/transformation/visitors/access.js +63 -36
  74. package/dist/transformation/visitors/binary-expression/assignments.d.ts +3 -2
  75. package/dist/transformation/visitors/binary-expression/assignments.js +66 -57
  76. package/dist/transformation/visitors/binary-expression/compound.d.ts +5 -4
  77. package/dist/transformation/visitors/binary-expression/compound.js +66 -68
  78. package/dist/transformation/visitors/binary-expression/destructuring-assignments.d.ts +2 -2
  79. package/dist/transformation/visitors/binary-expression/destructuring-assignments.js +54 -25
  80. package/dist/transformation/visitors/binary-expression/index.d.ts +3 -1
  81. package/dist/transformation/visitors/binary-expression/index.js +64 -20
  82. package/dist/transformation/visitors/call.d.ts +3 -2
  83. package/dist/transformation/visitors/call.js +109 -122
  84. package/dist/transformation/visitors/class/index.js +4 -7
  85. package/dist/transformation/visitors/class/new.js +4 -6
  86. package/dist/transformation/visitors/conditional.js +28 -14
  87. package/dist/transformation/visitors/delete.js +4 -0
  88. package/dist/transformation/visitors/expression-list.d.ts +7 -0
  89. package/dist/transformation/visitors/expression-list.js +120 -0
  90. package/dist/transformation/visitors/function.js +6 -8
  91. package/dist/transformation/visitors/identifier.js +4 -0
  92. package/dist/transformation/visitors/literal.js +69 -16
  93. package/dist/transformation/visitors/loops/do-while.js +34 -8
  94. package/dist/transformation/visitors/loops/for.js +25 -5
  95. package/dist/transformation/visitors/loops/utils.d.ts +1 -0
  96. package/dist/transformation/visitors/loops/utils.js +13 -2
  97. package/dist/transformation/visitors/modules/export.js +1 -1
  98. package/dist/transformation/visitors/optional-chaining.d.ts +16 -0
  99. package/dist/transformation/visitors/optional-chaining.js +172 -0
  100. package/dist/transformation/visitors/sourceFile.js +4 -1
  101. package/dist/transformation/visitors/switch.js +29 -9
  102. package/dist/transformation/visitors/template.js +5 -2
  103. package/dist/transformation/visitors/typeof.js +3 -1
  104. package/dist/transformation/visitors/variable-declaration.js +21 -7
  105. package/dist/transformation/visitors/void.js +4 -7
  106. package/dist/transpilation/transformers.js +4 -1
  107. package/dist/transpilation/utils.js +2 -1
  108. package/package.json +5 -5
  109. package/dist/lualib/OptionalChainAccess.lua +0 -6
  110. package/dist/lualib/OptionalFunctionCall.lua +0 -6
  111. package/dist/lualib/OptionalMethodCall.lua +0 -11
  112. package/dist/transformation/utils/transform.d.ts +0 -8
  113. package/dist/transformation/utils/transform.js +0 -14
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformCallExpression = exports.transformContextualCallExpression = exports.transformArguments = exports.flattenSpreadExpressions = void 0;
3
+ exports.transformCallExpression = exports.transformContextualCallExpression = exports.transformCallAndArguments = exports.transformArguments = exports.validateArguments = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../LuaAST");
6
6
  const builtins_1 = require("../builtins");
@@ -8,7 +8,6 @@ const annotations_1 = require("../utils/annotations");
8
8
  const assignment_validation_1 = require("../utils/assignment-validation");
9
9
  const function_context_1 = require("../utils/function-context");
10
10
  const lua_ast_1 = require("../utils/lua-ast");
11
- const lualib_1 = require("../utils/lualib");
12
11
  const safe_names_1 = require("../utils/safe-names");
13
12
  const typescript_1 = require("../utils/typescript");
14
13
  const access_1 = require("./access");
@@ -16,116 +15,97 @@ const multi_1 = require("./language-extensions/multi");
16
15
  const operators_1 = require("./language-extensions/operators");
17
16
  const table_1 = require("./language-extensions/table");
18
17
  const diagnostics_1 = require("../utils/diagnostics");
19
- const transform_1 = require("../utils/transform");
20
- function getExpressionsBeforeAndAfterFirstSpread(expressions) {
21
- // [a, b, ...c, d, ...e] --> [a, b] and [...c, d, ...e]
22
- const index = expressions.findIndex(ts.isSpreadElement);
23
- const hasSpreadElement = index !== -1;
24
- const before = hasSpreadElement ? expressions.slice(0, index) : expressions;
25
- const after = hasSpreadElement ? expressions.slice(index) : [];
26
- return [before, after];
27
- }
28
- function transformSpreadableExpressionsIntoArrayConcatArguments(context, expressions) {
29
- // [...array, a, b, ...tuple()] --> [ [...array], [a, b], [...tuple()] ]
30
- // chunk non-spread arguments together so they don't concat
31
- const chunks = [];
32
- for (const [index, expression] of expressions.entries()) {
33
- if (ts.isSpreadElement(expression)) {
34
- chunks.push([expression]);
35
- const next = expressions[index + 1];
36
- if (next && !ts.isSpreadElement(next)) {
37
- chunks.push([]);
38
- }
39
- }
40
- else {
41
- let lastChunk = chunks[chunks.length - 1];
42
- if (!lastChunk) {
43
- lastChunk = [];
44
- chunks.push(lastChunk);
45
- }
46
- lastChunk.push(expression);
18
+ const expression_list_1 = require("./expression-list");
19
+ const preceding_statements_1 = require("../utils/preceding-statements");
20
+ const optional_chaining_1 = require("./optional-chaining");
21
+ function validateArguments(context, params, signature) {
22
+ if (!signature || signature.parameters.length < params.length) {
23
+ return;
24
+ }
25
+ for (const [index, param] of params.entries()) {
26
+ const signatureParameter = signature.parameters[index];
27
+ const paramType = context.checker.getTypeAtLocation(param);
28
+ if (signatureParameter.valueDeclaration !== undefined) {
29
+ const signatureType = context.checker.getTypeAtLocation(signatureParameter.valueDeclaration);
30
+ (0, assignment_validation_1.validateAssignment)(context, param, paramType, signatureType, signatureParameter.name);
47
31
  }
48
32
  }
49
- return chunks.map(chunk => (0, lua_ast_1.wrapInTable)(...chunk.map(expression => context.transformExpression(expression))));
50
33
  }
51
- function flattenSpreadExpressions(context, expressions) {
52
- const [preSpreadExpressions, postSpreadExpressions] = getExpressionsBeforeAndAfterFirstSpread(expressions);
53
- const transformedPreSpreadExpressions = preSpreadExpressions.map(a => context.transformExpression(a));
54
- // Nothing special required
55
- if (postSpreadExpressions.length === 0) {
56
- return transformedPreSpreadExpressions;
57
- }
58
- // Only one spread element at the end? Will work as expected
59
- if (postSpreadExpressions.length === 1) {
60
- return [...transformedPreSpreadExpressions, context.transformExpression(postSpreadExpressions[0])];
61
- }
62
- // Use Array.concat and unpack the result of that as the last Expression
63
- const concatArguments = transformSpreadableExpressionsIntoArrayConcatArguments(context, postSpreadExpressions);
64
- const lastExpression = (0, lua_ast_1.createUnpackCall)(context, (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.ArrayConcat, undefined, ...concatArguments));
65
- return [...transformedPreSpreadExpressions, lastExpression];
66
- }
67
- exports.flattenSpreadExpressions = flattenSpreadExpressions;
34
+ exports.validateArguments = validateArguments;
68
35
  function transformArguments(context, params, signature, callContext) {
69
- const parameters = flattenSpreadExpressions(context, params);
70
- // Add context as first param if present
36
+ validateArguments(context, params, signature);
37
+ return (0, expression_list_1.transformExpressionList)(context, callContext ? [callContext, ...params] : params);
38
+ }
39
+ exports.transformArguments = transformArguments;
40
+ function transformCallWithArguments(context, callExpression, transformedArguments, argPrecedingStatements, callContext) {
41
+ let call = context.transformExpression(callExpression);
42
+ let transformedContext;
71
43
  if (callContext) {
72
- parameters.unshift(context.transformExpression(callContext));
73
- }
74
- if (signature && signature.parameters.length >= params.length) {
75
- for (const [index, param] of params.entries()) {
76
- const signatureParameter = signature.parameters[index];
77
- const paramType = context.checker.getTypeAtLocation(param);
78
- if (signatureParameter.valueDeclaration !== undefined) {
79
- const signatureType = context.checker.getTypeAtLocation(signatureParameter.valueDeclaration);
80
- (0, assignment_validation_1.validateAssignment)(context, param, paramType, signatureType, signatureParameter.name);
81
- }
44
+ transformedContext = context.transformExpression(callContext);
45
+ }
46
+ if (argPrecedingStatements.length > 0) {
47
+ if (transformedContext) {
48
+ transformedContext = (0, expression_list_1.moveToPrecedingTemp)(context, transformedContext, callContext);
82
49
  }
50
+ call = (0, expression_list_1.moveToPrecedingTemp)(context, call, callExpression);
51
+ context.addPrecedingStatements(argPrecedingStatements);
83
52
  }
84
- return parameters;
53
+ if (transformedContext) {
54
+ transformedArguments.unshift(transformedContext);
55
+ }
56
+ return [call, transformedArguments];
85
57
  }
86
- exports.transformArguments = transformArguments;
87
- function transformElementAccessCall(context, left, args, signature) {
88
- const transformedArguments = transformArguments(context, args, signature, ts.factory.createIdentifier("____self"));
58
+ function transformCallAndArguments(context, callExpression, params, signature, callContext) {
59
+ const [argPrecedingStatements, transformedArguments] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => transformArguments(context, params, signature, callContext));
60
+ return transformCallWithArguments(context, callExpression, transformedArguments, argPrecedingStatements);
61
+ }
62
+ exports.transformCallAndArguments = transformCallAndArguments;
63
+ function transformElementAccessCall(context, left, transformedArguments, argPrecedingStatements) {
89
64
  // Cache left-side if it has effects
90
- // (function() local ____self = context; return ____self[argument](parameters); end)()
65
+ // local ____self = context; return ____self[argument](parameters);
66
+ const selfIdentifier = lua.createIdentifier(context.createTempName("self"));
67
+ const callContext = context.transformExpression(left.expression);
68
+ const selfAssignment = lua.createVariableDeclarationStatement(selfIdentifier, callContext);
69
+ context.addPrecedingStatements(selfAssignment);
91
70
  const argument = ts.isElementAccessExpression(left)
92
71
  ? (0, access_1.transformElementAccessArgument)(context, left)
93
72
  : lua.createStringLiteral(left.name.text);
94
- const selfIdentifier = lua.createIdentifier("____self");
95
- const callContext = context.transformExpression(left.expression);
96
- const selfAssignment = lua.createVariableDeclarationStatement(selfIdentifier, callContext);
97
- const index = lua.createTableIndexExpression(selfIdentifier, argument);
98
- const callExpression = lua.createCallExpression(index, transformedArguments);
99
- return { statements: selfAssignment, result: callExpression };
73
+ let index = lua.createTableIndexExpression(selfIdentifier, argument);
74
+ if (argPrecedingStatements.length > 0) {
75
+ // Cache index in temp if args had preceding statements
76
+ index = (0, expression_list_1.moveToPrecedingTemp)(context, index);
77
+ context.addPrecedingStatements(argPrecedingStatements);
78
+ }
79
+ return lua.createCallExpression(index, [selfIdentifier, ...transformedArguments]);
100
80
  }
101
81
  function transformContextualCallExpression(context, node, args, signature) {
82
+ if (ts.isOptionalChain(node)) {
83
+ return (0, optional_chaining_1.transformOptionalChain)(context, node);
84
+ }
102
85
  const left = ts.isCallExpression(node) ? node.expression : node.tag;
103
- if (ts.isPropertyAccessExpression(left) && ts.isIdentifier(left.name) && (0, safe_names_1.isValidLuaIdentifier)(left.name.text)) {
86
+ let [argPrecedingStatements, transformedArguments] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => transformArguments(context, args, signature));
87
+ if (ts.isPropertyAccessExpression(left) &&
88
+ ts.isIdentifier(left.name) &&
89
+ (0, safe_names_1.isValidLuaIdentifier)(left.name.text) &&
90
+ argPrecedingStatements.length === 0) {
104
91
  // table:name()
105
92
  const table = context.transformExpression(left.expression);
106
- if (ts.isOptionalChain(node)) {
107
- return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.OptionalMethodCall, node, table, lua.createStringLiteral(left.name.text, left.name), lua.createBooleanLiteral(node.questionDotToken !== undefined), // Require method is present if no ?.() call
108
- ...transformArguments(context, args, signature));
109
- }
110
- else {
111
- return lua.createMethodCallExpression(table, lua.createIdentifier(left.name.text, left.name), transformArguments(context, args, signature), node);
112
- }
93
+ return lua.createMethodCallExpression(table, lua.createIdentifier(left.name.text, left.name), transformedArguments, node);
113
94
  }
114
95
  else if (ts.isElementAccessExpression(left) || ts.isPropertyAccessExpression(left)) {
115
96
  if ((0, typescript_1.isExpressionWithEvaluationEffect)(left.expression)) {
116
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => transformElementAccessCall(context, left, args, signature), node);
97
+ return transformElementAccessCall(context, left, transformedArguments, argPrecedingStatements);
117
98
  }
118
99
  else {
119
- const callContext = context.transformExpression(left.expression);
120
- const expression = context.transformExpression(left);
121
- const transformedArguments = transformArguments(context, args, signature);
122
- return lua.createCallExpression(expression, [callContext, ...transformedArguments]);
100
+ let expression;
101
+ [expression, transformedArguments] = transformCallWithArguments(context, left, transformedArguments, argPrecedingStatements, left.expression);
102
+ return lua.createCallExpression(expression, transformedArguments, node);
123
103
  }
124
104
  }
125
105
  else if (ts.isIdentifier(left)) {
126
106
  const callContext = context.isStrict ? ts.factory.createNull() : ts.factory.createIdentifier("_G");
127
- const transformedArguments = transformArguments(context, args, signature, callContext);
128
- const expression = context.transformExpression(left);
107
+ let expression;
108
+ [expression, transformedArguments] = transformCallWithArguments(context, left, transformedArguments, argPrecedingStatements, callContext);
129
109
  return lua.createCallExpression(expression, transformedArguments, node);
130
110
  }
131
111
  else {
@@ -147,14 +127,8 @@ function transformPropertyCall(context, node) {
147
127
  }
148
128
  else {
149
129
  // table.name()
150
- const callPath = context.transformExpression(node.expression);
151
- const parameters = transformArguments(context, node.arguments, signature);
152
- if (ts.isOptionalChain(node)) {
153
- return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.OptionalFunctionCall, node, callPath, ...parameters);
154
- }
155
- else {
156
- return lua.createCallExpression(callPath, parameters, node);
157
- }
130
+ const [callPath, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature);
131
+ return lua.createCallExpression(callPath, parameters, node);
158
132
  }
159
133
  }
160
134
  function transformElementCall(context, node) {
@@ -166,37 +140,58 @@ function transformElementCall(context, node) {
166
140
  }
167
141
  else {
168
142
  // No context
169
- const expression = context.transformExpression(node.expression);
170
- const parameters = transformArguments(context, node.arguments, signature);
143
+ const [expression, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature);
171
144
  return lua.createCallExpression(expression, parameters);
172
145
  }
173
146
  }
174
147
  const transformCallExpression = (node, context) => {
148
+ if (ts.isOptionalChain(node)) {
149
+ return (0, optional_chaining_1.transformOptionalChain)(context, node);
150
+ }
151
+ const optionalContinuation = ts.isIdentifier(node.expression)
152
+ ? (0, optional_chaining_1.getOptionalContinuationData)(node.expression)
153
+ : undefined;
175
154
  const wrapResultInTable = (0, multi_1.isMultiReturnCall)(context, node) && (0, multi_1.shouldMultiReturnCallBeWrapped)(context, node);
176
- const wrapResultInOptional = ts.isOptionalChain(node);
177
- const builtinResult = (0, builtins_1.transformBuiltinCallExpression)(context, node);
155
+ const builtinResult = (0, builtins_1.transformBuiltinCallExpression)(context, node, optionalContinuation !== undefined);
178
156
  if (builtinResult) {
157
+ if (optionalContinuation) {
158
+ context.diagnostics.push((0, diagnostics_1.unsupportedBuiltinOptionalCall)(node));
159
+ }
179
160
  return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(builtinResult) : builtinResult;
180
161
  }
181
162
  if ((0, annotations_1.isTupleReturnCall)(context, node)) {
182
163
  context.diagnostics.push((0, diagnostics_1.annotationRemoved)(node, annotations_1.AnnotationKind.TupleReturn));
183
164
  }
184
165
  if ((0, operators_1.isOperatorMapping)(context, node)) {
166
+ if (optionalContinuation) {
167
+ context.diagnostics.push((0, diagnostics_1.unsupportedBuiltinOptionalCall)(node));
168
+ return lua.createNilLiteral();
169
+ }
185
170
  return (0, operators_1.transformOperatorMappingExpression)(context, node);
186
171
  }
187
172
  if ((0, table_1.isTableDeleteCall)(context, node)) {
188
173
  context.diagnostics.push((0, diagnostics_1.invalidTableDeleteExpression)(node));
189
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => ({ statements: (0, table_1.transformTableDeleteExpression)(context, node), result: lua.createNilLiteral() }), node);
174
+ context.addPrecedingStatements((0, table_1.transformTableDeleteExpression)(context, node));
175
+ return lua.createNilLiteral();
190
176
  }
191
177
  if ((0, table_1.isTableGetCall)(context, node)) {
178
+ if (optionalContinuation) {
179
+ context.diagnostics.push((0, diagnostics_1.unsupportedBuiltinOptionalCall)(node));
180
+ return lua.createNilLiteral();
181
+ }
192
182
  return (0, table_1.transformTableGetExpression)(context, node);
193
183
  }
194
184
  if ((0, table_1.isTableHasCall)(context, node)) {
185
+ if (optionalContinuation) {
186
+ context.diagnostics.push((0, diagnostics_1.unsupportedBuiltinOptionalCall)(node));
187
+ return lua.createNilLiteral();
188
+ }
195
189
  return (0, table_1.transformTableHasExpression)(context, node);
196
190
  }
197
191
  if ((0, table_1.isTableSetCall)(context, node)) {
198
192
  context.diagnostics.push((0, diagnostics_1.invalidTableSetExpression)(node));
199
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => ({ statements: (0, table_1.transformTableSetExpression)(context, node), result: lua.createNilLiteral() }), node);
193
+ context.addPrecedingStatements((0, table_1.transformTableSetExpression)(context, node));
194
+ return lua.createNilLiteral();
200
195
  }
201
196
  if (ts.isPropertyAccessExpression(node.expression)) {
202
197
  const ownerType = context.checker.getTypeAtLocation(node.expression.expression);
@@ -205,12 +200,11 @@ const transformCallExpression = (node, context) => {
205
200
  context.diagnostics.push((0, diagnostics_1.annotationRemoved)(node, annotations_1.AnnotationKind.LuaTable));
206
201
  }
207
202
  const result = transformPropertyCall(context, node);
208
- // transformPropertyCall already wraps optional so no need to do so here
209
203
  return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(result) : result;
210
204
  }
211
205
  if (ts.isElementAccessExpression(node.expression)) {
212
206
  const result = transformElementCall(context, node);
213
- return wrapIfRequired(context, wrapResultInTable, wrapResultInOptional, result, node);
207
+ return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(result) : result;
214
208
  }
215
209
  const signature = context.checker.getResolvedSignature(node);
216
210
  // Handle super calls properly
@@ -218,31 +212,24 @@ const transformCallExpression = (node, context) => {
218
212
  const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis());
219
213
  return lua.createCallExpression(lua.createTableIndexExpression(context.transformExpression(ts.factory.createSuper()), lua.createStringLiteral("____constructor")), parameters);
220
214
  }
221
- const callPath = context.transformExpression(node.expression);
222
215
  const signatureDeclaration = signature === null || signature === void 0 ? void 0 : signature.getDeclaration();
223
- let parameters = [];
224
- if (signatureDeclaration && (0, function_context_1.getDeclarationContextType)(context, signatureDeclaration) === function_context_1.ContextType.Void) {
225
- parameters = transformArguments(context, node.arguments, signature);
216
+ let callPath;
217
+ let parameters;
218
+ const isContextualCall = !signatureDeclaration || (0, function_context_1.getDeclarationContextType)(context, signatureDeclaration) !== function_context_1.ContextType.Void;
219
+ if (!isContextualCall) {
220
+ [callPath, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature);
226
221
  }
227
222
  else {
228
- const callContext = context.isStrict ? ts.factory.createNull() : ts.factory.createIdentifier("_G");
229
- parameters = transformArguments(context, node.arguments, signature, callContext);
223
+ // if is optionalContinuation, context will be handled by transformOptionalChain.
224
+ const useGlobalContext = !context.isStrict && optionalContinuation === undefined;
225
+ const callContext = useGlobalContext ? ts.factory.createIdentifier("_G") : ts.factory.createNull();
226
+ [callPath, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature, callContext);
230
227
  }
231
228
  const callExpression = lua.createCallExpression(callPath, parameters, node);
232
- return wrapIfRequired(context, wrapResultInTable, wrapResultInOptional, callExpression, node);
229
+ if (optionalContinuation && isContextualCall) {
230
+ optionalContinuation.contextualCall = callExpression;
231
+ }
232
+ return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(callExpression) : callExpression;
233
233
  };
234
234
  exports.transformCallExpression = transformCallExpression;
235
- function wrapIfRequired(context, shouldWrapInTable, shouldWrapOptional, call, node) {
236
- const wrappedOptional = shouldWrapOptional ? wrapOptionalCall(context, call, node) : call;
237
- return shouldWrapInTable ? (0, lua_ast_1.wrapInTable)(wrappedOptional) : wrappedOptional;
238
- }
239
- function wrapOptionalCall(context, call, node) {
240
- if (lua.isMethodCallExpression(call)) {
241
- return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.OptionalMethodCall, node, call.prefixExpression, lua.createStringLiteral(call.name.text), lua.createBooleanLiteral(node.questionDotToken !== undefined), // Require method is present if no ?.() call
242
- ...call.params);
243
- }
244
- else {
245
- return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.OptionalFunctionCall, node, call.expression, ...call.params);
246
- }
247
- }
248
235
  //# sourceMappingURL=call.js.map
@@ -9,7 +9,6 @@ const diagnostics_1 = require("../../utils/diagnostics");
9
9
  const export_1 = require("../../utils/export");
10
10
  const lua_ast_1 = require("../../utils/lua-ast");
11
11
  const safe_names_1 = require("../../utils/safe-names");
12
- const transform_1 = require("../../utils/transform");
13
12
  const identifier_1 = require("../identifier");
14
13
  const decorators_1 = require("./decorators");
15
14
  const accessors_1 = require("./members/accessors");
@@ -33,10 +32,9 @@ exports.transformClassDeclaration = transformClassDeclaration;
33
32
  const transformThisExpression = node => (0, lua_ast_1.createSelfIdentifier)(node);
34
33
  exports.transformThisExpression = transformThisExpression;
35
34
  function transformClassAsExpression(expression, context) {
36
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => {
37
- const { statements, name } = transformClassLikeDeclaration(expression, context);
38
- return { statements: (0, lua_ast_1.unwrapVisitorResult)(statements), result: name };
39
- }, expression);
35
+ const { statements, name } = transformClassLikeDeclaration(expression, context);
36
+ context.addPrecedingStatements(statements);
37
+ return name;
40
38
  }
41
39
  exports.transformClassAsExpression = transformClassAsExpression;
42
40
  const classSuperInfos = new WeakMap();
@@ -49,8 +47,7 @@ function transformClassLikeDeclaration(classDeclaration, context, nameOverride)
49
47
  className = (0, identifier_1.transformIdentifier)(context, classDeclaration.name);
50
48
  }
51
49
  else {
52
- // TypeScript error
53
- className = lua.createAnonymousIdentifier();
50
+ className = lua.createIdentifier(context.createTempName("class"), classDeclaration);
54
51
  }
55
52
  const annotations = (0, annotations_1.getTypeAnnotations)(context.checker.getTypeAtLocation(classDeclaration));
56
53
  if (annotations.has(annotations_1.AnnotationKind.Extension)) {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformNewExpression = exports.checkForLuaLibType = void 0;
4
+ const ts = require("typescript");
4
5
  const lua = require("../../../LuaAST");
5
6
  const annotations_1 = require("../../utils/annotations");
6
7
  const diagnostics_1 = require("../../utils/diagnostics");
@@ -46,7 +47,7 @@ function checkForLuaLibType(context, type) {
46
47
  }
47
48
  exports.checkForLuaLibType = checkForLuaLibType;
48
49
  const transformNewExpression = (node, context) => {
49
- var _a;
50
+ var _a, _b;
50
51
  const type = context.checker.getTypeAtLocation(node);
51
52
  const annotations = (0, annotations_1.getTypeAnnotations)(type);
52
53
  if (annotations.has(annotations_1.AnnotationKind.LuaTable)) {
@@ -56,15 +57,12 @@ const transformNewExpression = (node, context) => {
56
57
  return lua.createTableExpression(undefined, node);
57
58
  }
58
59
  const signature = context.checker.getResolvedSignature(node);
59
- const params = node.arguments
60
- ? (0, call_1.transformArguments)(context, node.arguments, signature)
61
- : [lua.createBooleanLiteral(true)];
60
+ const [name, params] = (0, call_1.transformCallAndArguments)(context, node.expression, (_a = node.arguments) !== null && _a !== void 0 ? _a : [ts.factory.createTrue()], signature);
62
61
  checkForLuaLibType(context, type);
63
- const name = context.transformExpression(node.expression);
64
62
  const customConstructorAnnotation = annotations.get(annotations_1.AnnotationKind.CustomConstructor);
65
63
  if (customConstructorAnnotation) {
66
64
  if (customConstructorAnnotation.args.length === 1) {
67
- return lua.createCallExpression(lua.createIdentifier(customConstructorAnnotation.args[0]), (0, call_1.transformArguments)(context, (_a = node.arguments) !== null && _a !== void 0 ? _a : []), node);
65
+ return lua.createCallExpression(lua.createIdentifier(customConstructorAnnotation.args[0]), (0, call_1.transformArguments)(context, (_b = node.arguments) !== null && _b !== void 0 ? _b : []), node);
68
66
  }
69
67
  else {
70
68
  context.diagnostics.push((0, diagnostics_1.annotationInvalidArgumentCount)(node, annotations_1.AnnotationKind.CustomConstructor, customConstructorAnnotation.args.length, 1));
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformIfStatement = exports.transformConditionalExpression = 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 block_1 = require("./block");
8
9
  function canBeFalsy(context, type) {
@@ -27,20 +28,18 @@ function canBeFalsy(context, type) {
27
28
  return false;
28
29
  }
29
30
  }
30
- function wrapInFunctionCall(expression) {
31
- const returnStatement = lua.createReturnStatement([expression]);
32
- return lua.createFunctionExpression(lua.createBlock([returnStatement]), undefined, undefined, lua.FunctionExpressionFlags.Inline);
33
- }
34
31
  function transformProtectedConditionalExpression(context, expression) {
32
+ const tempVar = context.createTempNameForNode(expression.condition);
35
33
  const condition = context.transformExpression(expression.condition);
36
- const val1 = context.transformExpression(expression.whenTrue);
37
- const val2 = context.transformExpression(expression.whenFalse);
38
- const val1Function = wrapInFunctionCall(val1);
39
- const val2Function = wrapInFunctionCall(val2);
40
- // (condition and (() => v1) or (() => v2))()
41
- const conditionAnd = lua.createBinaryExpression(condition, val1Function, lua.SyntaxKind.AndOperator);
42
- const orExpression = lua.createBinaryExpression(conditionAnd, val2Function, lua.SyntaxKind.OrOperator);
43
- return lua.createCallExpression(orExpression, [], expression);
34
+ const [trueStatements, val1] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expression.whenTrue));
35
+ trueStatements.push(lua.createAssignmentStatement(lua.cloneIdentifier(tempVar), val1, expression.whenTrue));
36
+ const [falseStatements, val2] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expression.whenFalse));
37
+ falseStatements.push(lua.createAssignmentStatement(lua.cloneIdentifier(tempVar), val2, expression.whenFalse));
38
+ context.addPrecedingStatements([
39
+ lua.createVariableDeclarationStatement(tempVar, undefined, expression.condition),
40
+ lua.createIfStatement(condition, lua.createBlock(trueStatements, expression.whenTrue), lua.createBlock(falseStatements, expression.whenFalse), expression),
41
+ ]);
42
+ return lua.cloneIdentifier(tempVar);
44
43
  }
45
44
  const transformConditionalExpression = (expression, context) => {
46
45
  if (canBeFalsy(context, context.checker.getTypeAtLocation(expression.whenTrue))) {
@@ -62,8 +61,23 @@ function transformIfStatement(statement, context) {
62
61
  const ifBlock = lua.createBlock(statements);
63
62
  if (statement.elseStatement) {
64
63
  if (ts.isIfStatement(statement.elseStatement)) {
65
- const elseStatement = transformIfStatement(statement.elseStatement, context);
66
- return lua.createIfStatement(condition, ifBlock, elseStatement);
64
+ const tsElseStatement = statement.elseStatement;
65
+ const [precedingStatements, elseStatement] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => transformIfStatement(tsElseStatement, context));
66
+ // If else-if condition generates preceding statements, we can't use elseif, we have to break it down:
67
+ // if conditionA then
68
+ // ...
69
+ // else
70
+ // conditionB's preceding statements
71
+ // if conditionB then
72
+ // end
73
+ // end
74
+ if (precedingStatements.length > 0) {
75
+ const elseBlock = lua.createBlock([...precedingStatements, elseStatement]);
76
+ return lua.createIfStatement(condition, ifBlock, elseBlock);
77
+ }
78
+ else {
79
+ return lua.createIfStatement(condition, ifBlock, elseStatement);
80
+ }
67
81
  }
68
82
  else {
69
83
  (0, scope_1.pushScope)(context, scope_1.ScopeType.Conditional);
@@ -7,7 +7,11 @@ const lualib_1 = require("../utils/lualib");
7
7
  const diagnostics_1 = require("../utils/diagnostics");
8
8
  const typescript_1 = require("../utils/typescript");
9
9
  const lua_ast_1 = require("../utils/lua-ast");
10
+ const optional_chaining_1 = require("./optional-chaining");
10
11
  const transformDeleteExpression = (node, context) => {
12
+ if (ts.isOptionalChain(node.expression)) {
13
+ return (0, optional_chaining_1.transformOptionalDeleteExpression)(context, node, node.expression);
14
+ }
11
15
  let ownerExpression;
12
16
  let propertyExpression;
13
17
  if (ts.isPropertyAccessExpression(node.expression)) {
@@ -0,0 +1,7 @@
1
+ import * as ts from "typescript";
2
+ import * as lua from "../../LuaAST";
3
+ import { TransformationContext } from "../context";
4
+ export declare function shouldMoveToTemp(context: TransformationContext, expression: lua.Expression, tsOriginal?: ts.Node): boolean;
5
+ export declare function moveToPrecedingTemp(context: TransformationContext, expression: lua.Expression, tsOriginal?: ts.Node): lua.Expression;
6
+ export declare function transformExpressionList(context: TransformationContext, expressions: readonly ts.Expression[]): lua.Expression[];
7
+ export declare function transformOrderedExpressions(context: TransformationContext, expressions: readonly ts.Expression[]): lua.Expression[];
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformOrderedExpressions = exports.transformExpressionList = exports.moveToPrecedingTemp = exports.shouldMoveToTemp = void 0;
4
+ const assert = require("assert");
5
+ const ts = require("typescript");
6
+ const lua = require("../../LuaAST");
7
+ const context_1 = require("../context");
8
+ const lualib_1 = require("../utils/lualib");
9
+ const preceding_statements_1 = require("../utils/preceding-statements");
10
+ const typescript_1 = require("../utils/typescript");
11
+ const optional_chaining_1 = require("./optional-chaining");
12
+ function shouldMoveToTemp(context, expression, tsOriginal) {
13
+ return (!lua.isLiteral(expression) &&
14
+ !(lua.isIdentifier(expression) && expression.symbolId === context_1.tempSymbolId) && // Treat generated temps as consts
15
+ !(tsOriginal && ((0, typescript_1.isConstIdentifier)(context, tsOriginal) || (0, optional_chaining_1.isOptionalContinuation)(tsOriginal))));
16
+ }
17
+ exports.shouldMoveToTemp = shouldMoveToTemp;
18
+ // Cache an expression in a preceding statement and return the temp identifier
19
+ function moveToPrecedingTemp(context, expression, tsOriginal) {
20
+ if (!shouldMoveToTemp(context, expression, tsOriginal)) {
21
+ return expression;
22
+ }
23
+ const tempIdentifier = context.createTempNameForLuaExpression(expression);
24
+ const tempDeclaration = lua.createVariableDeclarationStatement(tempIdentifier, expression, tsOriginal);
25
+ context.addPrecedingStatements(tempDeclaration);
26
+ return lua.cloneIdentifier(tempIdentifier, tsOriginal);
27
+ }
28
+ exports.moveToPrecedingTemp = moveToPrecedingTemp;
29
+ function transformExpressions(context, expressions) {
30
+ const precedingStatements = [];
31
+ const transformedExpressions = [];
32
+ let lastPrecedingStatementsIndex = -1;
33
+ for (let i = 0; i < expressions.length; ++i) {
34
+ const [expressionPrecedingStatements, expression] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expressions[i]));
35
+ transformedExpressions.push(expression);
36
+ if (expressionPrecedingStatements.length > 0) {
37
+ lastPrecedingStatementsIndex = i;
38
+ }
39
+ precedingStatements.push(expressionPrecedingStatements);
40
+ }
41
+ return { transformedExpressions, precedingStatements, lastPrecedingStatementsIndex };
42
+ }
43
+ function transformExpressionsUsingTemps(context, expressions, transformedExpressions, precedingStatements, lastPrecedingStatementsIndex) {
44
+ for (let i = 0; i < transformedExpressions.length; ++i) {
45
+ context.addPrecedingStatements(precedingStatements[i]);
46
+ if (i < lastPrecedingStatementsIndex) {
47
+ transformedExpressions[i] = moveToPrecedingTemp(context, transformedExpressions[i], expressions[i]);
48
+ }
49
+ }
50
+ return transformedExpressions;
51
+ }
52
+ function pushToSparseArray(context, arrayIdentifier, expressions) {
53
+ if (!arrayIdentifier) {
54
+ arrayIdentifier = lua.createIdentifier(context.createTempName("array"));
55
+ const libCall = (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.SparseArrayNew, undefined, ...expressions);
56
+ const declaration = lua.createVariableDeclarationStatement(arrayIdentifier, libCall);
57
+ context.addPrecedingStatements(declaration);
58
+ }
59
+ else {
60
+ const libCall = (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.SparseArrayPush, undefined, arrayIdentifier, ...expressions);
61
+ context.addPrecedingStatements(lua.createExpressionStatement(libCall));
62
+ }
63
+ return arrayIdentifier;
64
+ }
65
+ function transformExpressionsUsingSparseArray(context, expressions, transformedExpressions, precedingStatements) {
66
+ let arrayIdentifier;
67
+ let expressionBatch = [];
68
+ for (let i = 0; i < expressions.length; ++i) {
69
+ // Expressions with preceding statements should always be at the start of a batch
70
+ if (precedingStatements[i].length > 0 && expressionBatch.length > 0) {
71
+ arrayIdentifier = pushToSparseArray(context, arrayIdentifier, expressionBatch);
72
+ expressionBatch = [];
73
+ }
74
+ context.addPrecedingStatements(precedingStatements[i]);
75
+ expressionBatch.push(transformedExpressions[i]);
76
+ // Spread expressions should always be at the end of a batch
77
+ if (ts.isSpreadElement(expressions[i])) {
78
+ arrayIdentifier = pushToSparseArray(context, arrayIdentifier, expressionBatch);
79
+ expressionBatch = [];
80
+ }
81
+ }
82
+ if (expressionBatch.length > 0) {
83
+ arrayIdentifier = pushToSparseArray(context, arrayIdentifier, expressionBatch);
84
+ }
85
+ assert(arrayIdentifier);
86
+ return [(0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.SparseArraySpread, undefined, arrayIdentifier)];
87
+ }
88
+ function countNeededTemps(context, expressions, transformedExpressions, lastPrecedingStatementsIndex) {
89
+ if (lastPrecedingStatementsIndex < 0) {
90
+ return 0;
91
+ }
92
+ return transformedExpressions
93
+ .slice(0, lastPrecedingStatementsIndex)
94
+ .filter((e, i) => shouldMoveToTemp(context, e, expressions[i])).length;
95
+ }
96
+ // Transforms a list of expressions while flattening spreads and maintaining execution order
97
+ function transformExpressionList(context, expressions) {
98
+ const { transformedExpressions, precedingStatements, lastPrecedingStatementsIndex } = transformExpressions(context, expressions);
99
+ // If more than this number of temps are required to preserve execution order, we'll fall back to using the
100
+ // sparse array lib functions instead to prevent excessive locals.
101
+ const maxTemps = 2;
102
+ // Use sparse array lib if there are spreads before the last expression
103
+ // or if too many temps are needed to preserve order
104
+ const lastSpread = expressions.findIndex(e => ts.isSpreadElement(e));
105
+ if ((lastSpread >= 0 && lastSpread < expressions.length - 1) ||
106
+ countNeededTemps(context, expressions, transformedExpressions, lastPrecedingStatementsIndex) > maxTemps) {
107
+ return transformExpressionsUsingSparseArray(context, expressions, transformedExpressions, precedingStatements);
108
+ }
109
+ else {
110
+ return transformExpressionsUsingTemps(context, expressions, transformedExpressions, precedingStatements, lastPrecedingStatementsIndex);
111
+ }
112
+ }
113
+ exports.transformExpressionList = transformExpressionList;
114
+ // Transforms a series of expressions while maintaining execution order
115
+ function transformOrderedExpressions(context, expressions) {
116
+ const { transformedExpressions, precedingStatements, lastPrecedingStatementsIndex } = transformExpressions(context, expressions);
117
+ return transformExpressionsUsingTemps(context, expressions, transformedExpressions, precedingStatements, lastPrecedingStatementsIndex);
118
+ }
119
+ exports.transformOrderedExpressions = transformOrderedExpressions;
120
+ //# sourceMappingURL=expression-list.js.map