typescript-to-lua 1.1.1 → 1.3.2

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 (139) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/LuaAST.d.ts +1 -0
  3. package/dist/LuaAST.js +10 -2
  4. package/dist/LuaLib.d.ts +4 -3
  5. package/dist/LuaLib.js +6 -5
  6. package/dist/LuaPrinter.d.ts +11 -4
  7. package/dist/LuaPrinter.js +58 -15
  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 +21 -13
  16. package/dist/lualib/CloneDescriptor.lua +7 -7
  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/MathSign.lua +8 -0
  26. package/dist/lualib/Number.lua +1 -1
  27. package/dist/lualib/NumberIsFinite.lua +1 -1
  28. package/dist/lualib/NumberToString.lua +3 -5
  29. package/dist/lualib/ObjectDefineProperty.lua +40 -8
  30. package/dist/lualib/ParseFloat.lua +9 -5
  31. package/dist/lualib/ParseInt.lua +15 -3
  32. package/dist/lualib/Promise.lua +28 -24
  33. package/dist/lualib/Set.lua +119 -123
  34. package/dist/lualib/SetDescriptor.lua +7 -1
  35. package/dist/lualib/SourceMapTraceBack.lua +1 -1
  36. package/dist/lualib/SparseArrayNew.lua +5 -0
  37. package/dist/lualib/SparseArrayPush.lua +9 -0
  38. package/dist/lualib/SparseArraySpread.lua +4 -0
  39. package/dist/lualib/StringAccess.lua +1 -1
  40. package/dist/lualib/StringCharCodeAt.lua +1 -1
  41. package/dist/lualib/StringEndsWith.lua +2 -2
  42. package/dist/lualib/StringPadEnd.lua +2 -2
  43. package/dist/lualib/StringPadStart.lua +2 -2
  44. package/dist/lualib/StringReplace.lua +7 -1
  45. package/dist/lualib/StringSlice.lua +2 -2
  46. package/dist/lualib/StringSplit.lua +3 -3
  47. package/dist/lualib/StringStartsWith.lua +1 -1
  48. package/dist/lualib/StringSubstr.lua +1 -1
  49. package/dist/lualib/StringSubstring.lua +2 -2
  50. package/dist/lualib/Symbol.lua +3 -5
  51. package/dist/lualib/WeakMap.lua +40 -42
  52. package/dist/lualib/WeakSet.lua +36 -38
  53. package/dist/lualib/lualib_bundle.lua +804 -735
  54. package/dist/transformation/builtins/array.js +1 -2
  55. package/dist/transformation/builtins/function.js +6 -4
  56. package/dist/transformation/builtins/index.d.ts +1 -1
  57. package/dist/transformation/builtins/index.js +41 -6
  58. package/dist/transformation/builtins/math.js +7 -1
  59. package/dist/transformation/builtins/object.d.ts +2 -1
  60. package/dist/transformation/builtins/object.js +1 -2
  61. package/dist/transformation/builtins/string.js +1 -2
  62. package/dist/transformation/context/context.d.ts +12 -0
  63. package/dist/transformation/context/context.js +102 -3
  64. package/dist/transformation/utils/diagnostics.d.ts +9 -6
  65. package/dist/transformation/utils/diagnostics.js +4 -3
  66. package/dist/transformation/utils/language-extensions.d.ts +2 -0
  67. package/dist/transformation/utils/language-extensions.js +7 -1
  68. package/dist/transformation/utils/lua-ast.d.ts +0 -1
  69. package/dist/transformation/utils/lua-ast.js +14 -23
  70. package/dist/transformation/utils/preceding-statements.d.ts +3 -0
  71. package/dist/transformation/utils/preceding-statements.js +11 -0
  72. package/dist/transformation/utils/safe-names.d.ts +4 -2
  73. package/dist/transformation/utils/safe-names.js +11 -5
  74. package/dist/transformation/utils/scope.d.ts +3 -1
  75. package/dist/transformation/utils/scope.js +9 -1
  76. package/dist/transformation/utils/typescript/index.d.ts +1 -0
  77. package/dist/transformation/utils/typescript/index.js +16 -1
  78. package/dist/transformation/utils/typescript/types.d.ts +3 -1
  79. package/dist/transformation/utils/typescript/types.js +40 -13
  80. package/dist/transformation/visitors/access.d.ts +3 -0
  81. package/dist/transformation/visitors/access.js +63 -36
  82. package/dist/transformation/visitors/binary-expression/assignments.d.ts +3 -2
  83. package/dist/transformation/visitors/binary-expression/assignments.js +66 -57
  84. package/dist/transformation/visitors/binary-expression/compound.d.ts +5 -4
  85. package/dist/transformation/visitors/binary-expression/compound.js +66 -68
  86. package/dist/transformation/visitors/binary-expression/destructuring-assignments.d.ts +2 -2
  87. package/dist/transformation/visitors/binary-expression/destructuring-assignments.js +54 -25
  88. package/dist/transformation/visitors/binary-expression/index.d.ts +3 -1
  89. package/dist/transformation/visitors/binary-expression/index.js +65 -23
  90. package/dist/transformation/visitors/call.d.ts +3 -2
  91. package/dist/transformation/visitors/call.js +95 -141
  92. package/dist/transformation/visitors/class/index.js +5 -8
  93. package/dist/transformation/visitors/class/members/constructor.js +3 -1
  94. package/dist/transformation/visitors/class/new.js +4 -6
  95. package/dist/transformation/visitors/conditional.js +30 -37
  96. package/dist/transformation/visitors/delete.js +4 -0
  97. package/dist/transformation/visitors/errors.js +11 -9
  98. package/dist/transformation/visitors/expression-list.d.ts +7 -0
  99. package/dist/transformation/visitors/expression-list.js +120 -0
  100. package/dist/transformation/visitors/expression-statement.d.ts +3 -1
  101. package/dist/transformation/visitors/expression-statement.js +21 -19
  102. package/dist/transformation/visitors/function.d.ts +2 -0
  103. package/dist/transformation/visitors/function.js +63 -13
  104. package/dist/transformation/visitors/identifier.js +5 -0
  105. package/dist/transformation/visitors/language-extensions/index.d.ts +4 -0
  106. package/dist/transformation/visitors/language-extensions/index.js +17 -0
  107. package/dist/transformation/visitors/language-extensions/operators.d.ts +1 -1
  108. package/dist/transformation/visitors/language-extensions/operators.js +7 -2
  109. package/dist/transformation/visitors/language-extensions/pairsIterable.d.ts +5 -0
  110. package/dist/transformation/visitors/language-extensions/pairsIterable.js +53 -0
  111. package/dist/transformation/visitors/language-extensions/table.d.ts +1 -8
  112. package/dist/transformation/visitors/language-extensions/table.js +55 -53
  113. package/dist/transformation/visitors/literal.js +69 -16
  114. package/dist/transformation/visitors/loops/do-while.js +34 -8
  115. package/dist/transformation/visitors/loops/for-of.js +4 -0
  116. package/dist/transformation/visitors/loops/for.js +25 -5
  117. package/dist/transformation/visitors/loops/utils.d.ts +1 -0
  118. package/dist/transformation/visitors/loops/utils.js +19 -3
  119. package/dist/transformation/visitors/modules/export.js +1 -1
  120. package/dist/transformation/visitors/namespace.js +1 -1
  121. package/dist/transformation/visitors/optional-chaining.d.ts +16 -0
  122. package/dist/transformation/visitors/optional-chaining.js +172 -0
  123. package/dist/transformation/visitors/sourceFile.js +4 -1
  124. package/dist/transformation/visitors/spread.js +14 -7
  125. package/dist/transformation/visitors/switch.js +29 -9
  126. package/dist/transformation/visitors/template.js +5 -2
  127. package/dist/transformation/visitors/typeof.js +3 -1
  128. package/dist/transformation/visitors/variable-declaration.js +37 -8
  129. package/dist/transformation/visitors/void.d.ts +1 -4
  130. package/dist/transformation/visitors/void.js +7 -12
  131. package/dist/transpilation/transformers.js +4 -1
  132. package/dist/transpilation/utils.js +2 -1
  133. package/language-extensions/index.d.ts +11 -1
  134. package/package.json +5 -5
  135. package/dist/lualib/OptionalChainAccess.lua +0 -6
  136. package/dist/lualib/OptionalFunctionCall.lua +0 -6
  137. package/dist/lualib/OptionalMethodCall.lua +0 -11
  138. package/dist/transformation/utils/transform.d.ts +0 -8
  139. package/dist/transformation/utils/transform.js +0 -14
@@ -1,35 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformCompoundAssignmentStatement = exports.transformCompoundAssignmentExpression = exports.transformCompoundAssignment = exports.unwrapCompoundAssignmentToken = exports.isCompoundAssignmentToken = exports.parseAccessExpressionWithEvaluationEffects = void 0;
3
+ exports.transformCompoundAssignmentStatement = exports.transformCompoundAssignmentExpression = exports.transformCompoundAssignment = exports.unwrapCompoundAssignmentToken = exports.isCompoundAssignmentToken = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../../LuaAST");
6
6
  const utils_1 = require("../../../utils");
7
- const transform_1 = require("../../utils/transform");
8
- const typescript_1 = require("../../utils/typescript");
7
+ const preceding_statements_1 = require("../../utils/preceding-statements");
9
8
  const binary_expression_1 = require("../binary-expression");
10
9
  const assignments_1 = require("./assignments");
11
- // If expression is property/element access with possible effects from being evaluated, returns separated object and index expressions.
12
- function parseAccessExpressionWithEvaluationEffects(context, node) {
13
- if (ts.isElementAccessExpression(node) &&
14
- ((0, typescript_1.isExpressionWithEvaluationEffect)(node.expression) || (0, typescript_1.isExpressionWithEvaluationEffect)(node.argumentExpression))) {
15
- const type = context.checker.getTypeAtLocation(node.expression);
16
- if ((0, typescript_1.isArrayType)(context, type)) {
17
- // Offset arrays by one
18
- const oneLit = ts.factory.createNumericLiteral("1");
19
- const exp = ts.factory.createParenthesizedExpression(node.argumentExpression);
20
- const addExp = ts.factory.createBinaryExpression(exp, ts.SyntaxKind.PlusToken, oneLit);
21
- return [node.expression, addExp];
22
- }
23
- else {
24
- return [node.expression, node.argumentExpression];
25
- }
26
- }
27
- else if (ts.isPropertyAccessExpression(node) && (0, typescript_1.isExpressionWithEvaluationEffect)(node.expression)) {
28
- return [node.expression, ts.factory.createStringLiteral(node.name.text)];
29
- }
30
- return [];
10
+ function isLuaExpressionWithSideEffect(expression) {
11
+ return !(lua.isLiteral(expression) || lua.isIdentifier(expression));
12
+ }
13
+ function shouldCacheTableIndexExpressions(expression, rightPrecedingStatements) {
14
+ return (isLuaExpressionWithSideEffect(expression.table) ||
15
+ isLuaExpressionWithSideEffect(expression.index) ||
16
+ rightPrecedingStatements.length > 0);
31
17
  }
32
- exports.parseAccessExpressionWithEvaluationEffects = parseAccessExpressionWithEvaluationEffects;
33
18
  const compoundToAssignmentTokens = {
34
19
  [ts.SyntaxKind.BarEqualsToken]: ts.SyntaxKind.BarToken,
35
20
  [ts.SyntaxKind.PlusEqualsToken]: ts.SyntaxKind.PlusToken,
@@ -53,69 +38,80 @@ const unwrapCompoundAssignmentToken = (token) => compoundToAssignmentTokens[toke
53
38
  exports.unwrapCompoundAssignmentToken = unwrapCompoundAssignmentToken;
54
39
  function transformCompoundAssignment(context, expression, lhs, rhs, operator, isPostfix) {
55
40
  const left = (0, utils_1.cast)(context.transformExpression(lhs), lua.isAssignmentLeftHandSideExpression);
56
- const right = context.transformExpression(rhs);
57
- const [objExpression, indexExpression] = parseAccessExpressionWithEvaluationEffects(context, lhs);
58
- if (objExpression && indexExpression) {
41
+ const [rightPrecedingStatements, right] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(rhs));
42
+ if (lua.isTableIndexExpression(left) && shouldCacheTableIndexExpressions(left, rightPrecedingStatements)) {
59
43
  // Complex property/element accesses need to cache object/index expressions to avoid repeating side-effects
60
44
  // local __obj, __index = ${objExpression}, ${indexExpression};
61
- const obj = lua.createIdentifier("____obj");
62
- const index = lua.createIdentifier("____index");
63
- const objAndIndexDeclaration = lua.createVariableDeclarationStatement([obj, index], [context.transformExpression(objExpression), context.transformExpression(indexExpression)]);
45
+ const obj = context.createTempNameForLuaExpression(left.table);
46
+ const index = context.createTempNameForLuaExpression(left.index);
47
+ const objAndIndexDeclaration = lua.createVariableDeclarationStatement([obj, index], [left.table, left.index]);
64
48
  const accessExpression = lua.createTableIndexExpression(obj, index);
65
- const tmp = lua.createIdentifier("____tmp");
66
- let tmpDeclaration;
67
- let assignStatement;
49
+ const tmp = context.createTempNameForLuaExpression(left);
68
50
  if (isPostfix) {
69
51
  // local ____tmp = ____obj[____index];
70
52
  // ____obj[____index] = ____tmp ${replacementOperator} ${right};
71
- tmpDeclaration = lua.createVariableDeclarationStatement(tmp, accessExpression);
72
- const operatorExpression = (0, binary_expression_1.transformBinaryOperation)(context, tmp, right, operator, expression);
73
- assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression);
53
+ // return ____tmp
54
+ const tmpDeclaration = lua.createVariableDeclarationStatement(tmp, accessExpression);
55
+ const [precedingStatements, operatorExpression] = (0, binary_expression_1.transformBinaryOperation)(context, tmp, right, rightPrecedingStatements, operator, expression);
56
+ const assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression);
57
+ return {
58
+ statements: [objAndIndexDeclaration, ...precedingStatements, tmpDeclaration, assignStatement],
59
+ result: tmp,
60
+ };
74
61
  }
75
62
  else {
76
63
  // local ____tmp = ____obj[____index] ${replacementOperator} ${right};
77
64
  // ____obj[____index] = ____tmp;
78
- const operatorExpression = (0, binary_expression_1.transformBinaryOperation)(context, accessExpression, right, operator, expression);
79
- tmpDeclaration = lua.createVariableDeclarationStatement(tmp, operatorExpression);
80
- assignStatement = lua.createAssignmentStatement(accessExpression, tmp);
65
+ // return ____tmp
66
+ const [precedingStatements, operatorExpression] = (0, binary_expression_1.transformBinaryOperation)(context, accessExpression, right, rightPrecedingStatements, operator, expression);
67
+ const tmpDeclaration = lua.createVariableDeclarationStatement(tmp, operatorExpression);
68
+ const assignStatement = lua.createAssignmentStatement(accessExpression, tmp);
69
+ return {
70
+ statements: [objAndIndexDeclaration, ...precedingStatements, tmpDeclaration, assignStatement],
71
+ result: tmp,
72
+ };
81
73
  }
82
- // return ____tmp
83
- return { statements: [objAndIndexDeclaration, tmpDeclaration, assignStatement], result: tmp };
84
74
  }
85
75
  else if (isPostfix) {
86
76
  // Postfix expressions need to cache original value in temp
87
77
  // local ____tmp = ${left};
88
78
  // ${left} = ____tmp ${replacementOperator} ${right};
89
79
  // return ____tmp
90
- const tmpIdentifier = lua.createIdentifier("____tmp");
80
+ const tmpIdentifier = context.createTempNameForLuaExpression(left);
91
81
  const tmpDeclaration = lua.createVariableDeclarationStatement(tmpIdentifier, left);
92
- const operatorExpression = (0, binary_expression_1.transformBinaryOperation)(context, tmpIdentifier, right, operator, expression);
93
- const assignStatements = (0, assignments_1.transformAssignment)(context, lhs, operatorExpression);
94
- return { statements: [tmpDeclaration, ...assignStatements], result: tmpIdentifier };
82
+ const [precedingStatements, operatorExpression] = (0, binary_expression_1.transformBinaryOperation)(context, tmpIdentifier, right, rightPrecedingStatements, operator, expression);
83
+ const assignStatements = (0, assignments_1.transformAssignmentWithRightPrecedingStatements)(context, lhs, operatorExpression, rightPrecedingStatements);
84
+ return { statements: [tmpDeclaration, ...precedingStatements, ...assignStatements], result: tmpIdentifier };
95
85
  }
96
86
  else if (ts.isPropertyAccessExpression(lhs) || ts.isElementAccessExpression(lhs)) {
97
87
  // Simple property/element access expressions need to cache in temp to avoid double-evaluation
98
88
  // local ____tmp = ${left} ${replacementOperator} ${right};
99
89
  // ${left} = ____tmp;
100
90
  // return ____tmp
101
- const tmpIdentifier = lua.createIdentifier("____tmp");
102
- const operatorExpression = (0, binary_expression_1.transformBinaryOperation)(context, left, right, operator, expression);
91
+ const tmpIdentifier = context.createTempNameForLuaExpression(left);
92
+ const [precedingStatements, operatorExpression] = (0, binary_expression_1.transformBinaryOperation)(context, left, right, rightPrecedingStatements, operator, expression);
103
93
  const tmpDeclaration = lua.createVariableDeclarationStatement(tmpIdentifier, operatorExpression);
104
- const assignStatements = (0, assignments_1.transformAssignment)(context, lhs, tmpIdentifier);
105
94
  if (isSetterSkippingCompoundAssignmentOperator(operator)) {
106
95
  const statements = [
107
96
  tmpDeclaration,
108
- ...transformSetterSkippingCompoundAssignment(context, tmpIdentifier, operator, rhs),
97
+ ...transformSetterSkippingCompoundAssignment(tmpIdentifier, operator, right, precedingStatements),
109
98
  ];
110
99
  return { statements, result: tmpIdentifier };
111
100
  }
101
+ const assignStatements = (0, assignments_1.transformAssignmentWithRightPrecedingStatements)(context, lhs, tmpIdentifier, precedingStatements);
112
102
  return { statements: [tmpDeclaration, ...assignStatements], result: tmpIdentifier };
113
103
  }
114
104
  else {
105
+ if (rightPrecedingStatements.length > 0 && isSetterSkippingCompoundAssignmentOperator(operator)) {
106
+ return {
107
+ statements: transformSetterSkippingCompoundAssignment(left, operator, right, rightPrecedingStatements),
108
+ result: left,
109
+ };
110
+ }
115
111
  // Simple expressions
116
- // ${left} = ${right}; return ${right}
117
- const operatorExpression = (0, binary_expression_1.transformBinaryOperation)(context, left, right, operator, expression);
118
- const statements = (0, assignments_1.transformAssignment)(context, lhs, operatorExpression);
112
+ // ${left} = ${left} ${operator} ${right}
113
+ const [precedingStatements, operatorExpression] = (0, binary_expression_1.transformBinaryOperation)(context, left, right, rightPrecedingStatements, operator, expression);
114
+ const statements = (0, assignments_1.transformAssignmentWithRightPrecedingStatements)(context, lhs, operatorExpression, precedingStatements);
119
115
  return { statements, result: left };
120
116
  }
121
117
  }
@@ -123,40 +119,42 @@ exports.transformCompoundAssignment = transformCompoundAssignment;
123
119
  function transformCompoundAssignmentExpression(context, expression,
124
120
  // TODO: Change type to ts.LeftHandSideExpression?
125
121
  lhs, rhs, operator, isPostfix) {
126
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => transformCompoundAssignment(context, expression, lhs, rhs, operator, isPostfix), expression);
122
+ const { statements, result } = transformCompoundAssignment(context, expression, lhs, rhs, operator, isPostfix);
123
+ context.addPrecedingStatements(statements);
124
+ return result;
127
125
  }
128
126
  exports.transformCompoundAssignmentExpression = transformCompoundAssignmentExpression;
129
127
  function transformCompoundAssignmentStatement(context, node, lhs, rhs, operator) {
130
128
  const left = (0, utils_1.cast)(context.transformExpression(lhs), lua.isAssignmentLeftHandSideExpression);
131
- const right = context.transformExpression(rhs);
132
- const [objExpression, indexExpression] = parseAccessExpressionWithEvaluationEffects(context, lhs);
133
- if (objExpression && indexExpression) {
129
+ let [rightPrecedingStatements, right] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(rhs));
130
+ if (lua.isTableIndexExpression(left) && shouldCacheTableIndexExpressions(left, rightPrecedingStatements)) {
134
131
  // Complex property/element accesses need to cache object/index expressions to avoid repeating side-effects
135
132
  // local __obj, __index = ${objExpression}, ${indexExpression};
136
133
  // ____obj[____index] = ____obj[____index] ${replacementOperator} ${right};
137
- const obj = lua.createIdentifier("____obj");
138
- const index = lua.createIdentifier("____index");
139
- const objAndIndexDeclaration = lua.createVariableDeclarationStatement([obj, index], [context.transformExpression(objExpression), context.transformExpression(indexExpression)]);
134
+ const obj = context.createTempNameForLuaExpression(left.table);
135
+ const index = context.createTempNameForLuaExpression(left.index);
136
+ const objAndIndexDeclaration = lua.createVariableDeclarationStatement([obj, index], [left.table, left.index]);
140
137
  const accessExpression = lua.createTableIndexExpression(obj, index);
141
138
  if (isSetterSkippingCompoundAssignmentOperator(operator)) {
142
139
  return [
143
140
  objAndIndexDeclaration,
144
- ...transformSetterSkippingCompoundAssignment(context, accessExpression, operator, rhs, node),
141
+ ...transformSetterSkippingCompoundAssignment(accessExpression, operator, right, rightPrecedingStatements, node),
145
142
  ];
146
143
  }
147
- const operatorExpression = (0, binary_expression_1.transformBinaryOperation)(context, accessExpression, right, operator, node);
144
+ let operatorExpression;
145
+ [rightPrecedingStatements, operatorExpression] = (0, binary_expression_1.transformBinaryOperation)(context, accessExpression, right, rightPrecedingStatements, operator, node);
148
146
  const assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression);
149
- return [objAndIndexDeclaration, assignStatement];
147
+ return [objAndIndexDeclaration, ...rightPrecedingStatements, assignStatement];
150
148
  }
151
149
  else {
152
150
  if (isSetterSkippingCompoundAssignmentOperator(operator)) {
153
- const luaLhs = context.transformExpression(lhs);
154
- return transformSetterSkippingCompoundAssignment(context, luaLhs, operator, rhs, node);
151
+ return transformSetterSkippingCompoundAssignment(left, operator, right, rightPrecedingStatements, node);
155
152
  }
156
153
  // Simple statements
157
154
  // ${left} = ${left} ${replacementOperator} ${right}
158
- const operatorExpression = (0, binary_expression_1.transformBinaryOperation)(context, left, right, operator, node);
159
- return (0, assignments_1.transformAssignment)(context, lhs, operatorExpression);
155
+ let operatorExpression;
156
+ [rightPrecedingStatements, operatorExpression] = (0, binary_expression_1.transformBinaryOperation)(context, left, right, rightPrecedingStatements, operator, node);
157
+ return (0, assignments_1.transformAssignmentWithRightPrecedingStatements)(context, lhs, operatorExpression, rightPrecedingStatements);
160
158
  }
161
159
  }
162
160
  exports.transformCompoundAssignmentStatement = transformCompoundAssignmentStatement;
@@ -165,7 +163,7 @@ function isSetterSkippingCompoundAssignmentOperator(operator) {
165
163
  operator === ts.SyntaxKind.BarBarToken ||
166
164
  operator === ts.SyntaxKind.QuestionQuestionToken);
167
165
  }
168
- function transformSetterSkippingCompoundAssignment(context, lhs, operator, rhs, node) {
166
+ function transformSetterSkippingCompoundAssignment(lhs, operator, right, rightPrecedingStatements, node) {
169
167
  // These assignments have the form 'if x then y = z', figure out what condition x is first.
170
168
  let condition;
171
169
  if (operator === ts.SyntaxKind.AmpersandAmpersandToken) {
@@ -182,7 +180,7 @@ function transformSetterSkippingCompoundAssignment(context, lhs, operator, rhs,
182
180
  }
183
181
  // if condition then lhs = rhs end
184
182
  return [
185
- lua.createIfStatement(condition, lua.createBlock([lua.createAssignmentStatement(lhs, context.transformExpression(rhs))]), undefined, node),
183
+ lua.createIfStatement(condition, lua.createBlock([...rightPrecedingStatements, lua.createAssignmentStatement(lhs, right, node)]), undefined, node),
186
184
  ];
187
185
  }
188
186
  //# sourceMappingURL=compound.js.map
@@ -2,5 +2,5 @@ import * as ts from "typescript";
2
2
  import * as lua from "../../../LuaAST";
3
3
  import { TransformationContext } from "../../context";
4
4
  export declare function isArrayLength(context: TransformationContext, expression: ts.Expression): expression is ts.PropertyAccessExpression | ts.ElementAccessExpression;
5
- export declare function transformDestructuringAssignment(context: TransformationContext, node: ts.DestructuringAssignment, root: lua.Expression): lua.Statement[];
6
- export declare function transformAssignmentPattern(context: TransformationContext, node: ts.AssignmentPattern, root: lua.Expression): lua.Statement[];
5
+ export declare function transformDestructuringAssignment(context: TransformationContext, node: ts.DestructuringAssignment, root: lua.Expression, rightHasPrecedingStatements: boolean): lua.Statement[];
6
+ export declare function transformAssignmentPattern(context: TransformationContext, node: ts.AssignmentPattern, root: lua.Expression, rightHasPrecedingStatements: boolean): lua.Statement[];
@@ -2,10 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformAssignmentPattern = exports.transformDestructuringAssignment = exports.isArrayLength = void 0;
4
4
  const ts = require("typescript");
5
+ const _1 = require(".");
5
6
  const lua = require("../../../LuaAST");
6
7
  const utils_1 = require("../../../utils");
7
8
  const lualib_1 = require("../../utils/lualib");
9
+ const preceding_statements_1 = require("../../utils/preceding-statements");
8
10
  const typescript_1 = require("../../utils/typescript");
11
+ const expression_list_1 = require("../expression-list");
9
12
  const literal_1 = require("../literal");
10
13
  const assignments_1 = require("./assignments");
11
14
  function isArrayLength(context, expression) {
@@ -24,32 +27,34 @@ function isArrayLength(context, expression) {
24
27
  return name === "length";
25
28
  }
26
29
  exports.isArrayLength = isArrayLength;
27
- function transformDestructuringAssignment(context, node, root) {
28
- return transformAssignmentPattern(context, node.left, root);
30
+ function transformDestructuringAssignment(context, node, root, rightHasPrecedingStatements) {
31
+ return transformAssignmentPattern(context, node.left, root, rightHasPrecedingStatements);
29
32
  }
30
33
  exports.transformDestructuringAssignment = transformDestructuringAssignment;
31
- function transformAssignmentPattern(context, node, root) {
34
+ function transformAssignmentPattern(context, node, root, rightHasPrecedingStatements) {
32
35
  switch (node.kind) {
33
36
  case ts.SyntaxKind.ObjectLiteralExpression:
34
- return transformObjectLiteralAssignmentPattern(context, node, root);
37
+ return transformObjectLiteralAssignmentPattern(context, node, root, rightHasPrecedingStatements);
35
38
  case ts.SyntaxKind.ArrayLiteralExpression:
36
- return transformArrayLiteralAssignmentPattern(context, node, root);
39
+ return transformArrayLiteralAssignmentPattern(context, node, root, rightHasPrecedingStatements);
37
40
  }
38
41
  }
39
42
  exports.transformAssignmentPattern = transformAssignmentPattern;
40
- function transformArrayLiteralAssignmentPattern(context, node, root) {
43
+ function transformArrayLiteralAssignmentPattern(context, node, root, rightHasPrecedingStatements) {
41
44
  return node.elements.flatMap((element, index) => {
42
45
  const indexedRoot = lua.createTableIndexExpression(root, lua.createNumericLiteral(index + 1), element);
43
46
  switch (element.kind) {
44
47
  case ts.SyntaxKind.ObjectLiteralExpression:
45
- return transformObjectLiteralAssignmentPattern(context, element, indexedRoot);
48
+ return transformObjectLiteralAssignmentPattern(context, element, indexedRoot, rightHasPrecedingStatements);
46
49
  case ts.SyntaxKind.ArrayLiteralExpression:
47
- return transformArrayLiteralAssignmentPattern(context, element, indexedRoot);
50
+ return transformArrayLiteralAssignmentPattern(context, element, indexedRoot, rightHasPrecedingStatements);
48
51
  case ts.SyntaxKind.BinaryExpression:
49
- const assignedVariable = lua.createIdentifier("____bindingAssignmentValue");
52
+ const assignedVariable = context.createTempNameForLuaExpression(indexedRoot);
50
53
  const assignedVariableDeclaration = lua.createVariableDeclarationStatement(assignedVariable, indexedRoot);
51
54
  const nilCondition = lua.createBinaryExpression(assignedVariable, lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator);
52
- const defaultAssignmentStatements = (0, assignments_1.transformAssignment)(context, element.left, context.transformExpression(element.right));
55
+ const [defaultPrecedingStatements, defaultAssignmentStatements] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, assignments_1.transformAssignment)(context, element.left, context.transformExpression(element.right)));
56
+ // Keep preceding statements inside if block
57
+ defaultAssignmentStatements.unshift(...defaultPrecedingStatements);
53
58
  const elseAssignmentStatements = (0, assignments_1.transformAssignment)(context, element.left, assignedVariable);
54
59
  const ifBlock = lua.createBlock(defaultAssignmentStatements);
55
60
  const elseBlock = lua.createBlock(elseAssignmentStatements);
@@ -58,14 +63,16 @@ function transformArrayLiteralAssignmentPattern(context, node, root) {
58
63
  case ts.SyntaxKind.Identifier:
59
64
  case ts.SyntaxKind.PropertyAccessExpression:
60
65
  case ts.SyntaxKind.ElementAccessExpression:
61
- return (0, assignments_1.transformAssignment)(context, element, indexedRoot);
66
+ const [precedingStatements, statements] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, assignments_1.transformAssignment)(context, element, indexedRoot, rightHasPrecedingStatements));
67
+ return [...precedingStatements, ...statements]; // Keep preceding statements in order
62
68
  case ts.SyntaxKind.SpreadElement:
63
69
  if (index !== node.elements.length - 1) {
64
70
  // TypeScript error
65
71
  return [];
66
72
  }
67
73
  const restElements = (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.ArraySlice, undefined, root, lua.createNumericLiteral(index));
68
- return (0, assignments_1.transformAssignment)(context, element.expression, restElements);
74
+ const [spreadPrecedingStatements, spreadStatements] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, assignments_1.transformAssignment)(context, element.expression, restElements, rightHasPrecedingStatements));
75
+ return [...spreadPrecedingStatements, ...spreadStatements]; // Keep preceding statements in order
69
76
  case ts.SyntaxKind.OmittedExpression:
70
77
  return [];
71
78
  default:
@@ -74,7 +81,7 @@ function transformArrayLiteralAssignmentPattern(context, node, root) {
74
81
  }
75
82
  });
76
83
  }
77
- function transformObjectLiteralAssignmentPattern(context, node, root) {
84
+ function transformObjectLiteralAssignmentPattern(context, node, root, rightHasPrecedingStatements) {
78
85
  const result = [];
79
86
  for (const property of node.properties) {
80
87
  switch (property.kind) {
@@ -82,7 +89,7 @@ function transformObjectLiteralAssignmentPattern(context, node, root) {
82
89
  result.push(...transformShorthandPropertyAssignment(context, property, root));
83
90
  break;
84
91
  case ts.SyntaxKind.PropertyAssignment:
85
- result.push(...transformPropertyAssignment(context, property, root));
92
+ result.push(...transformPropertyAssignment(context, property, root, rightHasPrecedingStatements));
86
93
  break;
87
94
  case ts.SyntaxKind.SpreadAssignment:
88
95
  result.push(...transformSpreadAssignment(context, property, root, node.properties));
@@ -115,29 +122,51 @@ function transformShorthandPropertyAssignment(context, node, root) {
115
122
  }
116
123
  return result;
117
124
  }
118
- function transformPropertyAssignment(context, node, root) {
125
+ function transformPropertyAssignment(context, node, root, rightHasPrecedingStatements) {
119
126
  const result = [];
120
127
  if ((0, typescript_1.isAssignmentPattern)(node.initializer)) {
121
128
  const propertyAccessString = (0, literal_1.transformPropertyName)(context, node.name);
122
129
  const newRootAccess = lua.createTableIndexExpression(root, propertyAccessString);
123
130
  if (ts.isObjectLiteralExpression(node.initializer)) {
124
- return transformObjectLiteralAssignmentPattern(context, node.initializer, newRootAccess);
131
+ return transformObjectLiteralAssignmentPattern(context, node.initializer, newRootAccess, rightHasPrecedingStatements);
125
132
  }
126
133
  if (ts.isArrayLiteralExpression(node.initializer)) {
127
- return transformArrayLiteralAssignmentPattern(context, node.initializer, newRootAccess);
134
+ return transformArrayLiteralAssignmentPattern(context, node.initializer, newRootAccess, rightHasPrecedingStatements);
128
135
  }
129
136
  }
130
- const leftExpression = ts.isBinaryExpression(node.initializer) ? node.initializer.left : node.initializer;
131
- const variableToExtract = (0, literal_1.transformPropertyName)(context, node.name);
137
+ context.pushPrecedingStatements();
138
+ let variableToExtract = (0, literal_1.transformPropertyName)(context, node.name);
139
+ // Must be evaluated before left's preceding statements
140
+ variableToExtract = (0, expression_list_1.moveToPrecedingTemp)(context, variableToExtract, node.name);
132
141
  const extractingExpression = lua.createTableIndexExpression(root, variableToExtract);
133
- const destructureAssignmentStatements = (0, assignments_1.transformAssignment)(context, leftExpression, extractingExpression);
134
- result.push(...destructureAssignmentStatements);
142
+ let destructureAssignmentStatements;
135
143
  if (ts.isBinaryExpression(node.initializer)) {
136
- const assignmentLeftHandSide = context.transformExpression(node.initializer.left);
137
- const nilCondition = lua.createBinaryExpression(assignmentLeftHandSide, lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator);
138
- const ifBlock = lua.createBlock((0, assignments_1.transformAssignmentStatement)(context, node.initializer));
139
- result.push(lua.createIfStatement(nilCondition, ifBlock, undefined, node));
144
+ if (ts.isPropertyAccessExpression(node.initializer.left) ||
145
+ ts.isElementAccessExpression(node.initializer.left)) {
146
+ // Access expressions need their table and index expressions cached to preserve execution order
147
+ const left = (0, utils_1.cast)(context.transformExpression(node.initializer.left), lua.isTableIndexExpression);
148
+ const rightExpression = node.initializer.right;
149
+ const [defaultPrecedingStatements, defaultExpression] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(rightExpression));
150
+ const tableTemp = context.createTempNameForLuaExpression(left.table);
151
+ const indexTemp = context.createTempNameForLuaExpression(left.index);
152
+ const tempsDeclaration = lua.createVariableDeclarationStatement([tableTemp, indexTemp], [left.table, left.index]);
153
+ // obj[index] = extractingExpression ?? defaultExpression
154
+ const [rightPrecedingStatements, rhs] = (0, _1.transformBinaryOperation)(context, extractingExpression, defaultExpression, defaultPrecedingStatements, ts.SyntaxKind.QuestionQuestionToken, node.initializer);
155
+ const assignStatement = lua.createAssignmentStatement(lua.createTableIndexExpression(tableTemp, indexTemp), rhs);
156
+ destructureAssignmentStatements = [tempsDeclaration, ...rightPrecedingStatements, assignStatement];
157
+ }
158
+ else {
159
+ const assignmentLeftHandSide = context.transformExpression(node.initializer.left);
160
+ const nilCondition = lua.createBinaryExpression(assignmentLeftHandSide, lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator);
161
+ const ifBlock = lua.createBlock((0, assignments_1.transformAssignmentStatement)(context, node.initializer));
162
+ destructureAssignmentStatements = [lua.createIfStatement(nilCondition, ifBlock, undefined, node)];
163
+ }
164
+ }
165
+ else {
166
+ destructureAssignmentStatements = (0, assignments_1.transformAssignment)(context, node.initializer, extractingExpression, rightHasPrecedingStatements);
140
167
  }
168
+ result.push(...context.popPrecedingStatements());
169
+ result.push(...destructureAssignmentStatements);
141
170
  return result;
142
171
  }
143
172
  function transformSpreadAssignment(context, node, root, properties) {
@@ -2,8 +2,10 @@ import * as ts from "typescript";
2
2
  import * as lua from "../../../LuaAST";
3
3
  import { FunctionVisitor, TransformationContext } from "../../context";
4
4
  import { BitOperator } from "./bit";
5
+ declare type ShortCircuitOperator = ts.SyntaxKind.AmpersandAmpersandToken | ts.SyntaxKind.BarBarToken | ts.SyntaxKind.QuestionQuestionToken;
5
6
  declare type SimpleOperator = ts.AdditiveOperatorOrHigher | Exclude<ts.RelationalOperator, ts.SyntaxKind.InstanceOfKeyword | ts.SyntaxKind.InKeyword> | ts.EqualityOperator | ts.LogicalOperator;
6
- export declare function transformBinaryOperation(context: TransformationContext, left: lua.Expression, right: lua.Expression, operator: BitOperator | SimpleOperator | ts.SyntaxKind.QuestionQuestionToken, node: ts.Node): lua.Expression;
7
+ export declare function createShortCircuitBinaryExpressionPrecedingStatements(context: TransformationContext, lhs: lua.Expression, rhs: lua.Expression, rightPrecedingStatements: lua.Statement[], operator: ShortCircuitOperator, node?: ts.BinaryExpression): [lua.Statement[], lua.Expression];
8
+ export declare function transformBinaryOperation(context: TransformationContext, left: lua.Expression, right: lua.Expression, rightPrecedingStatements: lua.Statement[], operator: BitOperator | SimpleOperator | ts.SyntaxKind.QuestionQuestionToken, node: ts.Node): [lua.Statement[], lua.Expression];
7
9
  export declare const transformBinaryExpression: FunctionVisitor<ts.BinaryExpression>;
8
10
  export declare function transformBinaryExpressionStatement(context: TransformationContext, node: ts.ExpressionStatement): lua.Statement[] | lua.Statement | undefined;
9
11
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformBinaryExpressionStatement = exports.transformBinaryExpression = exports.transformBinaryOperation = void 0;
3
+ exports.transformBinaryExpressionStatement = exports.transformBinaryExpression = exports.transformBinaryOperation = exports.createShortCircuitBinaryExpressionPrecedingStatements = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../../LuaAST");
6
6
  const lua_ast_1 = require("../../utils/lua-ast");
@@ -11,7 +11,11 @@ const assignments_1 = require("./assignments");
11
11
  const bit_1 = require("./bit");
12
12
  const compound_1 = require("./compound");
13
13
  const utils_1 = require("../../../utils");
14
- const transform_1 = require("../../utils/transform");
14
+ const expression_list_1 = require("../expression-list");
15
+ const preceding_statements_1 = require("../../utils/preceding-statements");
16
+ const isShortCircuitOperator = (value) => value === ts.SyntaxKind.AmpersandAmpersandToken ||
17
+ value === ts.SyntaxKind.BarBarToken ||
18
+ value === ts.SyntaxKind.QuestionQuestionToken;
15
19
  const simpleOperatorsToLua = {
16
20
  [ts.SyntaxKind.AmpersandAmpersandToken]: lua.SyntaxKind.AndOperator,
17
21
  [ts.SyntaxKind.BarBarToken]: lua.SyntaxKind.OrOperator,
@@ -30,13 +34,13 @@ const simpleOperatorsToLua = {
30
34
  [ts.SyntaxKind.ExclamationEqualsToken]: lua.SyntaxKind.InequalityOperator,
31
35
  [ts.SyntaxKind.ExclamationEqualsEqualsToken]: lua.SyntaxKind.InequalityOperator,
32
36
  };
33
- function transformBinaryOperation(context, left, right, operator, node) {
37
+ function transformBinaryOperationWithNoPrecedingStatements(context, left, right, operator, node) {
34
38
  if ((0, bit_1.isBitOperator)(operator)) {
35
39
  return (0, bit_1.transformBinaryBitOperation)(context, node, left, right, operator);
36
40
  }
37
41
  if (operator === ts.SyntaxKind.QuestionQuestionToken) {
38
42
  (0, utils_1.assert)(ts.isBinaryExpression(node));
39
- return transformNullishCoalescingExpression(context, node, left, right);
43
+ return transformNullishCoalescingOperationNoPrecedingStatements(context, node, left, right);
40
44
  }
41
45
  let luaOperator = simpleOperatorsToLua[operator];
42
46
  // Check if we need to use string concat operator
@@ -53,6 +57,40 @@ function transformBinaryOperation(context, left, right, operator, node) {
53
57
  }
54
58
  return lua.createBinaryExpression(left, right, luaOperator, node);
55
59
  }
60
+ function createShortCircuitBinaryExpressionPrecedingStatements(context, lhs, rhs, rightPrecedingStatements, operator, node) {
61
+ const conditionIdentifier = context.createTempNameForLuaExpression(lhs);
62
+ const assignmentStatement = lua.createVariableDeclarationStatement(conditionIdentifier, lhs, node === null || node === void 0 ? void 0 : node.left);
63
+ let condition;
64
+ switch (operator) {
65
+ case ts.SyntaxKind.BarBarToken:
66
+ condition = lua.createUnaryExpression(lua.cloneIdentifier(conditionIdentifier), lua.SyntaxKind.NotOperator, node);
67
+ break;
68
+ case ts.SyntaxKind.AmpersandAmpersandToken:
69
+ condition = lua.cloneIdentifier(conditionIdentifier);
70
+ break;
71
+ case ts.SyntaxKind.QuestionQuestionToken:
72
+ condition = lua.createBinaryExpression(lua.cloneIdentifier(conditionIdentifier), lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator, node);
73
+ break;
74
+ }
75
+ const ifStatement = lua.createIfStatement(condition, lua.createBlock([...rightPrecedingStatements, lua.createAssignmentStatement(conditionIdentifier, rhs)]), undefined, node === null || node === void 0 ? void 0 : node.left);
76
+ return [[assignmentStatement, ifStatement], conditionIdentifier];
77
+ }
78
+ exports.createShortCircuitBinaryExpressionPrecedingStatements = createShortCircuitBinaryExpressionPrecedingStatements;
79
+ function transformShortCircuitBinaryExpression(context, node, operator) {
80
+ const lhs = context.transformExpression(node.left);
81
+ const [rightPrecedingStatements, rhs] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(node.right));
82
+ return transformBinaryOperation(context, lhs, rhs, rightPrecedingStatements, operator, node);
83
+ }
84
+ function transformBinaryOperation(context, left, right, rightPrecedingStatements, operator, node) {
85
+ if (rightPrecedingStatements.length > 0 && isShortCircuitOperator(operator)) {
86
+ (0, utils_1.assert)(ts.isBinaryExpression(node));
87
+ return createShortCircuitBinaryExpressionPrecedingStatements(context, left, right, rightPrecedingStatements, operator, node);
88
+ }
89
+ return [
90
+ rightPrecedingStatements,
91
+ transformBinaryOperationWithNoPrecedingStatements(context, left, right, operator, node),
92
+ ];
93
+ }
56
94
  exports.transformBinaryOperation = transformBinaryOperation;
57
95
  const transformBinaryExpression = (node, context) => {
58
96
  const operator = node.operatorToken.kind;
@@ -83,14 +121,25 @@ const transformBinaryExpression = (node, context) => {
83
121
  return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.InstanceOf, node, lhs, rhs);
84
122
  }
85
123
  case ts.SyntaxKind.CommaToken: {
86
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => ({
87
- statements: context.transformStatements(ts.factory.createExpressionStatement(node.left)),
88
- result: context.transformExpression(node.right),
89
- }), node);
124
+ const statements = context.transformStatements(ts.factory.createExpressionStatement(node.left));
125
+ const [precedingStatements, result] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(node.right));
126
+ statements.push(...precedingStatements);
127
+ context.addPrecedingStatements(statements);
128
+ return result;
129
+ }
130
+ case ts.SyntaxKind.QuestionQuestionToken:
131
+ case ts.SyntaxKind.AmpersandAmpersandToken:
132
+ case ts.SyntaxKind.BarBarToken: {
133
+ const [precedingStatements, result] = transformShortCircuitBinaryExpression(context, node, operator);
134
+ context.addPrecedingStatements(precedingStatements);
135
+ return result;
90
136
  }
91
- default:
92
- return transformBinaryOperation(context, context.transformExpression(node.left), context.transformExpression(node.right), operator, node);
93
137
  }
138
+ let [precedingStatements, [lhs, rhs]] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => (0, expression_list_1.transformOrderedExpressions)(context, [node.left, node.right]));
139
+ let result;
140
+ [precedingStatements, result] = transformBinaryOperation(context, lhs, rhs, precedingStatements, operator, node);
141
+ context.addPrecedingStatements(precedingStatements);
142
+ return result;
94
143
  };
95
144
  exports.transformBinaryExpression = transformBinaryExpression;
96
145
  function transformBinaryExpressionStatement(context, node) {
@@ -115,21 +164,14 @@ function transformBinaryExpressionStatement(context, node) {
115
164
  }
116
165
  }
117
166
  exports.transformBinaryExpressionStatement = transformBinaryExpressionStatement;
118
- function transformNullishCoalescingExpression(context, node, transformedLeft, transformedRight) {
167
+ function transformNullishCoalescingOperationNoPrecedingStatements(context, node, transformedLeft, transformedRight) {
119
168
  const lhsType = context.checker.getTypeAtLocation(node.left);
120
169
  // Check if we can take a shortcut to 'lhs or rhs' if the left-hand side cannot be 'false'.
121
- const typeCanBeFalse = (type) => (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Boolean)) !== 0 ||
122
- (type.flags & ts.TypeFlags.BooleanLiteral & ts.TypeFlags.PossiblyFalsy) !== 0;
123
- if ((0, typescript_1.typeCanSatisfy)(context, lhsType, typeCanBeFalse)) {
124
- // lhs can be false, transform to IIFE
125
- const lhsIdentifier = lua.createIdentifier("____lhs");
126
- const nilComparison = lua.createBinaryExpression(lua.cloneIdentifier(lhsIdentifier), lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator);
127
- // if ____ == nil then return rhs else return ____ end
128
- const ifStatement = lua.createIfStatement(nilComparison, lua.createBlock([lua.createReturnStatement([transformedRight])]), lua.createBlock([lua.createReturnStatement([lua.cloneIdentifier(lhsIdentifier)])]));
129
- // (function(lhs') if lhs' == nil then return rhs else return lhs' end)(lhs)
130
- return lua.createCallExpression(lua.createFunctionExpression(lua.createBlock([ifStatement]), [lhsIdentifier]), [
131
- transformedLeft,
132
- ]);
170
+ if ((0, typescript_1.canBeFalsyWhenNotNull)(context, lhsType)) {
171
+ // reuse logic from case with preceding statements
172
+ const [precedingStatements, result] = createShortCircuitBinaryExpressionPrecedingStatements(context, transformedLeft, transformedRight, [], ts.SyntaxKind.QuestionQuestionToken, node);
173
+ context.addPrecedingStatements(precedingStatements);
174
+ return result;
133
175
  }
134
176
  else {
135
177
  // lhs or rhs
@@ -4,7 +4,8 @@ import { FunctionVisitor, TransformationContext } from "../context";
4
4
  export declare type PropertyCallExpression = ts.CallExpression & {
5
5
  expression: ts.PropertyAccessExpression;
6
6
  };
7
- export declare function flattenSpreadExpressions(context: TransformationContext, expressions: readonly ts.Expression[]): lua.Expression[];
7
+ export declare function validateArguments(context: TransformationContext, params: readonly ts.Expression[], signature?: ts.Signature): void;
8
8
  export declare function transformArguments(context: TransformationContext, params: readonly ts.Expression[], signature?: ts.Signature, callContext?: ts.Expression): lua.Expression[];
9
- export declare function transformContextualCallExpression(context: TransformationContext, node: ts.CallExpression | ts.TaggedTemplateExpression, args: ts.Expression[] | ts.NodeArray<ts.Expression>, signature?: ts.Signature): lua.CallExpression | lua.MethodCallExpression;
9
+ export declare function transformCallAndArguments(context: TransformationContext, callExpression: ts.Expression, params: readonly ts.Expression[], signature?: ts.Signature, callContext?: ts.Expression): [lua.Expression, lua.Expression[]];
10
+ export declare function transformContextualCallExpression(context: TransformationContext, node: ts.CallExpression | ts.TaggedTemplateExpression, args: ts.Expression[] | ts.NodeArray<ts.Expression>, signature?: ts.Signature): lua.Expression;
10
11
  export declare const transformCallExpression: FunctionVisitor<ts.CallExpression>;