| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 7579194 commit 979e9eb
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -39,8 +39,8 @@ export interface Scope { | |||
| 39 | 39 | loopContinued?: LoopContinued; | |
| 40 | 40 | functionReturned?: boolean; | |
| 41 | 41 | asyncTryHasReturn?: boolean; | |
| 42 | - asyncTryHasBreak?: boolean; | ||
| 43 | - asyncTryHasContinue?: LoopContinued; | ||
| 42 | + tryHasBreak?: boolean; | ||
| 43 | + tryHasContinue?: LoopContinued; | ||
| 44 | 44 | } | |
| 45 | 45 | ||
| 46 | 46 | export interface HoistingResult { | |
@@ -96,7 +96,7 @@ export function findAsyncTryScopeInStack(context: TransformationContext): Scope | |||
| 96 | 96 | } | |
| 97 | 97 | ||
| 98 | 98 | /** Like findAsyncTryScopeInStack, but also stops at Loop boundaries. */ | |
| 99 | - export function findAsyncTryScopeBeforeLoop(context: TransformationContext): Scope | undefined { | ||
| 99 | + export function findTryScopeBeforeLoop(context: TransformationContext): Scope | undefined { | ||
| 100 | 100 | for (const scope of walkScopesUp(context)) { | |
| 101 | 101 | if (scope.type === ScopeType.Function || scope.type === ScopeType.Loop) return undefined; | |
| 102 | 102 | if (scope.type === ScopeType.Try || scope.type === ScopeType.Catch) return scope; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,13 +2,12 @@ import * as ts from "typescript"; | |||
| 2 | 2 | import { LuaTarget } from "../../CompilerOptions"; | |
| 3 | 3 | import * as lua from "../../LuaAST"; | |
| 4 | 4 | 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"; | ||
| 7 | 6 | ||
| 8 | 7 | export const transformBreakStatement: FunctionVisitor<ts.BreakStatement> = (breakStatement, context) => { | |
| 9 | - const tryScope = isInAsyncFunction(breakStatement) ? findAsyncTryScopeBeforeLoop(context) : undefined; | ||
| 8 | + const tryScope = findTryScopeBeforeLoop(context); | ||
| 10 | 9 | if (tryScope) { | |
| 11 | - tryScope.asyncTryHasBreak = true; | ||
| 10 | + tryScope.tryHasBreak = true; | ||
| 12 | 11 | return [ | |
| 13 | 12 | lua.createAssignmentStatement( | |
| 14 | 13 | lua.createIdentifier("____hasBroken"), | |
@@ -40,9 +39,9 @@ export const transformContinueStatement: FunctionVisitor<ts.ContinueStatement> = | |||
| 40 | 39 | scope.loopContinued = continuedWith; | |
| 41 | 40 | } | |
| 42 | 41 | ||
| 43 | - const tryScope = isInAsyncFunction(statement) ? findAsyncTryScopeBeforeLoop(context) : undefined; | ||
| 42 | + const tryScope = findTryScopeBeforeLoop(context); | ||
| 44 | 43 | if (tryScope) { | |
| 45 | - tryScope.asyncTryHasContinue = continuedWith; | ||
| 44 | + tryScope.tryHasContinue = continuedWith; | ||
| 46 | 45 | return [ | |
| 47 | 46 | lua.createAssignmentStatement( | |
| 48 | 47 | lua.createIdentifier("____hasContinued"), | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -79,8 +79,8 @@ const transformAsyncTry: FunctionVisitor<ts.TryStatement> = (statement, context) | |||
| 79 | 79 | chainCalls.push(lua.createExpressionStatement(promiseAwait, statement)); | |
| 80 | 80 | ||
| 81 | 81 | 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; | ||
| 84 | 84 | ||
| 85 | 85 | // Build result in output order: flag declarations, awaiter, chain calls, post-checks | |
| 86 | 86 | const result: lua.Statement[] = []; | |
@@ -114,7 +114,12 @@ const transformAsyncTry: FunctionVisitor<ts.TryStatement> = (statement, context) | |||
| 114 | 114 | ||
| 115 | 115 | if (hasBreak) { | |
| 116 | 116 | 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 | + ) | ||
| 118 | 123 | ); | |
| 119 | 124 | } | |
| 120 | 125 | ||
@@ -125,21 +130,30 @@ const transformAsyncTry: FunctionVisitor<ts.TryStatement> = (statement, context) | |||
| 125 | 130 | const continueStatements: lua.Statement[] = []; | |
| 126 | 131 | switch (hasContinue) { | |
| 127 | 132 | case LoopContinued.WithGoto: | |
| 128 | - continueStatements.push(lua.createGotoStatement(label)); | ||
| 133 | + continueStatements.push(lua.createGotoStatement(label, statement)); | ||
| 129 | 134 | break; | |
| 130 | 135 | case LoopContinued.WithContinue: | |
| 131 | - continueStatements.push(lua.createContinueStatement()); | ||
| 136 | + continueStatements.push(lua.createContinueStatement(statement)); | ||
| 132 | 137 | break; | |
| 133 | 138 | case LoopContinued.WithRepeatBreak: | |
| 134 | 139 | 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 | + ) | ||
| 136 | 145 | ); | |
| 137 | - continueStatements.push(lua.createBreakStatement()); | ||
| 146 | + continueStatements.push(lua.createBreakStatement(statement)); | ||
| 138 | 147 | break; | |
| 139 | 148 | } | |
| 140 | 149 | ||
| 141 | 150 | 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 | + ) | ||
| 143 | 157 | ); | |
| 144 | 158 | } | |
| 145 | 159 | ||
@@ -153,7 +167,7 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen | |||
| 153 | 167 | ||
| 154 | 168 | const tsTryBlock = statement.tryBlock; | |
| 155 | 169 | const tsCatchClause = statement.catchClause; | |
| 156 | - const [tryBlock] = transformScopeBlock(context, tsTryBlock, ScopeType.Try); | ||
| 170 | + const [tryBlock, tryScope] = transformScopeBlock(context, tsTryBlock, ScopeType.Try); | ||
| 157 | 171 | ||
| 158 | 172 | if ( | |
| 159 | 173 | (context.options.luaTarget === LuaTarget.Lua50 || context.options.luaTarget === LuaTarget.Lua51) && | |
@@ -180,11 +194,13 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen | |||
| 180 | 194 | result.push(lua.createVariableDeclarationStatement(tryReturnIdentifiers, tryCall, tsTryBlock)); | |
| 181 | 195 | ||
| 182 | 196 | const hasCatch = tsCatchClause && tsCatchClause.block.statements.length > 0; | |
| 197 | + let catchScope: Scope | undefined; | ||
| 183 | 198 | if (hasCatch) { | |
| 184 | 199 | // local ____catchSuccess | |
| 185 | 200 | result.push(lua.createVariableDeclarationStatement(catchSuccessIdentifier, undefined, tsCatchClause)); | |
| 186 | 201 | ||
| 187 | - const [catchFunction] = transformCatchClause(context, tsCatchClause); | ||
| 202 | + const [catchFunction, cScope] = transformCatchClause(context, tsCatchClause); | ||
| 203 | + catchScope = cScope; | ||
| 188 | 204 | ||
| 189 | 205 | const catchIdentifier = lua.createIdentifier("____catch", tsCatchClause); | |
| 190 | 206 | result.push(lua.createVariableDeclarationStatement(catchIdentifier, catchFunction, tsCatchClause)); | |
@@ -301,6 +317,71 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen | |||
| 301 | 317 | ); | |
| 302 | 318 | result.push(ifTrySuccessStatement); | |
| 303 | 319 | ||
| 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 | + | ||
| 304 | 385 | return lua.createDoStatement(result, statement); | |
| 305 | 386 | }; | |
| 306 | 387 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -689,6 +689,130 @@ test("try/finally rethrow with non-string error", () => { | |||
| 689 | 689 | `.expectToMatchJsResult(); | |
| 690 | 690 | }); | |
| 691 | 691 | ||
| 692 | + test("break inside try in loop", () => { | ||
| 693 | + util.testFunction` | ||
| 694 | + const result: number[] = []; | ||
| 695 | + for (let i = 0; i < 5; i++) { | ||
| 696 | + try { | ||
| 697 | + if (i === 3) break; | ||
| 698 | + result.push(i); | ||
| 699 | + } catch {} | ||
| 700 | + } | ||
| 701 | + return result; | ||
| 702 | + `.expectToMatchJsResult(); | ||
| 703 | + }); | ||
| 704 | + | ||
| 705 | + test("continue inside try in loop", () => { | ||
| 706 | + util.testFunction` | ||
| 707 | + const result: number[] = []; | ||
| 708 | + for (let i = 0; i < 5; i++) { | ||
| 709 | + try { | ||
| 710 | + if (i === 2) continue; | ||
| 711 | + result.push(i); | ||
| 712 | + } catch {} | ||
| 713 | + } | ||
| 714 | + return result; | ||
| 715 | + `.expectToMatchJsResult(); | ||
| 716 | + }); | ||
| 717 | + | ||
| 718 | + test("break inside catch in loop", () => { | ||
| 719 | + util.testFunction` | ||
| 720 | + const result: number[] = []; | ||
| 721 | + for (let i = 0; i < 5; i++) { | ||
| 722 | + try { | ||
| 723 | + throw i; | ||
| 724 | + } catch (e: any) { | ||
| 725 | + if (e === 3) break; | ||
| 726 | + result.push(e); | ||
| 727 | + } | ||
| 728 | + } | ||
| 729 | + return result; | ||
| 730 | + `.expectToMatchJsResult(); | ||
| 731 | + }); | ||
| 732 | + | ||
| 733 | + test("continue inside catch in loop", () => { | ||
| 734 | + util.testFunction` | ||
| 735 | + const result: number[] = []; | ||
| 736 | + for (let i = 0; i < 5; i++) { | ||
| 737 | + try { | ||
| 738 | + throw i; | ||
| 739 | + } catch (e: any) { | ||
| 740 | + if (e === 2) continue; | ||
| 741 | + result.push(e); | ||
| 742 | + } | ||
| 743 | + } | ||
| 744 | + return result; | ||
| 745 | + `.expectToMatchJsResult(); | ||
| 746 | + }); | ||
| 747 | + | ||
| 748 | + test("break inside try with finally in loop", () => { | ||
| 749 | + util.testFunction` | ||
| 750 | + const result: number[] = []; | ||
| 751 | + let finallyCalls = 0; | ||
| 752 | + for (let i = 0; i < 5; i++) { | ||
| 753 | + try { | ||
| 754 | + if (i === 3) break; | ||
| 755 | + result.push(i); | ||
| 756 | + } finally { | ||
| 757 | + finallyCalls++; | ||
| 758 | + } | ||
| 759 | + } | ||
| 760 | + return { result, finallyCalls }; | ||
| 761 | + `.expectToMatchJsResult(); | ||
| 762 | + }); | ||
| 763 | + | ||
| 764 | + test("continue inside try with finally in loop", () => { | ||
| 765 | + util.testFunction` | ||
| 766 | + const result: number[] = []; | ||
| 767 | + let finallyCalls = 0; | ||
| 768 | + for (let i = 0; i < 5; i++) { | ||
| 769 | + try { | ||
| 770 | + if (i === 2) continue; | ||
| 771 | + result.push(i); | ||
| 772 | + } finally { | ||
| 773 | + finallyCalls++; | ||
| 774 | + } | ||
| 775 | + } | ||
| 776 | + return { result, finallyCalls }; | ||
| 777 | + `.expectToMatchJsResult(); | ||
| 778 | + }); | ||
| 779 | + | ||
| 780 | + test("break inside catch with finally in loop", () => { | ||
| 781 | + util.testFunction` | ||
| 782 | + const result: number[] = []; | ||
| 783 | + let finallyCalls = 0; | ||
| 784 | + for (let i = 0; i < 5; i++) { | ||
| 785 | + try { | ||
| 786 | + throw i; | ||
| 787 | + } catch (e: any) { | ||
| 788 | + if (e === 3) break; | ||
| 789 | + result.push(e); | ||
| 790 | + } finally { | ||
| 791 | + finallyCalls++; | ||
| 792 | + } | ||
| 793 | + } | ||
| 794 | + return { result, finallyCalls }; | ||
| 795 | + `.expectToMatchJsResult(); | ||
| 796 | + }); | ||
| 797 | + | ||
| 798 | + test("continue inside catch with finally in loop", () => { | ||
| 799 | + util.testFunction` | ||
| 800 | + const result: number[] = []; | ||
| 801 | + let finallyCalls = 0; | ||
| 802 | + for (let i = 0; i < 5; i++) { | ||
| 803 | + try { | ||
| 804 | + throw i; | ||
| 805 | + } catch (e: any) { | ||
| 806 | + if (e === 2) continue; | ||
| 807 | + result.push(e); | ||
| 808 | + } finally { | ||
| 809 | + finallyCalls++; | ||
| 810 | + } | ||
| 811 | + } | ||
| 812 | + return { result, finallyCalls }; | ||
| 813 | + `.expectToMatchJsResult(); | ||
| 814 | + }); | ||
| 815 | + | ||
| 692 | 816 | util.testEachVersion( | |
| 693 | 817 | "error stacktrace omits constructor and __TS_New", | |
| 694 | 818 | () => util.testFunction` | |
| Back | FazBrowse Home | New Git URL |
0 commit comments