X Tutup
Skip to content

Commit c1485af

Browse files
hazzard993Perryvw
authored andcommitted
fix back assignment (#764)
* fix back assignment * fix modules reassignment test indentation * use cast for leftHandSideExpression * Add and fix export test case for shadowed identifiers * Support 'export as' reassignment with side effects * Use transformAssignment in destructuring * Fix modules test formatting and cut out weak test cases * Use symbol.name in assignments, not escapedName * Use node and element instead of expression for canBeTransformed * Use Statement[] for transformAssignment * Refactor for getDependenciesOfSymbol * Use lua.Statement[] for component and unary expression * Inline createAssignment to transformAssignment
1 parent 9f82954 commit c1485af

File tree

6 files changed

+178
-61
lines changed

6 files changed

+178
-61
lines changed

src/transformation/utils/export.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,12 @@ export function getSymbolExportScope(
7171
return scope;
7272
}
7373

74-
export function isSymbolExported(context: TransformationContext, symbol: ts.Symbol): boolean {
75-
return (
76-
getExportedSymbolDeclaration(symbol) !== undefined ||
77-
// Symbol may have been exported separately (e.g. 'const foo = "bar"; export { foo }')
78-
isSymbolExportedFromScope(context, symbol, context.sourceFile)
79-
);
80-
}
81-
82-
export function isSymbolExportedFromScope(
74+
export function getExportedSymbolsFromScope(
8375
context: TransformationContext,
84-
symbol: ts.Symbol,
8576
scope: ts.SourceFile | ts.ModuleDeclaration
86-
): boolean {
77+
): ts.Symbol[] {
8778
if (ts.isSourceFile(scope) && !isFileModule(scope)) {
88-
return false;
79+
return [];
8980
}
9081

9182
let scopeSymbol = context.checker.getSymbolAtLocation(scope);
@@ -95,12 +86,37 @@ export function isSymbolExportedFromScope(
9586
}
9687

9788
if (scopeSymbol === undefined || scopeSymbol.exports === undefined) {
98-
return false;
89+
return [];
9990
}
10091

10192
// ts.Iterator is not a ES6-compatible iterator, because TypeScript targets ES5
10293
const it: Iterable<ts.Symbol> = { [Symbol.iterator]: () => scopeSymbol!.exports!.values() };
103-
return [...it].includes(symbol);
94+
return [...it];
95+
}
96+
97+
export function getDependenciesOfSymbol(context: TransformationContext, originalSymbol: ts.Symbol): ts.Symbol[] {
98+
return getExportedSymbolsFromScope(context, context.sourceFile).filter(exportSymbol =>
99+
exportSymbol.declarations
100+
.filter(ts.isExportSpecifier)
101+
.map(context.checker.getExportSpecifierLocalTargetSymbol)
102+
.includes(originalSymbol)
103+
);
104+
}
105+
106+
export function isSymbolExported(context: TransformationContext, symbol: ts.Symbol): boolean {
107+
return (
108+
getExportedSymbolDeclaration(symbol) !== undefined ||
109+
// Symbol may have been exported separately (e.g. 'const foo = "bar"; export { foo }')
110+
isSymbolExportedFromScope(context, symbol, context.sourceFile)
111+
);
112+
}
113+
114+
export function isSymbolExportedFromScope(
115+
context: TransformationContext,
116+
symbol: ts.Symbol,
117+
scope: ts.SourceFile | ts.ModuleDeclaration
118+
): boolean {
119+
return getExportedSymbolsFromScope(context, scope).includes(symbol);
104120
}
105121

106122
export function addExportToIdentifier(

src/transformation/visitors/binary-expression/assignments.ts

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
import * as ts from "typescript";
22
import * as lua from "../../../LuaAST";
3-
import { cast, castEach } from "../../../utils";
3+
import { cast } from "../../../utils";
44
import { TransformationContext } from "../../context";
55
import { isTupleReturnCall } from "../../utils/annotations";
66
import { validateAssignment, validatePropertyAssignment } from "../../utils/assignment-validation";
7+
import { createExportedIdentifier, getDependenciesOfSymbol, isSymbolExported } from "../../utils/export";
78
import { createImmediatelyInvokedFunctionExpression, createUnpackCall, wrapInTable } from "../../utils/lua-ast";
89
import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib";
910
import { isArrayType, isDestructuringAssignment } from "../../utils/typescript";
1011
import { transformElementAccessArgument } from "../access";
1112
import { isArrayLength, transformDestructuringAssignment } from "./destructuring-assignments";
1213

14+
export function transformAssignmentLeftHandSideExpression(
15+
context: TransformationContext,
16+
node: ts.Expression
17+
): lua.AssignmentLeftHandSideExpression {
18+
const symbol = context.checker.getSymbolAtLocation(node);
19+
const left = context.transformExpression(node);
20+
21+
return lua.isIdentifier(left) && symbol && isSymbolExported(context, symbol)
22+
? createExportedIdentifier(context, left)
23+
: cast(left, lua.isAssignmentLeftHandSideExpression);
24+
}
25+
1326
export function transformAssignment(
1427
context: TransformationContext,
1528
// TODO: Change type to ts.LeftHandSideExpression?
1629
lhs: ts.Expression,
1730
right: lua.Expression,
1831
parent?: ts.Expression
19-
): lua.Statement {
32+
): lua.Statement[] {
2033
if (isArrayLength(context, lhs)) {
21-
return lua.createExpressionStatement(
34+
const arrayLengthAssignment = lua.createExpressionStatement(
2235
transformLuaLibFunction(
2336
context,
2437
LuaLibFeature.ArraySetLength,
@@ -27,13 +40,28 @@ export function transformAssignment(
2740
right
2841
)
2942
);
43+
44+
return [arrayLengthAssignment];
3045
}
3146

32-
return lua.createAssignmentStatement(
33-
cast(context.transformExpression(lhs), lua.isAssignmentLeftHandSideExpression),
34-
right,
35-
lhs.parent
36-
);
47+
const symbol = ts.isShorthandPropertyAssignment(lhs.parent)
48+
? context.checker.getShorthandAssignmentValueSymbol(lhs.parent)
49+
: context.checker.getSymbolAtLocation(lhs);
50+
51+
const dependentSymbols = symbol ? getDependenciesOfSymbol(context, symbol) : [];
52+
53+
const left = transformAssignmentLeftHandSideExpression(context, lhs);
54+
55+
const rootAssignment = lua.createAssignmentStatement(left, right, lhs.parent);
56+
57+
return [
58+
rootAssignment,
59+
...dependentSymbols.map(symbol => {
60+
const [left] = rootAssignment.left;
61+
const identifierToAssign = createExportedIdentifier(context, lua.createIdentifier(symbol.name));
62+
return lua.createAssignmentStatement(identifierToAssign, left);
63+
}),
64+
];
3765
}
3866

3967
export function transformAssignmentExpression(
@@ -106,13 +134,39 @@ export function transformAssignmentExpression(
106134
const left = context.transformExpression(expression.left);
107135
const right = context.transformExpression(expression.right);
108136
return createImmediatelyInvokedFunctionExpression(
109-
[transformAssignment(context, expression.left, right)],
137+
transformAssignment(context, expression.left, right),
110138
left,
111139
expression
112140
);
113141
}
114142
}
115143

144+
const canBeTransformedToLuaAssignmentStatement = (
145+
context: TransformationContext,
146+
node: ts.DestructuringAssignment
147+
): node is ts.ArrayDestructuringAssignment => {
148+
return (
149+
ts.isArrayLiteralExpression(node.left) &&
150+
node.left.elements.every(element => {
151+
if (isArrayLength(context, element)) {
152+
return false;
153+
}
154+
155+
if (ts.isPropertyAccessExpression(element) || ts.isElementAccessExpression(element)) {
156+
return true;
157+
}
158+
159+
if (ts.isIdentifier(element)) {
160+
const symbol = context.checker.getSymbolAtLocation(element);
161+
if (symbol) {
162+
const aliases = getDependenciesOfSymbol(context, symbol);
163+
return aliases.length === 0;
164+
}
165+
}
166+
})
167+
);
168+
};
169+
116170
export function transformAssignmentStatement(
117171
context: TransformationContext,
118172
expression: ts.AssignmentExpression<ts.EqualsToken>
@@ -124,25 +178,15 @@ export function transformAssignmentStatement(
124178
validatePropertyAssignment(context, expression);
125179

126180
if (isDestructuringAssignment(expression)) {
127-
if (
128-
ts.isArrayLiteralExpression(expression.left) &&
129-
expression.left.elements.every(
130-
e =>
131-
(ts.isIdentifier(e) || ts.isPropertyAccessExpression(e) || ts.isElementAccessExpression(e)) &&
132-
!isArrayLength(context, e)
133-
)
134-
) {
181+
if (canBeTransformedToLuaAssignmentStatement(context, expression)) {
135182
const rightType = context.checker.getTypeAtLocation(expression.right);
136183
let right = context.transformExpression(expression.right);
137184

138185
if (!isTupleReturnCall(context, expression.right) && isArrayType(context, rightType)) {
139186
right = createUnpackCall(context, right, expression.right);
140187
}
141188

142-
const left = castEach(
143-
expression.left.elements.map(e => context.transformExpression(e)),
144-
lua.isAssignmentLeftHandSideExpression
145-
);
189+
const left = expression.left.elements.map(e => transformAssignmentLeftHandSideExpression(context, e));
146190

147191
return [lua.createAssignmentStatement(left, right, expression)];
148192
}
@@ -158,6 +202,6 @@ export function transformAssignmentStatement(
158202
...transformDestructuringAssignment(context, expression, rootIdentifier),
159203
];
160204
} else {
161-
return [transformAssignment(context, expression.left, context.transformExpression(expression.right))];
205+
return transformAssignment(context, expression.left, context.transformExpression(expression.right));
162206
}
163207
}

src/transformation/visitors/binary-expression/compound.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,12 @@ export function transformCompoundAssignmentExpression(
121121
replacementOperator,
122122
expression
123123
);
124-
const assignStatement = transformAssignment(context, lhs, operatorExpression);
125-
return createImmediatelyInvokedFunctionExpression([tmpDeclaration, assignStatement], tmpIdentifier, expression);
124+
const assignStatements = transformAssignment(context, lhs, operatorExpression);
125+
return createImmediatelyInvokedFunctionExpression(
126+
[tmpDeclaration, ...assignStatements],
127+
tmpIdentifier,
128+
expression
129+
);
126130
} else if (ts.isPropertyAccessExpression(lhs) || ts.isElementAccessExpression(lhs)) {
127131
// Simple property/element access expressions need to cache in temp to avoid double-evaluation
128132
// local ____tmp = ${left} ${replacementOperator} ${right};
@@ -131,14 +135,18 @@ export function transformCompoundAssignmentExpression(
131135
const tmpIdentifier = lua.createIdentifier("____tmp");
132136
const operatorExpression = transformBinaryOperation(context, left, right, replacementOperator, expression);
133137
const tmpDeclaration = lua.createVariableDeclarationStatement(tmpIdentifier, operatorExpression);
134-
const assignStatement = transformAssignment(context, lhs, tmpIdentifier);
135-
return createImmediatelyInvokedFunctionExpression([tmpDeclaration, assignStatement], tmpIdentifier, expression);
138+
const assignStatements = transformAssignment(context, lhs, tmpIdentifier);
139+
return createImmediatelyInvokedFunctionExpression(
140+
[tmpDeclaration, ...assignStatements],
141+
tmpIdentifier,
142+
expression
143+
);
136144
} else {
137145
// Simple expressions
138146
// ${left} = ${right}; return ${right}
139147
const operatorExpression = transformBinaryOperation(context, left, right, replacementOperator, expression);
140-
const assignStatement = transformAssignment(context, lhs, operatorExpression);
141-
return createImmediatelyInvokedFunctionExpression([assignStatement], left, expression);
148+
const assignStatements = transformAssignment(context, lhs, operatorExpression);
149+
return createImmediatelyInvokedFunctionExpression(assignStatements, left, expression);
142150
}
143151
}
144152

@@ -148,7 +156,7 @@ export function transformCompoundAssignmentStatement(
148156
lhs: ts.Expression,
149157
rhs: ts.Expression,
150158
replacementOperator: ts.BinaryOperator
151-
): lua.Statement {
159+
): lua.Statement[] {
152160
const left = cast(context.transformExpression(lhs), lua.isAssignmentLeftHandSideExpression);
153161
const right = context.transformExpression(rhs);
154162

@@ -172,11 +180,12 @@ export function transformCompoundAssignmentStatement(
172180
node
173181
);
174182
const assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression);
175-
return lua.createDoStatement([objAndIndexDeclaration, assignStatement]);
183+
return [objAndIndexDeclaration, assignStatement];
176184
} else {
177185
// Simple statements
178186
// ${left} = ${left} ${replacementOperator} ${right}
179187
const operatorExpression = transformBinaryOperation(context, left, right, replacementOperator, node);
180-
return transformAssignment(context, lhs, operatorExpression);
188+
const assignmentStatements = transformAssignment(context, lhs, operatorExpression);
189+
return assignmentStatements;
181190
}
182191
}

src/transformation/visitors/binary-expression/destructuring-assignments.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import { TransformationContext } from "../../context";
44
import { UnsupportedKind } from "../../utils/errors";
55
import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib";
66
import { isArrayType, isAssignmentPattern } from "../../utils/typescript";
7-
import { transformIdentifier } from "../identifier";
87
import { transformPropertyName } from "../literal";
9-
import { transformAssignment, transformAssignmentStatement } from "./assignments";
8+
import {
9+
transformAssignment,
10+
transformAssignmentLeftHandSideExpression,
11+
transformAssignmentStatement,
12+
} from "./assignments";
1013

1114
export function isArrayLength(
1215
context: TransformationContext,
@@ -86,21 +89,21 @@ function transformArrayLiteralAssignmentPattern(
8689
lua.SyntaxKind.EqualityOperator
8790
);
8891

89-
const defaultAssignmentStatement = transformAssignment(
92+
const defaultAssignmentStatements = transformAssignment(
9093
context,
9194
(element as ts.BinaryExpression).left,
9295
context.transformExpression((element as ts.BinaryExpression).right)
9396
);
9497

95-
const elseAssignmentStatement = transformAssignment(
98+
const elseAssignmentStatements = transformAssignment(
9699
context,
97100
(element as ts.BinaryExpression).left,
98101
assignedVariable
99102
);
100103

101-
const ifBlock = lua.createBlock([defaultAssignmentStatement]);
104+
const ifBlock = lua.createBlock(defaultAssignmentStatements);
102105

103-
const elseBlock = lua.createBlock([elseAssignmentStatement]);
106+
const elseBlock = lua.createBlock(elseAssignmentStatements);
104107

105108
const ifStatement = lua.createIfStatement(nilCondition, ifBlock, elseBlock, node);
106109

@@ -169,14 +172,15 @@ function transformShorthandPropertyAssignment(
169172
root: lua.Expression
170173
): lua.Statement[] {
171174
const result: lua.Statement[] = [];
172-
const assignmentVariableName = transformIdentifier(context, node.name);
175+
const assignmentVariableName = transformAssignmentLeftHandSideExpression(context, node.name);
173176
const extractionIndex = lua.createStringLiteral(node.name.text);
174-
const variableExtractionAssignmentStatement = lua.createAssignmentStatement(
175-
assignmentVariableName,
177+
const variableExtractionAssignmentStatements = transformAssignment(
178+
context,
179+
node.name,
176180
lua.createTableIndexExpression(root, extractionIndex)
177181
);
178182

179-
result.push(variableExtractionAssignmentStatement);
183+
result.push(...variableExtractionAssignmentStatements);
180184

181185
const defaultInitializer = node.objectAssignmentInitializer
182186
? context.transformExpression(node.objectAssignmentInitializer)
@@ -189,9 +193,9 @@ function transformShorthandPropertyAssignment(
189193
lua.SyntaxKind.EqualityOperator
190194
);
191195

192-
const assignment = lua.createAssignmentStatement(assignmentVariableName, defaultInitializer);
196+
const assignmentStatements = transformAssignment(context, node.name, defaultInitializer);
193197

194-
const ifBlock = lua.createBlock([assignment]);
198+
const ifBlock = lua.createBlock(assignmentStatements);
195199

196200
result.push(lua.createIfStatement(nilCondition, ifBlock, undefined, node));
197201
}
@@ -223,9 +227,9 @@ function transformPropertyAssignment(
223227
const variableToExtract = transformPropertyName(context, node.name);
224228
const extractingExpression = lua.createTableIndexExpression(root, variableToExtract);
225229

226-
const destructureAssignmentStatement = transformAssignment(context, leftExpression, extractingExpression);
230+
const destructureAssignmentStatements = transformAssignment(context, leftExpression, extractingExpression);
227231

228-
result.push(destructureAssignmentStatement);
232+
result.push(...destructureAssignmentStatements);
229233

230234
if (ts.isBinaryExpression(node.initializer)) {
231235
const assignmentLeftHandSide = context.transformExpression(node.initializer.left);
@@ -274,5 +278,5 @@ function transformSpreadAssignment(
274278
lua.createTableExpression(usedProperties)
275279
);
276280

277-
return [transformAssignment(context, node.expression, extractingExpression)];
281+
return transformAssignment(context, node.expression, extractingExpression);
278282
}

src/transformation/visitors/unary-expression.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
export function transformUnaryExpressionStatement(
1212
context: TransformationContext,
1313
node: ts.ExpressionStatement
14-
): lua.Statement | undefined {
14+
): lua.Statement[] | undefined {
1515
const expression = ts.isExpressionStatement(node) ? node.expression : node;
1616
if (
1717
ts.isPrefixUnaryExpression(expression) &&

0 commit comments

Comments
 (0)
X Tutup