@@ -4,7 +4,7 @@ import * as lua from "../../LuaAST";
44import { FunctionVisitor , TransformationContext } from "../context" ;
55import { unsupportedForTarget , unsupportedForTargetButOverrideAvailable } from "../utils/diagnostics" ;
66import { createUnpackCall } from "../utils/lua-ast" ;
7- import { transformLuaLibFunction } from "../utils/lualib" ;
7+ import { importLuaLibFeature , transformLuaLibFunction } from "../utils/lualib" ;
88import { findScope , LoopContinued , Scope , ScopeType } from "../utils/scope" ;
99import { isInAsyncFunction , isInGeneratorFunction } from "../utils/typescript" ;
1010import { wrapInAsyncAwaiter } from "./async-await" ;
@@ -171,8 +171,22 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen
171171 const returnedIdentifier = lua . createIdentifier ( "____hasReturned" ) ;
172172 let returnCondition : lua . Expression | undefined ;
173173
174- const pCall = lua . createIdentifier ( "pcall" ) ;
175- const tryCall = lua . createCallExpression ( pCall , [ lua . createFunctionExpression ( tryBlock ) ] ) ;
174+ // On Lua 5.5 (and Universal, which must work on 5.5), a `nil` error object
175+ // is replaced by a string when it propagates out of a protected call. To
176+ // preserve `throw undefined` semantics, route through xpcall with a message
177+ // handler that wraps nil into a sentinel, and unwrap before the user catch.
178+ const wrapErrorObjects =
179+ context . options . luaTarget === LuaTarget . Lua55 || context . options . luaTarget === LuaTarget . Universal ;
180+ if ( wrapErrorObjects ) {
181+ importLuaLibFeature ( context , LuaLibFeature . ErrorObject ) ;
182+ }
183+
184+ const tryCall = wrapErrorObjects
185+ ? lua . createCallExpression ( lua . createIdentifier ( "xpcall" ) , [
186+ lua . createFunctionExpression ( tryBlock ) ,
187+ lua . createIdentifier ( "__TS__WrapErrorObject" ) ,
188+ ] )
189+ : lua . createCallExpression ( lua . createIdentifier ( "pcall" ) , [ lua . createFunctionExpression ( tryBlock ) ] ) ;
176190
177191 if ( statement . catchClause && statement . catchClause . block . statements . length > 0 ) {
178192 // try with catch
@@ -192,9 +206,14 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen
192206 }
193207 result . push ( lua . createVariableDeclarationStatement ( tryReturnIdentifiers , tryCall ) ) ;
194208
209+ const catchArg = wrapErrorObjects
210+ ? lua . createCallExpression ( lua . createIdentifier ( "__TS__UnwrapErrorObject" ) , [
211+ lua . cloneIdentifier ( returnedIdentifier ) ,
212+ ] )
213+ : lua . cloneIdentifier ( returnedIdentifier ) ;
195214 const catchCall = lua . createCallExpression (
196215 catchIdentifier ,
197- statement . catchClause . variableDeclaration ? [ lua . cloneIdentifier ( returnedIdentifier ) ] : [ ]
216+ statement . catchClause . variableDeclaration ? [ catchArg ] : [ ]
198217 ) ;
199218 const catchCallStatement = hasReturn
200219 ? lua . createAssignmentStatement (
0 commit comments