Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/transformation/visitors/class/members/constructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { transformParameters, transformFunctionBodyStatements, transformFunction
import { TransformationContext } from "../../../context";
import { transformIdentifier } from "../../identifier";
import { transformClassInstanceFields } from "./fields";
import { pushScope, ScopeType, popScope } from "../../../utils/scope";

export function createConstructorName(className: lua.Identifier): lua.TableIndexExpression {
return lua.createTableIndexExpression(
Expand All @@ -26,7 +27,8 @@ export function transformConstructorDeclaration(
}

// Transform body
const [body, scope] = transformFunctionBodyStatements(context, statement.body);
const scope = pushScope(context, ScopeType.Function);
const body = transformFunctionBodyStatements(context, statement.body);

const [params, dotsLiteral, restParamName] = transformParameters(
context,
Expand Down Expand Up @@ -81,6 +83,8 @@ export function transformConstructorDeclaration(

const constructorWasGenerated = statement.pos === -1;

popScope(context);

return lua.createAssignmentStatement(
createConstructorName(className),
lua.createFunctionExpression(
Expand Down
13 changes: 5 additions & 8 deletions src/transformation/visitors/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,9 @@ function isRestParameterReferenced(context: TransformationContext, identifier: l
return references.some(r => !r.parent || !ts.isSpreadElement(r.parent) || !isVarArgType(context, r));
}

export function transformFunctionBodyStatements(
context: TransformationContext,
body: ts.Block
): [lua.Statement[], Scope] {
pushScope(context, ScopeType.Function);
export function transformFunctionBodyStatements(context: TransformationContext, body: ts.Block): lua.Statement[] {
const bodyStatements = performHoisting(context, context.transformStatements(body.statements));
const scope = popScope(context);
return [bodyStatements, scope];
return bodyStatements;
}

export function transformFunctionBodyHeader(
Expand Down Expand Up @@ -116,8 +111,10 @@ export function transformFunctionBody(
body: ts.Block,
spreadIdentifier?: lua.Identifier
): [lua.Statement[], Scope] {
const [bodyStatements, scope] = transformFunctionBodyStatements(context, body);
const scope = pushScope(context, ScopeType.Function);
const bodyStatements = transformFunctionBodyStatements(context, body);
const headerStatements = transformFunctionBodyHeader(context, scope, parameters, spreadIdentifier);
popScope(context);
return [[...headerStatements, ...bodyStatements], scope];
}

Expand Down
39 changes: 23 additions & 16 deletions src/transformation/visitors/loops/for-of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,59 +14,66 @@ import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib";
import { isArrayType, isNumberType } from "../../utils/typescript";
import { transformArguments } from "../call";
import { transformIdentifier } from "../identifier";
import { transformArrayBindingElement, transformVariableDeclaration } from "../variable-declaration";
import {
transformBindingPattern,
transformArrayBindingElement,
transformVariableDeclaration,
} from "../variable-declaration";
import { getVariableDeclarationBinding, transformLoopBody } from "./utils";

function transformForOfInitializer(
context: TransformationContext,
initializer: ts.ForInitializer,
expression: lua.Expression
): lua.Statement | undefined {
expression: lua.Identifier
): lua.Statement[] {
if (ts.isVariableDeclarationList(initializer)) {
const binding = getVariableDeclarationBinding(initializer);
// Declaration of new variable
if (ts.isArrayBindingPattern(binding)) {
if (binding.elements.length === 0) {
// Ignore empty destructuring assignment
return undefined;
return [];
}

expression = createUnpackCall(context, expression, initializer);
return transformBindingPattern(context, binding, expression);
} else if (ts.isObjectBindingPattern(binding)) {
throw UnsupportedObjectDestructuringInForOf(initializer);
}

const variableStatements = transformVariableDeclaration(context, initializer.declarations[0]);
if (variableStatements[0]) {
// we can safely assume that for vars are not exported and therefore declarationstatenents
return lua.createVariableDeclarationStatement(
(variableStatements[0] as lua.VariableDeclarationStatement).left,
expression
);
return [
lua.createVariableDeclarationStatement(
(variableStatements[0] as lua.VariableDeclarationStatement).left,
expression
),
];
} else {
throw MissingForOfVariables(initializer);
}
} else {
// Assignment to existing variable
let variables: lua.AssignmentLeftHandSideExpression | lua.AssignmentLeftHandSideExpression[];
let valueExpression: lua.Expression = expression;
if (ts.isArrayLiteralExpression(initializer)) {
if (initializer.elements.length > 0) {
expression = createUnpackCall(context, expression, initializer);
valueExpression = createUnpackCall(context, expression, initializer);
variables = castEach(
initializer.elements.map(e => context.transformExpression(e)),
lua.isAssignmentLeftHandSideExpression
);
} else {
// Ignore empty destructring assignment
return undefined;
return [];
}
} else if (ts.isObjectLiteralExpression(initializer)) {
throw UnsupportedObjectDestructuringInForOf(initializer);
} else {
variables = cast(context.transformExpression(initializer), lua.isAssignmentLeftHandSideExpression);
}

return lua.createAssignmentStatement(variables, expression);
return [lua.createAssignmentStatement(variables, valueExpression)];
}
}

Expand Down Expand Up @@ -178,7 +185,7 @@ function transformForOfLuaIteratorStatement(
const valueVariable = lua.createIdentifier("____value");
const initializer = transformForOfInitializer(context, statement.initializer, valueVariable);
if (initializer) {
block.statements.splice(0, 0, initializer);
block.statements.splice(0, 0, ...initializer);
}
return lua.createForInStatement(block, [valueVariable], [luaIterator]);
}
Expand All @@ -198,7 +205,7 @@ function transformForOfArrayStatement(
valueVariable = lua.createIdentifier("____values");
const initializer = transformForOfInitializer(context, statement.initializer, valueVariable);
if (initializer) {
block.statements.unshift(initializer);
block.statements.unshift(...initializer);
}
} else {
valueVariable = transformIdentifier(context, binding);
Expand All @@ -208,7 +215,7 @@ function transformForOfArrayStatement(
valueVariable = lua.createIdentifier("____value");
const initializer = transformForOfInitializer(context, statement.initializer, valueVariable);
if (initializer) {
block.statements.unshift(initializer);
block.statements.unshift(...initializer);
}
}

Expand Down Expand Up @@ -244,7 +251,7 @@ function transformForOfIteratorStatement(
const valueVariable = lua.createIdentifier("____value");
const initializer = transformForOfInitializer(context, statement.initializer, valueVariable);
if (initializer) {
block.statements.unshift(initializer);
block.statements.unshift(...initializer);
}

return lua.createForInStatement(
Expand Down
1 change: 1 addition & 0 deletions src/transformation/visitors/switch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const transformSwitchStatement: FunctionVisitor<ts.SwitchStatement> = (st
}

const scope = pushScope(context, ScopeType.Switch);

// Give the switch a unique name to prevent nested switches from acting up.
const switchName = `____switch${scope.id}`;
const switchVariable = lua.createIdentifier(switchName);
Expand Down
167 changes: 87 additions & 80 deletions src/transformation/visitors/variable-declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,92 @@ export function transformBindingPattern(
return result;
}

export function transformBindingVariableDeclaration(
context: TransformationContext,
bindingPattern: ts.BindingPattern,
initializer?: ts.Expression
): lua.Statement[] {
const statements: lua.Statement[] = [];

// For object, nested or rest bindings fall back to transformBindingPattern
const isComplexBindingElement = (e: ts.ArrayBindingElement) =>
ts.isBindingElement(e) && (!ts.isIdentifier(e.name) || e.dotDotDotToken);

if (ts.isObjectBindingPattern(bindingPattern) || bindingPattern.elements.some(isComplexBindingElement)) {
let table: lua.Identifier;
if (initializer !== undefined && ts.isIdentifier(initializer)) {
table = transformIdentifier(context, initializer);
} else {
// Contain the expression in a temporary variable
table = lua.createAnonymousIdentifier();
if (initializer) {
statements.push(
lua.createVariableDeclarationStatement(table, context.transformExpression(initializer))
);
}
}
statements.push(...transformBindingPattern(context, bindingPattern, table));
return statements;
}

const vars =
bindingPattern.elements.length > 0
? bindingPattern.elements.map(e => transformArrayBindingElement(context, e))
: lua.createAnonymousIdentifier();

if (initializer) {
if (isTupleReturnCall(context, initializer)) {
// Don't unpack @tupleReturn annotated functions
statements.push(
...createLocalOrExportedOrGlobalDeclaration(
context,
vars,
context.transformExpression(initializer),
initializer
)
);
} else if (ts.isArrayLiteralExpression(initializer)) {
// Don't unpack array literals
const values =
initializer.elements.length > 0
? initializer.elements.map(e => context.transformExpression(e))
: lua.createNilLiteral();
statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, values, initializer));
} else {
// local vars = this.transpileDestructingAssignmentValue(node.initializer);
const unpackedInitializer = createUnpackCall(
context,
context.transformExpression(initializer),
initializer
);
statements.push(
...createLocalOrExportedOrGlobalDeclaration(context, vars, unpackedInitializer, initializer)
);
}
} else {
statements.push(
...createLocalOrExportedOrGlobalDeclaration(context, vars, lua.createNilLiteral(), initializer)
);
}

for (const element of bindingPattern.elements) {
if (!ts.isOmittedExpression(element) && element.initializer) {
const variableName = transformIdentifier(context, element.name as ts.Identifier);
const identifier = addExportToIdentifier(context, variableName);
statements.push(
lua.createIfStatement(
lua.createBinaryExpression(identifier, lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator),
lua.createBlock([
lua.createAssignmentStatement(identifier, context.transformExpression(element.initializer)),
])
)
);
}
}

return statements;
}

// TODO: FunctionVisitor<ts.VariableDeclaration>
export function transformVariableDeclaration(
context: TransformationContext,
Expand All @@ -137,86 +223,7 @@ export function transformVariableDeclaration(
const value = statement.initializer && context.transformExpression(statement.initializer);
return createLocalOrExportedOrGlobalDeclaration(context, identifierName, value, statement);
} else if (ts.isArrayBindingPattern(statement.name) || ts.isObjectBindingPattern(statement.name)) {
const statements: lua.Statement[] = [];

// For object, nested or rest bindings fall back to transformBindingPattern
if (
ts.isObjectBindingPattern(statement.name) ||
statement.name.elements.some(e => ts.isBindingElement(e) && (!ts.isIdentifier(e.name) || e.dotDotDotToken))
) {
let table: lua.Identifier;
if (statement.initializer !== undefined && ts.isIdentifier(statement.initializer)) {
table = transformIdentifier(context, statement.initializer);
} else {
// Contain the expression in a temporary variable
table = lua.createAnonymousIdentifier();
if (statement.initializer) {
statements.push(
lua.createVariableDeclarationStatement(
table,
context.transformExpression(statement.initializer)
)
);
}
}
statements.push(...transformBindingPattern(context, statement.name, table));
return statements;
}

const vars =
statement.name.elements.length > 0
? statement.name.elements.map(e => transformArrayBindingElement(context, e))
: lua.createAnonymousIdentifier(statement.name);

if (statement.initializer) {
if (isTupleReturnCall(context, statement.initializer)) {
// Don't unpack @tupleReturn annotated functions
statements.push(
...createLocalOrExportedOrGlobalDeclaration(
context,
vars,
context.transformExpression(statement.initializer),
statement
)
);
} else if (ts.isArrayLiteralExpression(statement.initializer)) {
// Don't unpack array literals
const values =
statement.initializer.elements.length > 0
? statement.initializer.elements.map(e => context.transformExpression(e))
: lua.createNilLiteral();
statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, values, statement));
} else {
// local vars = this.transpileDestructingAssignmentValue(node.initializer);
const initializer = createUnpackCall(
context,
context.transformExpression(statement.initializer),
statement.initializer
);
statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, initializer, statement));
}
} else {
statements.push(
...createLocalOrExportedOrGlobalDeclaration(context, vars, lua.createNilLiteral(), statement)
);
}

for (const element of statement.name.elements) {
if (!ts.isOmittedExpression(element) && element.initializer) {
const variableName = transformIdentifier(context, element.name as ts.Identifier);
const identifier = addExportToIdentifier(context, variableName);
statements.push(
lua.createIfStatement(
lua.createBinaryExpression(identifier, lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator),
lua.createBlock([
lua.createAssignmentStatement(identifier, context.transformExpression(element.initializer)),
])
)
);
}
}

return statements;
return transformBindingVariableDeclaration(context, statement.name, statement.initializer);
} else {
return assertNever(statement.name);
}
Expand Down
Loading