GeezerVison is showing github.com — fix back assignment (#764) · TypeScriptToLua/TypeScriptToLua@c1485af
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: src/transformation/utils/export.ts +30-14Lines changed: 30 additions & 14 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -71,21 +71,12 @@ export function getSymbolExportScope( | 71 | 71 | return scope; | 72 | 72 | } | 73 | 73 |
| 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( | 83 | 75 | context: TransformationContext, | 84 | | - symbol: ts.Symbol, | 85 | 76 | scope: ts.SourceFile | ts.ModuleDeclaration | 86 | | -): boolean { | | 77 | +): ts.Symbol[] { | 87 | 78 | if (ts.isSourceFile(scope) && !isFileModule(scope)) { | 88 | | - return false; | | 79 | + return []; | 89 | 80 | } | 90 | 81 |
| 91 | 82 | let scopeSymbol = context.checker.getSymbolAtLocation(scope); | @@ -95,12 +86,37 @@ export function isSymbolExportedFromScope( | 95 | 86 | } | 96 | 87 |
| 97 | 88 | if (scopeSymbol === undefined || scopeSymbol.exports === undefined) { | 98 | | - return false; | | 89 | + return []; | 99 | 90 | } | 100 | 91 |
| 101 | 92 | // ts.Iterator is not a ES6-compatible iterator, because TypeScript targets ES5 | 102 | 93 | 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); | 104 | 120 | } | 105 | 121 |
| 106 | 122 | export function addExportToIdentifier( | |
Collapse file Copy file name to clipboardExpand all lines: src/transformation/visitors/binary-expression/assignments.ts +66-22Lines changed: 66 additions & 22 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | import * as ts from "typescript"; | 2 | 2 | import * as lua from "../../../LuaAST"; | 3 | | -import { cast, castEach } from "../../../utils"; | | 3 | +import { cast } from "../../../utils"; | 4 | 4 | import { TransformationContext } from "../../context"; | 5 | 5 | import { isTupleReturnCall } from "../../utils/annotations"; | 6 | 6 | import { validateAssignment, validatePropertyAssignment } from "../../utils/assignment-validation"; | | 7 | +import { createExportedIdentifier, getDependenciesOfSymbol, isSymbolExported } from "../../utils/export"; | 7 | 8 | import { createImmediatelyInvokedFunctionExpression, createUnpackCall, wrapInTable } from "../../utils/lua-ast"; | 8 | 9 | import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; | 9 | 10 | import { isArrayType, isDestructuringAssignment } from "../../utils/typescript"; | 10 | 11 | import { transformElementAccessArgument } from "../access"; | 11 | 12 | import { isArrayLength, transformDestructuringAssignment } from "./destructuring-assignments"; | 12 | 13 |
| | 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 | + | 13 | 26 | export function transformAssignment( | 14 | 27 | context: TransformationContext, | 15 | 28 | // TODO: Change type to ts.LeftHandSideExpression? | 16 | 29 | lhs: ts.Expression, | 17 | 30 | right: lua.Expression, | 18 | 31 | parent?: ts.Expression | 19 | | -): lua.Statement { | | 32 | +): lua.Statement[] { | 20 | 33 | if (isArrayLength(context, lhs)) { | 21 | | - return lua.createExpressionStatement( | | 34 | + const arrayLengthAssignment = lua.createExpressionStatement( | 22 | 35 | transformLuaLibFunction( | 23 | 36 | context, | 24 | 37 | LuaLibFeature.ArraySetLength, | @@ -27,13 +40,28 @@ export function transformAssignment( | 27 | 40 | right | 28 | 41 | ) | 29 | 42 | ); | | 43 | + | | 44 | + return [arrayLengthAssignment]; | 30 | 45 | } | 31 | 46 |
| 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 | + ]; | 37 | 65 | } | 38 | 66 |
| 39 | 67 | export function transformAssignmentExpression( | @@ -106,13 +134,39 @@ export function transformAssignmentExpression( | 106 | 134 | const left = context.transformExpression(expression.left); | 107 | 135 | const right = context.transformExpression(expression.right); | 108 | 136 | return createImmediatelyInvokedFunctionExpression( | 109 | | - [transformAssignment(context, expression.left, right)], | | 137 | + transformAssignment(context, expression.left, right), | 110 | 138 | left, | 111 | 139 | expression | 112 | 140 | ); | 113 | 141 | } | 114 | 142 | } | 115 | 143 |
| | 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 | + | 116 | 170 | export function transformAssignmentStatement( | 117 | 171 | context: TransformationContext, | 118 | 172 | expression: ts.AssignmentExpression<ts.EqualsToken> | @@ -124,25 +178,15 @@ export function transformAssignmentStatement( | 124 | 178 | validatePropertyAssignment(context, expression); | 125 | 179 |
| 126 | 180 | 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)) { | 135 | 182 | const rightType = context.checker.getTypeAtLocation(expression.right); | 136 | 183 | let right = context.transformExpression(expression.right); | 137 | 184 |
| 138 | 185 | if (!isTupleReturnCall(context, expression.right) && isArrayType(context, rightType)) { | 139 | 186 | right = createUnpackCall(context, right, expression.right); | 140 | 187 | } | 141 | 188 |
| 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)); | 146 | 190 |
| 147 | 191 | return [lua.createAssignmentStatement(left, right, expression)]; | 148 | 192 | } | @@ -158,6 +202,6 @@ export function transformAssignmentStatement( | 158 | 202 | ...transformDestructuringAssignment(context, expression, rootIdentifier), | 159 | 203 | ]; | 160 | 204 | } else { | 161 | | - return [transformAssignment(context, expression.left, context.transformExpression(expression.right))]; | | 205 | + return transformAssignment(context, expression.left, context.transformExpression(expression.right)); | 162 | 206 | } | 163 | 207 | } |
Collapse file Copy file name to clipboardExpand all lines: src/transformation/visitors/binary-expression/compound.ts +18-9Lines changed: 18 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -121,8 +121,12 @@ export function transformCompoundAssignmentExpression( | 121 | 121 | replacementOperator, | 122 | 122 | expression | 123 | 123 | ); | 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 | + ); | 126 | 130 | } else if (ts.isPropertyAccessExpression(lhs) || ts.isElementAccessExpression(lhs)) { | 127 | 131 | // Simple property/element access expressions need to cache in temp to avoid double-evaluation | 128 | 132 | // local ____tmp = ${left} ${replacementOperator} ${right}; | @@ -131,14 +135,18 @@ export function transformCompoundAssignmentExpression( | 131 | 135 | const tmpIdentifier = lua.createIdentifier("____tmp"); | 132 | 136 | const operatorExpression = transformBinaryOperation(context, left, right, replacementOperator, expression); | 133 | 137 | 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 | + ); | 136 | 144 | } else { | 137 | 145 | // Simple expressions | 138 | 146 | // ${left} = ${right}; return ${right} | 139 | 147 | 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); | 142 | 150 | } | 143 | 151 | } | 144 | 152 |
| @@ -148,7 +156,7 @@ export function transformCompoundAssignmentStatement( | 148 | 156 | lhs: ts.Expression, | 149 | 157 | rhs: ts.Expression, | 150 | 158 | replacementOperator: ts.BinaryOperator | 151 | | -): lua.Statement { | | 159 | +): lua.Statement[] { | 152 | 160 | const left = cast(context.transformExpression(lhs), lua.isAssignmentLeftHandSideExpression); | 153 | 161 | const right = context.transformExpression(rhs); | 154 | 162 |
| @@ -172,11 +180,12 @@ export function transformCompoundAssignmentStatement( | 172 | 180 | node | 173 | 181 | ); | 174 | 182 | const assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression); | 175 | | - return lua.createDoStatement([objAndIndexDeclaration, assignStatement]); | | 183 | + return [objAndIndexDeclaration, assignStatement]; | 176 | 184 | } else { | 177 | 185 | // Simple statements | 178 | 186 | // ${left} = ${left} ${replacementOperator} ${right} | 179 | 187 | const operatorExpression = transformBinaryOperation(context, left, right, replacementOperator, node); | 180 | | - return transformAssignment(context, lhs, operatorExpression); | | 188 | + const assignmentStatements = transformAssignment(context, lhs, operatorExpression); | | 189 | + return assignmentStatements; | 181 | 190 | } | 182 | 191 | } |
Collapse file Copy file name to clipboardExpand all lines: src/transformation/visitors/binary-expression/destructuring-assignments.ts +19-15Lines changed: 19 additions & 15 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -4,9 +4,12 @@ import { TransformationContext } from "../../context"; | 4 | 4 | import { UnsupportedKind } from "../../utils/errors"; | 5 | 5 | import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; | 6 | 6 | import { isArrayType, isAssignmentPattern } from "../../utils/typescript"; | 7 | | -import { transformIdentifier } from "../identifier"; | 8 | 7 | import { transformPropertyName } from "../literal"; | 9 | | -import { transformAssignment, transformAssignmentStatement } from "./assignments"; | | 8 | +import { | | 9 | + transformAssignment, | | 10 | + transformAssignmentLeftHandSideExpression, | | 11 | + transformAssignmentStatement, | | 12 | +} from "./assignments"; | 10 | 13 |
| 11 | 14 | export function isArrayLength( | 12 | 15 | context: TransformationContext, | @@ -86,21 +89,21 @@ function transformArrayLiteralAssignmentPattern( | 86 | 89 | lua.SyntaxKind.EqualityOperator | 87 | 90 | ); | 88 | 91 |
| 89 | | - const defaultAssignmentStatement = transformAssignment( | | 92 | + const defaultAssignmentStatements = transformAssignment( | 90 | 93 | context, | 91 | 94 | (element as ts.BinaryExpression).left, | 92 | 95 | context.transformExpression((element as ts.BinaryExpression).right) | 93 | 96 | ); | 94 | 97 |
| 95 | | - const elseAssignmentStatement = transformAssignment( | | 98 | + const elseAssignmentStatements = transformAssignment( | 96 | 99 | context, | 97 | 100 | (element as ts.BinaryExpression).left, | 98 | 101 | assignedVariable | 99 | 102 | ); | 100 | 103 |
| 101 | | - const ifBlock = lua.createBlock([defaultAssignmentStatement]); | | 104 | + const ifBlock = lua.createBlock(defaultAssignmentStatements); | 102 | 105 |
| 103 | | - const elseBlock = lua.createBlock([elseAssignmentStatement]); | | 106 | + const elseBlock = lua.createBlock(elseAssignmentStatements); | 104 | 107 |
| 105 | 108 | const ifStatement = lua.createIfStatement(nilCondition, ifBlock, elseBlock, node); | 106 | 109 |
| @@ -169,14 +172,15 @@ function transformShorthandPropertyAssignment( | 169 | 172 | root: lua.Expression | 170 | 173 | ): lua.Statement[] { | 171 | 174 | const result: lua.Statement[] = []; | 172 | | - const assignmentVariableName = transformIdentifier(context, node.name); | | 175 | + const assignmentVariableName = transformAssignmentLeftHandSideExpression(context, node.name); | 173 | 176 | const extractionIndex = lua.createStringLiteral(node.name.text); | 174 | | - const variableExtractionAssignmentStatement = lua.createAssignmentStatement( | 175 | | - assignmentVariableName, | | 177 | + const variableExtractionAssignmentStatements = transformAssignment( | | 178 | + context, | | 179 | + node.name, | 176 | 180 | lua.createTableIndexExpression(root, extractionIndex) | 177 | 181 | ); | 178 | 182 |
| 179 | | - result.push(variableExtractionAssignmentStatement); | | 183 | + result.push(...variableExtractionAssignmentStatements); | 180 | 184 |
| 181 | 185 | const defaultInitializer = node.objectAssignmentInitializer | 182 | 186 | ? context.transformExpression(node.objectAssignmentInitializer) | @@ -189,9 +193,9 @@ function transformShorthandPropertyAssignment( | 189 | 193 | lua.SyntaxKind.EqualityOperator | 190 | 194 | ); | 191 | 195 |
| 192 | | - const assignment = lua.createAssignmentStatement(assignmentVariableName, defaultInitializer); | | 196 | + const assignmentStatements = transformAssignment(context, node.name, defaultInitializer); | 193 | 197 |
| 194 | | - const ifBlock = lua.createBlock([assignment]); | | 198 | + const ifBlock = lua.createBlock(assignmentStatements); | 195 | 199 |
| 196 | 200 | result.push(lua.createIfStatement(nilCondition, ifBlock, undefined, node)); | 197 | 201 | } | @@ -223,9 +227,9 @@ function transformPropertyAssignment( | 223 | 227 | const variableToExtract = transformPropertyName(context, node.name); | 224 | 228 | const extractingExpression = lua.createTableIndexExpression(root, variableToExtract); | 225 | 229 |
| 226 | | - const destructureAssignmentStatement = transformAssignment(context, leftExpression, extractingExpression); | | 230 | + const destructureAssignmentStatements = transformAssignment(context, leftExpression, extractingExpression); | 227 | 231 |
| 228 | | - result.push(destructureAssignmentStatement); | | 232 | + result.push(...destructureAssignmentStatements); | 229 | 233 |
| 230 | 234 | if (ts.isBinaryExpression(node.initializer)) { | 231 | 235 | const assignmentLeftHandSide = context.transformExpression(node.initializer.left); | @@ -274,5 +278,5 @@ function transformSpreadAssignment( | 274 | 278 | lua.createTableExpression(usedProperties) | 275 | 279 | ); | 276 | 280 |
| 277 | | - return [transformAssignment(context, node.expression, extractingExpression)]; | | 281 | + return transformAssignment(context, node.expression, extractingExpression); | 278 | 282 | } |
Collapse file Copy file name to clipboardExpand all lines: src/transformation/visitors/unary-expression.ts +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -11,7 +11,7 @@ import { | 11 | 11 | export function transformUnaryExpressionStatement( | 12 | 12 | context: TransformationContext, | 13 | 13 | node: ts.ExpressionStatement | 14 | | -): lua.Statement | undefined { | | 14 | +): lua.Statement[] | undefined { | 15 | 15 | const expression = ts.isExpressionStatement(node) ? node.expression : node; | 16 | 16 | if ( | 17 | 17 | ts.isPrefixUnaryExpression(expression) && | |
|