(transformation): · TypeScriptToLua/TypeScriptToLua@3d34a67 · GitHub
Skip to content

Commit 3d34a67

Browse files
committed
(transformation):
- implement break and continue support within try-catch-finally blocks - generalize and rename async-specific try scope properties and helpers (test): add tests for break and continue inside try-catch-finally blocks
1 parent b843c09 commit 3d34a67

4 files changed

Lines changed: 223 additions & 19 deletions

File tree

‎src/transformation/utils/scope.ts‎

Lines changed: 3 additions & 3 deletions

‎src/transformation/visitors/break-continue.ts‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import * as ts from "typescript";
22
import { LuaTarget } from "../../CompilerOptions";
33
import * as lua from "../../LuaAST";
44
import { FunctionVisitor } from "../context";
5-
import { findAsyncTryScopeBeforeLoop, findScope, LoopContinued, ScopeType } from "../utils/scope";
6-
import { isInAsyncFunction } from "../utils/typescript";
5+
import { findScope, findTryScopeBeforeLoop, LoopContinued, ScopeType } from "../utils/scope";
76

87
export const transformBreakStatement: FunctionVisitor<ts.BreakStatement> = (breakStatement, context) => {
9-
const tryScope = isInAsyncFunction(breakStatement) ? findAsyncTryScopeBeforeLoop(context) : undefined;
8+
const tryScope = findTryScopeBeforeLoop(context);
109
if (tryScope) {
11-
tryScope.asyncTryHasBreak = true;
10+
tryScope.tryHasBreak = true;
1211
return [
1312
lua.createAssignmentStatement(
1413
lua.createIdentifier("____hasBroken"),
@@ -40,9 +39,9 @@ export const transformContinueStatement: FunctionVisitor<ts.ContinueStatement> =
4039
scope.loopContinued = continuedWith;
4140
}
4241

43-
const tryScope = isInAsyncFunction(statement) ? findAsyncTryScopeBeforeLoop(context) : undefined;
42+
const tryScope = findTryScopeBeforeLoop(context);
4443
if (tryScope) {
45-
tryScope.asyncTryHasContinue = continuedWith;
44+
tryScope.tryHasContinue = continuedWith;
4645
return [
4746
lua.createAssignmentStatement(
4847
lua.createIdentifier("____hasContinued"),

‎src/transformation/visitors/errors.ts‎

Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ const transformAsyncTry: FunctionVisitor<ts.TryStatement> = (statement, context)
7979
chainCalls.push(lua.createExpressionStatement(promiseAwait, statement));
8080

8181
const hasReturn = tryScope.asyncTryHasReturn ?? catchScope?.asyncTryHasReturn;
82-
const hasBreak = tryScope.asyncTryHasBreak ?? catchScope?.asyncTryHasBreak;
83-
const hasContinue = tryScope.asyncTryHasContinue ?? catchScope?.asyncTryHasContinue;
82+
const hasBreak = tryScope.tryHasBreak ?? catchScope?.tryHasBreak;
83+
const hasContinue = tryScope.tryHasContinue ?? catchScope?.tryHasContinue;
8484

8585
// Build result in output order: flag declarations, awaiter, chain calls, post-checks
8686
const result: lua.Statement[] = [];
@@ -114,7 +114,12 @@ const transformAsyncTry: FunctionVisitor<ts.TryStatement> = (statement, context)
114114

115115
if (hasBreak) {
116116
result.push(
117-
lua.createIfStatement(lua.createIdentifier("____hasBroken"), lua.createBlock([lua.createBreakStatement()]))
117+
lua.createIfStatement(
118+
lua.createIdentifier("____hasBroken", statement),
119+
lua.createBlock([lua.createBreakStatement(statement)], statement),
120+
undefined,
121+
statement
122+
)
118123
);
119124
}
120125

@@ -125,21 +130,30 @@ const transformAsyncTry: FunctionVisitor<ts.TryStatement> = (statement, context)
125130
const continueStatements: lua.Statement[] = [];
126131
switch (hasContinue) {
127132
case LoopContinued.WithGoto:
128-
continueStatements.push(lua.createGotoStatement(label));
133+
continueStatements.push(lua.createGotoStatement(label, statement));
129134
break;
130135
case LoopContinued.WithContinue:
131-
continueStatements.push(lua.createContinueStatement());
136+
continueStatements.push(lua.createContinueStatement(statement));
132137
break;
133138
case LoopContinued.WithRepeatBreak:
134139
continueStatements.push(
135-
lua.createAssignmentStatement(lua.createIdentifier(label), lua.createBooleanLiteral(true))
140+
lua.createAssignmentStatement(
141+
lua.createIdentifier(label, statement),
142+
lua.createBooleanLiteral(true),
143+
statement
144+
)
136145
);
137-
continueStatements.push(lua.createBreakStatement());
146+
continueStatements.push(lua.createBreakStatement(statement));
138147
break;
139148
}
140149

141150
result.push(
142-
lua.createIfStatement(lua.createIdentifier("____hasContinued"), lua.createBlock(continueStatements))
151+
lua.createIfStatement(
152+
lua.createIdentifier("____hasContinued", statement),
153+
lua.createBlock(continueStatements, statement),
154+
undefined,
155+
statement
156+
)
143157
);
144158
}
145159

@@ -153,7 +167,7 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen
153167

154168
const tsTryBlock = statement.tryBlock;
155169
const tsCatchClause = statement.catchClause;
156-
const [tryBlock] = transformScopeBlock(context, tsTryBlock, ScopeType.Try);
170+
const [tryBlock, tryScope] = transformScopeBlock(context, tsTryBlock, ScopeType.Try);
157171

158172
if (
159173
(context.options.luaTarget === LuaTarget.Lua50 || context.options.luaTarget === LuaTarget.Lua51) &&
@@ -180,11 +194,13 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen
180194
result.push(lua.createVariableDeclarationStatement(tryReturnIdentifiers, tryCall, tsTryBlock));
181195

182196
const hasCatch = tsCatchClause && tsCatchClause.block.statements.length > 0;
197+
let catchScope: Scope | undefined;
183198
if (hasCatch) {
184199
// local ____catchSuccess
185200
result.push(lua.createVariableDeclarationStatement(catchSuccessIdentifier, undefined, tsCatchClause));
186201

187-
const [catchFunction] = transformCatchClause(context, tsCatchClause);
202+
const [catchFunction, cScope] = transformCatchClause(context, tsCatchClause);
203+
catchScope = cScope;
188204

189205
const catchIdentifier = lua.createIdentifier("____catch", tsCatchClause);
190206
result.push(lua.createVariableDeclarationStatement(catchIdentifier, catchFunction, tsCatchClause));
@@ -301,6 +317,71 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen
301317
);
302318
result.push(ifTrySuccessStatement);
303319

320+
// local ____hasBroken
321+
// local ____hasContinued
322+
const hasBreak = tryScope.tryHasBreak ?? catchScope?.tryHasBreak;
323+
const hasContinue = tryScope.tryHasContinue ?? catchScope?.tryHasContinue;
324+
325+
if (hasBreak || hasContinue !== undefined) {
326+
const flagDecls: lua.Identifier[] = [];
327+
if (hasBreak) flagDecls.push(lua.createIdentifier("____hasBroken", statement));
328+
if (hasContinue !== undefined) flagDecls.push(lua.createIdentifier("____hasContinued", statement));
329+
result.unshift(lua.createVariableDeclarationStatement(flagDecls, undefined, statement));
330+
}
331+
332+
// if ____hasBroken then
333+
// break
334+
// end
335+
if (hasBreak) {
336+
result.push(
337+
lua.createIfStatement(
338+
lua.createIdentifier("____hasBroken", statement),
339+
lua.createBlock([lua.createBreakStatement(statement)], statement),
340+
undefined,
341+
statement
342+
)
343+
);
344+
}
345+
346+
// if ____hasContinued then
347+
// goto __continueN (Lua 5.2+)
348+
// continue (Luau)
349+
// __continueN = true; break (Lua 5.0/5.1)
350+
// end
351+
if (hasContinue !== undefined) {
352+
const loopScope = findScope(context, ScopeType.Loop);
353+
const label = `__continue${loopScope?.id ?? ""}`;
354+
355+
const continueStatements: lua.Statement[] = [];
356+
switch (hasContinue) {
357+
case LoopContinued.WithGoto:
358+
continueStatements.push(lua.createGotoStatement(label, statement));
359+
break;
360+
case LoopContinued.WithContinue:
361+
continueStatements.push(lua.createContinueStatement(statement));
362+
break;
363+
case LoopContinued.WithRepeatBreak:
364+
continueStatements.push(
365+
lua.createAssignmentStatement(
366+
lua.createIdentifier(label, statement),
367+
lua.createBooleanLiteral(true),
368+
statement
369+
)
370+
);
371+
continueStatements.push(lua.createBreakStatement(statement));
372+
break;
373+
}
374+
375+
result.push(
376+
lua.createIfStatement(
377+
lua.createIdentifier("____hasContinued", statement),
378+
lua.createBlock(continueStatements, statement),
379+
undefined,
380+
statement
381+
)
382+
);
383+
}
384+
304385
return lua.createDoStatement(result, statement);
305386
};
306387

‎test/unit/error.spec.ts‎

Lines changed: 124 additions & 0 deletions

0 commit comments

Comments
 (0)