@@ -17,6 +17,8 @@ export enum ScopeType {
1717 Loop = 0x8 ,
1818 Conditional = 0x10 ,
1919 Block = 0x20 ,
20+ Try = 0x40 ,
21+ Catch = 0x80 ,
2022}
2123
2224interface SymbolInfo {
@@ -37,6 +39,7 @@ interface Scope {
3739 functionDefinitions ?: Map < tstl . SymbolId , FunctionDefinitionInfo > ;
3840 importStatements ?: tstl . Statement [ ] ;
3941 loopContinued ?: boolean ;
42+ functionReturned ?: boolean ;
4043}
4144
4245export interface EmitResolver {
@@ -2132,23 +2135,33 @@ export class LuaTransformer {
21322135 }
21332136
21342137 public transformReturnStatement ( statement : ts . ReturnStatement ) : StatementVisitResult {
2138+ // Bubble up explicit return flag and check if we're inside a try/catch block
2139+ let insideTryCatch = false ;
2140+ for ( let i = this . scopeStack . length - 1 ; i >= 0 ; -- i ) {
2141+ const scope = this . scopeStack [ i ] ;
2142+ scope . functionReturned = true ;
2143+
2144+ if ( scope . type === ScopeType . Function ) {
2145+ break ;
2146+ }
2147+
2148+ insideTryCatch = insideTryCatch || scope . type === ScopeType . Try || scope . type === ScopeType . Catch ;
2149+ }
2150+
2151+ let results : tstl . Expression [ ] ;
2152+
21352153 if ( statement . expression ) {
2154+ const expressionType = this . checker . getTypeAtLocation ( statement . expression ) ;
21362155 const returnType = tsHelper . getContainingFunctionReturnType ( statement , this . checker ) ;
21372156 if ( returnType ) {
2138- const expressionType = this . checker . getTypeAtLocation ( statement . expression ) ;
21392157 this . validateFunctionAssignment ( statement , expressionType , returnType ) ;
21402158 }
21412159 if ( tsHelper . isInTupleReturnFunction ( statement , this . checker ) ) {
21422160 // Parent function is a TupleReturn function
21432161 if ( ts . isArrayLiteralExpression ( statement . expression ) ) {
21442162 // If return expression is an array literal, leave out brackets.
2145- return tstl . createReturnStatement (
2146- statement . expression . elements . map ( elem => this . transformExpression ( elem ) )
2147- ) ;
2148- }
2149-
2150- const expressionType = this . checker . getTypeAtLocation ( statement . expression ) ;
2151- if (
2163+ results = statement . expression . elements . map ( elem => this . transformExpression ( elem ) ) ;
2164+ } else if (
21522165 ! tsHelper . isTupleReturnCall ( statement . expression , this . checker ) &&
21532166 tsHelper . isArrayType ( expressionType , this . checker , this . program )
21542167 ) {
@@ -2157,15 +2170,28 @@ export class LuaTransformer {
21572170 this . transformExpression ( statement . expression ) ,
21582171 statement . expression
21592172 ) ;
2160- return tstl . createReturnStatement ( [ expression ] ) ;
2173+ results = [ expression ] ;
2174+ } else {
2175+ results = [ this . transformExpression ( statement . expression ) ] ;
21612176 }
2177+
2178+ // Wrap tupleReturn results when returning inside try/catch
2179+ if ( insideTryCatch ) {
2180+ results = [ this . wrapInTable ( ...results ) ] ;
2181+ }
2182+ } else {
2183+ results = [ this . transformExpression ( statement . expression ) ] ;
21622184 }
2163- const returnExpressions = [ this . transformExpression ( statement . expression ) ] ;
2164- return tstl . createReturnStatement ( returnExpressions , statement ) ;
21652185 } else {
21662186 // Empty return
2167- return tstl . createReturnStatement ( [ ] , statement ) ;
2187+ results = [ ] ;
21682188 }
2189+
2190+ if ( insideTryCatch ) {
2191+ results . unshift ( tstl . createBooleanLiteral ( true ) ) ;
2192+ }
2193+
2194+ return tstl . createReturnStatement ( results , statement ) ;
21692195 }
21702196
21712197 public transformIfStatement ( statement : ts . IfStatement ) : StatementVisitResult {
@@ -2592,39 +2618,104 @@ export class LuaTransformer {
25922618 }
25932619 }
25942620
2621+ protected transformScopeBlock ( block : ts . Block , scopeType : ScopeType ) : [ tstl . Block , Scope ] {
2622+ this . pushScope ( scopeType ) ;
2623+ const statements = this . performHoisting ( this . transformStatements ( block . statements ) ) ;
2624+ const scope = this . popScope ( ) ;
2625+ return [ tstl . createBlock ( statements , block ) , scope ] ;
2626+ }
2627+
25952628 public transformTryStatement ( statement : ts . TryStatement ) : StatementVisitResult {
2596- const pCall = tstl . createIdentifier ( "pcall" ) ;
2597- const tryBlock = this . transformBlock ( statement . tryBlock ) ;
2598- const tryCall = tstl . createCallExpression ( pCall , [ tstl . createFunctionExpression ( tryBlock ) ] ) ;
2629+ const [ tryBlock , tryScope ] = this . transformScopeBlock ( statement . tryBlock , ScopeType . Try ) ;
2630+
2631+ const tryResultIdentfier = tstl . createIdentifier ( "____TS_try" ) ;
2632+ const returnValueIdentifier = tstl . createIdentifier ( "____TS_returnValue" ) ;
25992633
26002634 const result : tstl . Statement [ ] = [ ] ;
26012635
2602- if ( statement . catchClause ) {
2603- const tryResult = tstl . createIdentifier ( "____TS_try" ) ;
2636+ let returnedIdentifier : tstl . Identifier | undefined ;
2637+ let returnCondition : tstl . Expression | undefined ;
26042638
2605- const returnVariables =
2606- statement . catchClause && statement . catchClause . variableDeclaration
2607- ? [
2608- tryResult ,
2609- this . transformIdentifier ( statement . catchClause . variableDeclaration . name as ts . Identifier ) ,
2610- ]
2611- : [ tryResult ] ;
2639+ const pCall = tstl . createIdentifier ( "pcall" ) ;
2640+ const tryCall = tstl . createCallExpression ( pCall , [ tstl . createFunctionExpression ( tryBlock ) ] ) ;
26122641
2613- const catchAssignment = tstl . createVariableDeclarationStatement ( returnVariables , tryCall ) ;
2642+ if ( statement . catchClause && statement . catchClause . block . statements . length > 0 ) {
2643+ // try with catch
2644+ let [ catchBlock , catchScope ] = this . transformScopeBlock ( statement . catchClause . block , ScopeType . Catch ) ;
2645+ if ( statement . catchClause . variableDeclaration ) {
2646+ // Replace ____TS_returned with catch variable
2647+ returnedIdentifier = this . transformIdentifier ( statement . catchClause . variableDeclaration
2648+ . name as ts . Identifier ) ;
2649+ } else if ( tryScope . functionReturned || catchScope . functionReturned ) {
2650+ returnedIdentifier = tstl . createIdentifier ( "____TS_returned" ) ;
2651+ }
26142652
2615- result . push ( catchAssignment ) ;
2653+ const tryReturnIdentifiers = [ tryResultIdentfier ] ; // ____TS_try
2654+ if ( returnedIdentifier ) {
2655+ tryReturnIdentifiers . push ( returnedIdentifier ) ; // ____TS_returned or catch variable
2656+ if ( tryScope . functionReturned || catchScope . functionReturned ) {
2657+ tryReturnIdentifiers . push ( returnValueIdentifier ) ; // ____TS_returnValue
2658+ returnCondition = tstl . cloneIdentifier ( returnedIdentifier ) ;
2659+ }
2660+ }
2661+ result . push ( tstl . createVariableDeclarationStatement ( tryReturnIdentifiers , tryCall ) ) ;
26162662
2617- const notTryResult = tstl . createUnaryExpression (
2618- tstl . createParenthesizedExpression ( tryResult ) ,
2663+ if ( ( tryScope . functionReturned || catchScope . functionReturned ) && returnedIdentifier ) {
2664+ // Wrap catch in function if try or catch has return
2665+ const catchCall = tstl . createCallExpression (
2666+ tstl . createParenthesizedExpression ( tstl . createFunctionExpression ( catchBlock ) )
2667+ ) ;
2668+ const catchAssign = tstl . createAssignmentStatement (
2669+ [ tstl . cloneIdentifier ( returnedIdentifier ) , tstl . cloneIdentifier ( returnValueIdentifier ) ] ,
2670+ catchCall
2671+ ) ;
2672+ catchBlock = tstl . createBlock ( [ catchAssign ] ) ;
2673+ }
2674+ const notTryCondition = tstl . createUnaryExpression (
2675+ tstl . createParenthesizedExpression ( tryResultIdentfier ) ,
26192676 tstl . SyntaxKind . NotOperator
26202677 ) ;
2621- result . push ( tstl . createIfStatement ( notTryResult , this . transformBlock ( statement . catchClause . block ) ) ) ;
2678+ result . push ( tstl . createIfStatement ( notTryCondition , catchBlock ) ) ;
2679+ } else if ( tryScope . functionReturned ) {
2680+ // try with return, but no catch
2681+ returnedIdentifier = tstl . createIdentifier ( "____TS_returned" ) ;
2682+ const returnedVariables = [ tryResultIdentfier , returnedIdentifier , returnValueIdentifier ] ;
2683+ result . push ( tstl . createVariableDeclarationStatement ( returnedVariables , tryCall ) ) ;
2684+
2685+ // change return condition from '____TS_returned' to '____TS_try and ____TS_returned'
2686+ returnCondition = tstl . createBinaryExpression (
2687+ tstl . cloneIdentifier ( tryResultIdentfier ) ,
2688+ returnedIdentifier ,
2689+ tstl . SyntaxKind . AndOperator
2690+ ) ;
26222691 } else {
2692+ // try without return or catch
26232693 result . push ( tstl . createExpressionStatement ( tryCall ) ) ;
26242694 }
26252695
2626- if ( statement . finallyBlock ) {
2627- result . push ( tstl . createDoStatement ( this . transformBlock ( statement . finallyBlock ) . statements ) ) ;
2696+ if ( statement . finallyBlock && statement . finallyBlock . statements . length > 0 ) {
2697+ result . push ( ...this . statementVisitResultToArray ( this . transformBlockAsDoStatement ( statement . finallyBlock ) ) ) ;
2698+ }
2699+
2700+ if ( returnCondition && returnedIdentifier ) {
2701+ // With catch clause:
2702+ // if ____TS_returned then return ____TS_returnValue end
2703+ // No catch clause:
2704+ // if ____TS_try and ____TS_returned then return ____TS_returnValue end
2705+ const returnValues : tstl . Expression [ ] = [ ] ;
2706+ const parentTryCatch = this . findScope ( ScopeType . Function | ScopeType . Try | ScopeType . Catch ) ;
2707+ if ( parentTryCatch && parentTryCatch . type !== ScopeType . Function ) {
2708+ // Nested try/catch needs to prefix a 'true' return value
2709+ returnValues . push ( tstl . createBooleanLiteral ( true ) ) ;
2710+ }
2711+ if ( tsHelper . isInTupleReturnFunction ( statement , this . checker ) ) {
2712+ returnValues . push ( this . createUnpackCall ( tstl . cloneIdentifier ( returnValueIdentifier ) ) ) ;
2713+ } else {
2714+ returnValues . push ( tstl . cloneIdentifier ( returnValueIdentifier ) ) ;
2715+ }
2716+ const returnStatement = tstl . createReturnStatement ( returnValues ) ;
2717+ const ifReturnedStatement = tstl . createIfStatement ( returnCondition , tstl . createBlock ( [ returnStatement ] ) ) ;
2718+ result . push ( ifReturnedStatement ) ;
26282719 }
26292720
26302721 return tstl . createDoStatement ( result , statement ) ;
@@ -4980,7 +5071,7 @@ export class LuaTransformer {
49805071 return tstl . createCallExpression ( tstl . createParenthesizedExpression ( iife ) , [ ] , tsOriginal ) ;
49815072 }
49825073
4983- protected createUnpackCall ( expression : tstl . Expression | undefined , tsOriginal : ts . Node ) : tstl . Expression {
5074+ protected createUnpackCall ( expression : tstl . Expression | undefined , tsOriginal ? : ts . Node ) : tstl . Expression {
49845075 switch ( this . luaTarget ) {
49855076 case LuaTarget . Lua51 :
49865077 case LuaTarget . LuaJIT :
0 commit comments