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.transformQualifiedName = exports.transformPropertyAccessExpression = exports.transformElementAccessExpression = exports.transformElementAccessArgument = void 0;
3
+ exports.transformQualifiedName = exports.transformPropertyAccessExpressionWithCapture = exports.transformPropertyAccessExpression = exports.transformElementAccessExpressionWithCapture = exports.transformElementAccessExpression = exports.transformElementAccessArgument = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../LuaAST");
6
6
  const builtins_1 = require("../builtins");
@@ -10,9 +10,10 @@ const lua_ast_1 = require("../utils/lua-ast");
10
10
  const lualib_1 = require("../utils/lualib");
11
11
  const typescript_1 = require("../utils/typescript");
12
12
  const enum_1 = require("./enum");
13
+ const expression_list_1 = require("./expression-list");
13
14
  const multi_1 = require("./language-extensions/multi");
14
- function transformElementAccessArgument(context, node) {
15
- const index = context.transformExpression(node.argumentExpression);
15
+ const optional_chaining_1 = require("./optional-chaining");
16
+ function addOneToArrayAccessArgument(context, node, index) {
16
17
  const type = context.checker.getTypeAtLocation(node.expression);
17
18
  const argumentType = context.checker.getTypeAtLocation(node.argumentExpression);
18
19
  if ((0, typescript_1.isArrayType)(context, type) && (0, typescript_1.isNumberType)(context, argumentType)) {
@@ -20,20 +21,31 @@ function transformElementAccessArgument(context, node) {
20
21
  }
21
22
  return index;
22
23
  }
24
+ function transformElementAccessArgument(context, node) {
25
+ const index = context.transformExpression(node.argumentExpression);
26
+ return addOneToArrayAccessArgument(context, node, index);
27
+ }
23
28
  exports.transformElementAccessArgument = transformElementAccessArgument;
24
- const transformElementAccessExpression = (node, context) => {
29
+ const transformElementAccessExpression = (node, context) => transformElementAccessExpressionWithCapture(context, node, undefined).expression;
30
+ exports.transformElementAccessExpression = transformElementAccessExpression;
31
+ function transformElementAccessExpressionWithCapture(context, node, thisValueCapture) {
25
32
  const constEnumValue = (0, enum_1.tryGetConstEnumValue)(context, node);
26
33
  if (constEnumValue) {
27
- return constEnumValue;
34
+ return { expression: constEnumValue };
28
35
  }
29
- const table = context.transformExpression(node.expression);
36
+ if (ts.isOptionalChain(node)) {
37
+ return (0, optional_chaining_1.transformOptionalChainWithCapture)(context, node, thisValueCapture);
38
+ }
39
+ const [table, accessExpression] = (0, expression_list_1.transformOrderedExpressions)(context, [node.expression, node.argumentExpression]);
30
40
  const type = context.checker.getTypeAtLocation(node.expression);
31
41
  const argumentType = context.checker.getTypeAtLocation(node.argumentExpression);
32
42
  if ((0, typescript_1.isStringType)(context, type) && (0, typescript_1.isNumberType)(context, argumentType)) {
33
- const index = context.transformExpression(node.argumentExpression);
34
- return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.StringAccess, node, table, index);
43
+ // strings are not callable, so ignore thisValueCapture
44
+ return {
45
+ expression: (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.StringAccess, node, table, accessExpression),
46
+ };
35
47
  }
36
- const accessExpression = transformElementAccessArgument(context, node);
48
+ const updatedAccessExpression = addOneToArrayAccessArgument(context, node, accessExpression);
37
49
  if ((0, multi_1.isMultiReturnCall)(context, node.expression)) {
38
50
  const accessType = context.checker.getTypeAtLocation(node.argumentExpression);
39
51
  if (!(0, typescript_1.isNumberType)(context, accessType)) {
@@ -41,61 +53,76 @@ const transformElementAccessExpression = (node, context) => {
41
53
  }
42
54
  // When selecting the first element, we can shortcut
43
55
  if (ts.isNumericLiteral(node.argumentExpression) && node.argumentExpression.text === "0") {
44
- return table;
56
+ return { expression: table };
45
57
  }
46
58
  else {
47
59
  const selectIdentifier = lua.createIdentifier("select");
48
- const selectCall = lua.createCallExpression(selectIdentifier, [accessExpression, table]);
49
- return selectCall;
60
+ return { expression: lua.createCallExpression(selectIdentifier, [updatedAccessExpression, table]) };
50
61
  }
51
62
  }
52
- if (ts.isOptionalChain(node)) {
53
- return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.OptionalChainAccess, node, table, accessExpression);
63
+ if (thisValueCapture) {
64
+ const thisValue = (0, optional_chaining_1.captureThisValue)(context, table, thisValueCapture, node.expression);
65
+ return {
66
+ expression: lua.createTableIndexExpression(thisValue, updatedAccessExpression, node),
67
+ thisValue,
68
+ };
54
69
  }
55
- return lua.createTableIndexExpression(table, accessExpression, node);
56
- };
57
- exports.transformElementAccessExpression = transformElementAccessExpression;
58
- const transformPropertyAccessExpression = (node, context) => {
70
+ return { expression: lua.createTableIndexExpression(table, updatedAccessExpression, node) };
71
+ }
72
+ exports.transformElementAccessExpressionWithCapture = transformElementAccessExpressionWithCapture;
73
+ const transformPropertyAccessExpression = (node, context) => transformPropertyAccessExpressionWithCapture(context, node, undefined).expression;
74
+ exports.transformPropertyAccessExpression = transformPropertyAccessExpression;
75
+ function transformPropertyAccessExpressionWithCapture(context, node, thisValueCapture) {
59
76
  const property = node.name.text;
60
77
  const type = context.checker.getTypeAtLocation(node.expression);
78
+ const isOptionalLeft = (0, optional_chaining_1.isOptionalContinuation)(node.expression);
61
79
  const annotations = (0, annotations_1.getTypeAnnotations)(type);
62
80
  if (annotations.has(annotations_1.AnnotationKind.LuaTable)) {
63
81
  context.diagnostics.push((0, diagnostics_1.annotationRemoved)(node, annotations_1.AnnotationKind.LuaTable));
64
82
  }
65
83
  const constEnumValue = (0, enum_1.tryGetConstEnumValue)(context, node);
66
84
  if (constEnumValue) {
67
- return constEnumValue;
68
- }
69
- const builtinResult = (0, builtins_1.transformBuiltinPropertyAccessExpression)(context, node);
70
- if (builtinResult) {
71
- return builtinResult;
85
+ return { expression: constEnumValue };
72
86
  }
73
87
  if (ts.isCallExpression(node.expression) && (0, multi_1.returnsMultiType)(context, node.expression)) {
74
88
  context.diagnostics.push((0, diagnostics_1.invalidMultiReturnAccess)(node));
75
89
  }
90
+ if (ts.isOptionalChain(node)) {
91
+ return (0, optional_chaining_1.transformOptionalChainWithCapture)(context, node, thisValueCapture);
92
+ }
76
93
  // Do not output path for member only enums
77
94
  if (annotations.has(annotations_1.AnnotationKind.CompileMembersOnly)) {
95
+ if (isOptionalLeft) {
96
+ context.diagnostics.push((0, diagnostics_1.unsupportedOptionalCompileMembersOnly)(node));
97
+ }
78
98
  if (ts.isPropertyAccessExpression(node.expression)) {
79
99
  // in case of ...x.enum.y transform to ...x.y
80
- return lua.createTableIndexExpression(context.transformExpression(node.expression.expression), lua.createStringLiteral(property), node);
100
+ const expression = lua.createTableIndexExpression(context.transformExpression(node.expression.expression), lua.createStringLiteral(property), node);
101
+ return { expression };
81
102
  }
82
103
  else {
83
- return lua.createIdentifier(property, node);
104
+ return { expression: lua.createIdentifier(property, node) };
84
105
  }
85
106
  }
86
- if (ts.isOptionalChain(node)) {
87
- // Only handle full optional chains separately, not partial ones
88
- return transformOptionalChain(context, node);
107
+ const builtinResult = (0, builtins_1.transformBuiltinPropertyAccessExpression)(context, node);
108
+ if (builtinResult) {
109
+ // Ignore thisValueCapture.
110
+ // This assumes that nothing returned by builtin property accesses are callable.
111
+ // If this assumption is no longer true, this may need to be updated.
112
+ return { expression: builtinResult };
89
113
  }
90
- const callPath = context.transformExpression(node.expression);
91
- return lua.createTableIndexExpression(callPath, lua.createStringLiteral(property), node);
92
- };
93
- exports.transformPropertyAccessExpression = transformPropertyAccessExpression;
94
- function transformOptionalChain(context, node) {
95
- const left = context.transformExpression(node.expression);
96
- const right = lua.createStringLiteral(node.name.text, node.name);
97
- return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.OptionalChainAccess, node, left, right);
114
+ const table = context.transformExpression(node.expression);
115
+ if (thisValueCapture) {
116
+ const thisValue = (0, optional_chaining_1.captureThisValue)(context, table, thisValueCapture, node.expression);
117
+ const expression = lua.createTableIndexExpression(thisValue, lua.createStringLiteral(property), node);
118
+ return {
119
+ expression,
120
+ thisValue,
121
+ };
122
+ }
123
+ return { expression: lua.createTableIndexExpression(table, lua.createStringLiteral(property), node) };
98
124
  }
125
+ exports.transformPropertyAccessExpressionWithCapture = transformPropertyAccessExpressionWithCapture;
99
126
  const transformQualifiedName = (node, context) => {
100
127
  const right = lua.createStringLiteral(node.right.text, node.right);
101
128
  const left = context.transformExpression(node.left);
@@ -1,7 +1,8 @@
1
1
  import * as ts from "typescript";
2
2
  import * as lua from "../../../LuaAST";
3
3
  import { TransformationContext } from "../../context";
4
- export declare function transformAssignmentLeftHandSideExpression(context: TransformationContext, node: ts.Expression): lua.AssignmentLeftHandSideExpression;
5
- export declare function transformAssignment(context: TransformationContext, lhs: ts.Expression, right: lua.Expression, parent?: ts.Expression): lua.Statement[];
4
+ export declare function transformAssignmentLeftHandSideExpression(context: TransformationContext, node: ts.Expression, rightHasPrecedingStatements?: boolean): lua.AssignmentLeftHandSideExpression;
5
+ export declare function transformAssignment(context: TransformationContext, lhs: ts.Expression, right: lua.Expression, rightHasPrecedingStatements?: boolean, parent?: ts.Expression): lua.Statement[];
6
+ export declare function transformAssignmentWithRightPrecedingStatements(context: TransformationContext, lhs: ts.Expression, right: lua.Expression, rightPrecedingStatements: lua.Statement[], parent?: ts.Expression): lua.Statement[];
6
7
  export declare function transformAssignmentExpression(context: TransformationContext, expression: ts.AssignmentExpression<ts.EqualsToken>): lua.Expression;
7
8
  export declare function transformAssignmentStatement(context: TransformationContext, expression: ts.AssignmentExpression<ts.EqualsToken>): lua.Statement[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformAssignmentStatement = exports.transformAssignmentExpression = exports.transformAssignment = exports.transformAssignmentLeftHandSideExpression = void 0;
3
+ exports.transformAssignmentStatement = exports.transformAssignmentExpression = exports.transformAssignmentWithRightPrecedingStatements = exports.transformAssignment = exports.transformAssignmentLeftHandSideExpression = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../../LuaAST");
6
6
  const utils_1 = require("../../../utils");
@@ -9,13 +9,27 @@ const export_1 = require("../../utils/export");
9
9
  const lua_ast_1 = require("../../utils/lua-ast");
10
10
  const lualib_1 = require("../../utils/lualib");
11
11
  const typescript_1 = require("../../utils/typescript");
12
- const access_1 = require("../access");
13
12
  const destructuring_assignments_1 = require("./destructuring-assignments");
14
13
  const multi_1 = require("../language-extensions/multi");
15
- const scope_1 = require("../../utils/scope");
16
- const transform_1 = require("../../utils/transform");
17
14
  const diagnostics_1 = require("../../utils/diagnostics");
18
- function transformAssignmentLeftHandSideExpression(context, node) {
15
+ const access_1 = require("../access");
16
+ const expression_list_1 = require("../expression-list");
17
+ const preceding_statements_1 = require("../../utils/preceding-statements");
18
+ function transformAssignmentLeftHandSideExpression(context, node, rightHasPrecedingStatements) {
19
+ // Access expressions need the components of the left side cached in temps before the right side's preceding statements
20
+ if (rightHasPrecedingStatements && (ts.isElementAccessExpression(node) || ts.isPropertyAccessExpression(node))) {
21
+ let table = context.transformExpression(node.expression);
22
+ table = (0, expression_list_1.moveToPrecedingTemp)(context, table, node.expression);
23
+ let index;
24
+ if (ts.isElementAccessExpression(node)) {
25
+ index = (0, access_1.transformElementAccessArgument)(context, node);
26
+ index = (0, expression_list_1.moveToPrecedingTemp)(context, index, node.argumentExpression);
27
+ }
28
+ else {
29
+ index = lua.createStringLiteral(node.name.text, node.name);
30
+ }
31
+ return lua.createTableIndexExpression(table, index, node);
32
+ }
19
33
  const symbol = context.checker.getSymbolAtLocation(node);
20
34
  const left = context.transformExpression(node);
21
35
  return lua.isIdentifier(left) && symbol && (0, export_1.isSymbolExported)(context, symbol)
@@ -25,7 +39,7 @@ function transformAssignmentLeftHandSideExpression(context, node) {
25
39
  exports.transformAssignmentLeftHandSideExpression = transformAssignmentLeftHandSideExpression;
26
40
  function transformAssignment(context,
27
41
  // TODO: Change type to ts.LeftHandSideExpression?
28
- lhs, right, parent) {
42
+ lhs, right, rightHasPrecedingStatements, parent) {
29
43
  if (ts.isOptionalChain(lhs)) {
30
44
  context.diagnostics.push((0, diagnostics_1.notAllowedOptionalAssignment)(lhs));
31
45
  return [];
@@ -34,11 +48,11 @@ lhs, right, parent) {
34
48
  const arrayLengthAssignment = lua.createExpressionStatement((0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.ArraySetLength, parent, context.transformExpression(lhs.expression), right));
35
49
  return [arrayLengthAssignment];
36
50
  }
37
- const symbol = ts.isShorthandPropertyAssignment(lhs.parent)
51
+ const symbol = lhs.parent && ts.isShorthandPropertyAssignment(lhs.parent)
38
52
  ? context.checker.getShorthandAssignmentValueSymbol(lhs.parent)
39
53
  : context.checker.getSymbolAtLocation(lhs);
40
54
  const dependentSymbols = symbol ? (0, export_1.getDependenciesOfSymbol)(context, symbol) : [];
41
- const left = transformAssignmentLeftHandSideExpression(context, lhs);
55
+ const left = transformAssignmentLeftHandSideExpression(context, lhs, rightHasPrecedingStatements);
42
56
  const rootAssignment = lua.createAssignmentStatement(left, right, lhs.parent);
43
57
  return [
44
58
  rootAssignment,
@@ -50,15 +64,23 @@ lhs, right, parent) {
50
64
  ];
51
65
  }
52
66
  exports.transformAssignment = transformAssignment;
67
+ function transformAssignmentWithRightPrecedingStatements(context, lhs, right, rightPrecedingStatements, parent) {
68
+ return [
69
+ ...rightPrecedingStatements,
70
+ ...transformAssignment(context, lhs, right, rightPrecedingStatements.length > 0, parent),
71
+ ];
72
+ }
73
+ exports.transformAssignmentWithRightPrecedingStatements = transformAssignmentWithRightPrecedingStatements;
53
74
  function transformDestructuredAssignmentExpression(context, expression) {
54
- const rootIdentifier = lua.createAnonymousIdentifier(expression.left);
55
- let right = context.transformExpression(expression.right);
75
+ const rootIdentifier = context.createTempNameForNode(expression.right);
76
+ let [rightPrecedingStatements, right] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expression.right));
77
+ context.addPrecedingStatements(rightPrecedingStatements);
56
78
  if ((0, multi_1.isMultiReturnCall)(context, expression.right)) {
57
79
  right = (0, lua_ast_1.wrapInTable)(right);
58
80
  }
59
81
  const statements = [
60
82
  lua.createVariableDeclarationStatement(rootIdentifier, right),
61
- ...(0, destructuring_assignments_1.transformDestructuringAssignment)(context, expression, rootIdentifier),
83
+ ...(0, destructuring_assignments_1.transformDestructuringAssignment)(context, expression, rootIdentifier, rightPrecedingStatements.length > 0),
62
84
  ];
63
85
  return { statements, result: rootIdentifier };
64
86
  }
@@ -72,48 +94,28 @@ function transformAssignmentExpression(context, expression) {
72
94
  return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.ArraySetLength, expression, context.transformExpression(expression.left.expression), context.transformExpression(expression.right));
73
95
  }
74
96
  if ((0, typescript_1.isDestructuringAssignment)(expression)) {
75
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => transformDestructuredAssignmentExpression(context, expression), expression);
97
+ const { statements, result } = transformDestructuredAssignmentExpression(context, expression);
98
+ context.addPrecedingStatements(statements);
99
+ return result;
76
100
  }
77
101
  if (ts.isPropertyAccessExpression(expression.left) || ts.isElementAccessExpression(expression.left)) {
78
- // Left is property/element access: cache result while maintaining order of evaluation
79
- // (function(o, i, v) o[i] = v; return v end)(${objExpression}, ${indexExpression}, ${right})
80
- const objParameter = lua.createIdentifier("o");
81
- const indexParameter = lua.createIdentifier("i");
82
- const valueParameter = lua.createIdentifier("v");
83
- const indexStatement = lua.createTableIndexExpression(objParameter, indexParameter);
84
- const statements = [
85
- lua.createAssignmentStatement(indexStatement, valueParameter),
86
- lua.createReturnStatement([valueParameter]),
87
- ];
88
- const iife = lua.createFunctionExpression(lua.createBlock(statements), [
89
- objParameter,
90
- indexParameter,
91
- valueParameter,
102
+ const tempVar = context.createTempNameForNode(expression.right);
103
+ const [precedingStatements, right] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expression.right));
104
+ const left = transformAssignmentLeftHandSideExpression(context, expression.left, precedingStatements.length > 0);
105
+ context.addPrecedingStatements([
106
+ ...precedingStatements,
107
+ lua.createVariableDeclarationStatement(tempVar, right, expression.right),
108
+ lua.createAssignmentStatement(left, lua.cloneIdentifier(tempVar), expression.left),
92
109
  ]);
93
- (0, scope_1.pushScope)(context, scope_1.ScopeType.Function);
94
- const objExpression = context.transformExpression(expression.left.expression);
95
- let indexExpression;
96
- if (ts.isPropertyAccessExpression(expression.left)) {
97
- // Property access
98
- indexExpression = lua.createStringLiteral(expression.left.name.text);
99
- }
100
- else {
101
- // Element access
102
- indexExpression = (0, access_1.transformElementAccessArgument)(context, expression.left);
103
- }
104
- const args = [objExpression, indexExpression, context.transformExpression(expression.right)];
105
- (0, scope_1.popScope)(context);
106
- return lua.createCallExpression(iife, args, expression);
110
+ return lua.cloneIdentifier(tempVar);
107
111
  }
108
112
  else {
109
- return (0, transform_1.transformToImmediatelyInvokedFunctionExpression)(context, () => {
110
- // Simple assignment
111
- // (function() ${left} = ${right}; return ${left} end)()
112
- const left = context.transformExpression(expression.left);
113
- const right = context.transformExpression(expression.right);
114
- const statements = transformAssignment(context, expression.left, right);
115
- return { statements, result: left };
116
- }, expression);
113
+ // Simple assignment
114
+ // ${left} = ${right}; return ${left}
115
+ const left = context.transformExpression(expression.left);
116
+ const right = context.transformExpression(expression.right);
117
+ context.addPrecedingStatements(transformAssignment(context, expression.left, right));
118
+ return left;
117
119
  }
118
120
  }
119
121
  exports.transformAssignmentExpression = transformAssignmentExpression;
@@ -123,7 +125,9 @@ const canBeTransformedToLuaAssignmentStatement = (context, node) => ts.isArrayLi
123
125
  return false;
124
126
  }
125
127
  if (ts.isPropertyAccessExpression(element) || ts.isElementAccessExpression(element)) {
126
- return true;
128
+ // Lua's execution order for multi-assignments is not the same as JS's, so we should always
129
+ // break these down when the left side may have side effects.
130
+ return false;
127
131
  }
128
132
  if (ts.isIdentifier(element)) {
129
133
  const symbol = context.checker.getSymbolAtLocation(element);
@@ -141,28 +145,33 @@ function transformAssignmentStatement(context, expression) {
141
145
  if ((0, typescript_1.isDestructuringAssignment)(expression)) {
142
146
  if (canBeTransformedToLuaAssignmentStatement(context, expression)) {
143
147
  const rightType = context.checker.getTypeAtLocation(expression.right);
144
- let right = context.transformExpression(expression.right);
148
+ let right;
145
149
  if (ts.isArrayLiteralExpression(expression.right)) {
146
- right = expression.right.elements.map(e => context.transformExpression(e));
150
+ right = (0, expression_list_1.transformExpressionList)(context, expression.right.elements);
147
151
  }
148
- else if (!(0, multi_1.isMultiReturnCall)(context, expression.right) && (0, typescript_1.isArrayType)(context, rightType)) {
149
- right = (0, lua_ast_1.createUnpackCall)(context, right, expression.right);
152
+ else {
153
+ right = context.transformExpression(expression.right);
154
+ if (!(0, multi_1.isMultiReturnCall)(context, expression.right) && (0, typescript_1.isArrayType)(context, rightType)) {
155
+ right = (0, lua_ast_1.createUnpackCall)(context, right, expression.right);
156
+ }
150
157
  }
151
158
  const left = expression.left.elements.map(e => transformAssignmentLeftHandSideExpression(context, e));
152
159
  return [lua.createAssignmentStatement(left, right, expression)];
153
160
  }
154
- let right = context.transformExpression(expression.right);
161
+ let [rightPrecedingStatements, right] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expression.right));
162
+ context.addPrecedingStatements(rightPrecedingStatements);
155
163
  if ((0, multi_1.isMultiReturnCall)(context, expression.right)) {
156
164
  right = (0, lua_ast_1.wrapInTable)(right);
157
165
  }
158
- const rootIdentifier = lua.createAnonymousIdentifier(expression.left);
166
+ const rootIdentifier = context.createTempNameForNode(expression.left);
159
167
  return [
160
168
  lua.createVariableDeclarationStatement(rootIdentifier, right),
161
- ...(0, destructuring_assignments_1.transformDestructuringAssignment)(context, expression, rootIdentifier),
169
+ ...(0, destructuring_assignments_1.transformDestructuringAssignment)(context, expression, rootIdentifier, rightPrecedingStatements.length > 0),
162
170
  ];
163
171
  }
164
172
  else {
165
- return transformAssignment(context, expression.left, context.transformExpression(expression.right));
173
+ const [precedingStatements, right] = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => context.transformExpression(expression.right));
174
+ return transformAssignmentWithRightPrecedingStatements(context, expression.left, right, precedingStatements);
166
175
  }
167
176
  }
168
177
  exports.transformAssignmentStatement = transformAssignmentStatement;
@@ -1,12 +1,13 @@
1
1
  import * as ts from "typescript";
2
2
  import * as lua from "../../../LuaAST";
3
3
  import { TransformationContext } from "../../context";
4
- import { ImmediatelyInvokedFunctionParameters } from "../../utils/transform";
5
- export declare function parseAccessExpressionWithEvaluationEffects(context: TransformationContext, node: ts.Expression): [ts.Expression, ts.Expression] | [];
6
4
  declare type CompoundAssignmentToken = ts.SyntaxKind.BarToken | ts.SyntaxKind.PlusToken | ts.SyntaxKind.CaretToken | ts.SyntaxKind.MinusToken | ts.SyntaxKind.SlashToken | ts.SyntaxKind.PercentToken | ts.SyntaxKind.AsteriskToken | ts.SyntaxKind.AmpersandToken | ts.SyntaxKind.AsteriskAsteriskToken | ts.SyntaxKind.LessThanLessThanToken | ts.SyntaxKind.GreaterThanGreaterThanToken | ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken | ts.SyntaxKind.BarBarToken | ts.SyntaxKind.AmpersandAmpersandToken | ts.SyntaxKind.QuestionQuestionToken;
7
5
  export declare const isCompoundAssignmentToken: (token: ts.BinaryOperator) => token is ts.CompoundAssignmentOperator;
8
6
  export declare const unwrapCompoundAssignmentToken: (token: ts.CompoundAssignmentOperator) => CompoundAssignmentToken;
9
- export declare function transformCompoundAssignment(context: TransformationContext, expression: ts.Expression, lhs: ts.Expression, rhs: ts.Expression, operator: CompoundAssignmentToken, isPostfix: boolean): ImmediatelyInvokedFunctionParameters;
10
- export declare function transformCompoundAssignmentExpression(context: TransformationContext, expression: ts.Expression, lhs: ts.Expression, rhs: ts.Expression, operator: CompoundAssignmentToken, isPostfix: boolean): lua.CallExpression;
7
+ export declare function transformCompoundAssignment(context: TransformationContext, expression: ts.Expression, lhs: ts.Expression, rhs: ts.Expression, operator: CompoundAssignmentToken, isPostfix: boolean): {
8
+ statements: lua.Statement[];
9
+ result: lua.AssignmentLeftHandSideExpression;
10
+ };
11
+ export declare function transformCompoundAssignmentExpression(context: TransformationContext, expression: ts.Expression, lhs: ts.Expression, rhs: ts.Expression, operator: CompoundAssignmentToken, isPostfix: boolean): lua.Expression;
11
12
  export declare function transformCompoundAssignmentStatement(context: TransformationContext, node: ts.Node, lhs: ts.Expression, rhs: ts.Expression, operator: CompoundAssignmentToken): lua.Statement[];
12
13
  export {};