diff --git a/src/posix/cocoa/i_system.mm b/src/posix/cocoa/i_system.mm index 1b1d79dbb..8ceaaf359 100644 --- a/src/posix/cocoa/i_system.mm +++ b/src/posix/cocoa/i_system.mm @@ -130,7 +130,7 @@ void I_Quit() extern FILE* Logfile; bool gameisdead; -void I_FatalError(const char* const error, ...) +static void I_FatalError(const char* const error, va_list ap) { static bool alreadyThrown = false; gameisdead = true; @@ -143,7 +143,7 @@ void I_FatalError(const char* const error, ...) int index; va_list argptr; va_start(argptr, error); - index = vsnprintf(errortext, MAX_ERRORTEXT, error, argptr); + index = vsnprintf(errortext, MAX_ERRORTEXT, error, ap); va_end(argptr); extern void Mac_I_FatalError(const char*); @@ -167,16 +167,34 @@ void I_FatalError(const char* const error, ...) } } -void I_Error(const char* const error, ...) +void I_FatalError(const char* const error, ...) +{ + va_list argptr; + va_start(argptr, error); + I_FatalError(error, argptr); + va_end(argptr); + +} + +extern thread_local int jit_frames; +void I_Error (const char *error, ...) { va_list argptr; char errortext[MAX_ERRORTEXT]; va_start(argptr, error); - vsnprintf(errortext, MAX_ERRORTEXT, error, argptr); - va_end(argptr); - throw CRecoverableError(errortext); + if (jit_frames == 0) + { + vsprintf (errortext, error, argptr); + va_end (argptr); + throw CRecoverableError(errortext); + } + else + { + I_FatalError(error, argptr); + va_end(argptr); + } } diff --git a/src/posix/sdl/i_system.cpp b/src/posix/sdl/i_system.cpp index 173f8a12f..2a9c7ffc0 100644 --- a/src/posix/sdl/i_system.cpp +++ b/src/posix/sdl/i_system.cpp @@ -173,7 +173,7 @@ void Linux_I_FatalError(const char* errortext) } #endif -void I_FatalError (const char *error, ...) +void I_FatalError (const char *error, va_list ap) { static bool alreadyThrown = false; gameisdead = true; @@ -183,10 +183,7 @@ void I_FatalError (const char *error, ...) alreadyThrown = true; char errortext[MAX_ERRORTEXT]; int index; - va_list argptr; - va_start (argptr, error); - index = vsnprintf (errortext, MAX_ERRORTEXT, error, argptr); - va_end (argptr); + index = vsnprintf (errortext, MAX_ERRORTEXT, error, ap); #ifdef __APPLE__ Mac_I_FatalError(errortext); @@ -214,16 +211,34 @@ void I_FatalError (const char *error, ...) } } +void I_FatalError(const char* const error, ...) +{ + va_list argptr; + va_start(argptr, error); + I_FatalError(error, argptr); + va_end(argptr); + +} + +extern thread_local int jit_frames; void I_Error (const char *error, ...) { va_list argptr; char errortext[MAX_ERRORTEXT]; - va_start (argptr, error); - vsprintf (errortext, error, argptr); - va_end (argptr); + va_start(argptr, error); - throw CRecoverableError (errortext); + if (jit_frames == 0) + { + vsprintf (errortext, error, argptr); + va_end (argptr); + throw CRecoverableError(errortext); + } + else + { + I_FatalError(error, argptr); + va_end(argptr); + } } void I_SetIWADInfo () diff --git a/src/scripting/vm/jit.cpp b/src/scripting/vm/jit.cpp index e794077b4..5da62ab5f 100644 --- a/src/scripting/vm/jit.cpp +++ b/src/scripting/vm/jit.cpp @@ -316,19 +316,10 @@ void JitCompiler::SetupSimpleFrame() static VMFrameStack *CreateFullVMFrame(VMScriptFunction *func, VMValue *args, int numargs) { - try - { - VMFrameStack *stack = &GlobalVMStack; - VMFrame *newf = stack->AllocFrame(func); - CurrentJitExceptInfo->vmframes++; - VMFillParams(args, newf, numargs); - return stack; - } - catch (...) - { - VMThrowException(std::current_exception()); - return nullptr; - } + VMFrameStack *stack = &GlobalVMStack; + VMFrame *newf = stack->AllocFrame(func); + VMFillParams(args, newf, numargs); + return stack; } void JitCompiler::SetupFullVMFrame() @@ -362,15 +353,7 @@ void JitCompiler::SetupFullVMFrame() static void PopFullVMFrame(VMFrameStack *stack) { - try - { - stack->PopFrame(); - CurrentJitExceptInfo->vmframes--; - } - catch (...) - { - VMThrowException(std::current_exception()); - } + stack->PopFrame(); } void JitCompiler::EmitPopFrame() @@ -434,14 +417,7 @@ void JitCompiler::EmitNullPointerThrow(int index, EVMAbortException reason) void JitCompiler::ThrowException(VMScriptFunction *func, VMOP *line, int reason) { - try - { - ThrowAbortException(func, line, (EVMAbortException)reason, nullptr); - } - catch (...) - { - VMThrowException(std::current_exception()); - } + ThrowAbortException(func, line, (EVMAbortException)reason, nullptr); } void JitCompiler::EmitThrowException(EVMAbortException reason) diff --git a/src/scripting/vm/jit_flow.cpp b/src/scripting/vm/jit_flow.cpp index b0b66ce2a..08e7b0660 100644 --- a/src/scripting/vm/jit_flow.cpp +++ b/src/scripting/vm/jit_flow.cpp @@ -47,14 +47,7 @@ void JitCompiler::EmitIJMP() static void ValidateCall(DObject *o, VMFunction *f, int b) { - try - { - FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1); - } - catch (...) - { - VMThrowException(std::current_exception()); - } + FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1); } void JitCompiler::EmitSCOPE() @@ -241,30 +234,22 @@ void JitCompiler::EmitRETI() static DObject* CreateNew(PClass *cls, int c) { - try + if (!cls->ConstructNative) { - if (!cls->ConstructNative) - { - ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars()); - } - else if (cls->bAbstract) - { - ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); - } - else if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited - { - ThrowAbortException(X_OTHER, "Cannot create actors with 'new'"); - } + ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars()); + } + else if (cls->bAbstract) + { + ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); + } + else if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited + { + ThrowAbortException(X_OTHER, "Cannot create actors with 'new'"); + } - // [ZZ] validate readonly and between scope construction - if (c) FScopeBarrier::ValidateNew(cls, c - 1); - return cls->CreateNew(); - } - catch (...) - { - VMThrowException(std::current_exception()); - return nullptr; - } + // [ZZ] validate readonly and between scope construction + if (c) FScopeBarrier::ValidateNew(cls, c - 1); + return cls->CreateNew(); } void JitCompiler::EmitNEW() @@ -280,39 +265,24 @@ void JitCompiler::EmitNEW() static void ThrowNewK(PClass *cls, int c) { - try + if (!cls->ConstructNative) { - if (!cls->ConstructNative) - { - ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars()); - } - else if (cls->bAbstract) - { - ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); - } - else // if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited - { - ThrowAbortException(X_OTHER, "Cannot create actors with 'new'"); - } + ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars()); } - catch (...) + else if (cls->bAbstract) { - VMThrowException(std::current_exception()); + ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); + } + else // if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited + { + ThrowAbortException(X_OTHER, "Cannot create actors with 'new'"); } } static DObject *CreateNewK(PClass *cls, int c) { - try - { - if (c) FScopeBarrier::ValidateNew(cls, c - 1); - return cls->CreateNew(); - } - catch (...) - { - VMThrowException(std::current_exception()); - return nullptr; - } + if (c) FScopeBarrier::ValidateNew(cls, c - 1); + return cls->CreateNew(); } void JitCompiler::EmitNEW_K() @@ -393,19 +363,12 @@ void JitCompiler::EmitBOUND_R() void JitCompiler::ThrowArrayOutOfBounds(VMScriptFunction *func, VMOP *line, int index, int size) { - try + if (index >= size) { - if (index >= size) - { - ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", size, index); - } - else - { - ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Negative current index = %i\n", index); - } + ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", size, index); } - catch (...) + else { - VMThrowException(std::current_exception()); + ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Negative current index = %i\n", index); } } diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index b2820f409..f5ccc25b2 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -58,6 +58,17 @@ CUSTOM_CVAR(Bool, vm_jit, true, CVAR_NOINITCALL) CVAR(Bool, vm_jit, false, CVAR_NOINITCALL|CVAR_NOSET) #endif +// On Windows we can rely on the system's unwinder to deal with JITed code. +// On Linux we can not and need to be able to tell the error management that we have some JITed code in the call chain. +#ifdef _WIN32 +inline void beginVM() { } +inline void endVM() { } +#else +thread_local int jit_frames; +inline void beginVM() { if (vm_jit) jit_frames++; } +inline void endVM() { if (vm_jit) jit_frames--; } +#endif + cycle_t VMCycles[10]; int VMCalls[10]; @@ -314,13 +325,7 @@ int VMNativeFunction::NativeScriptCall(VMFunction *func, VMValue *params, int nu { err.MaybePrintMessage(); err.stacktrace.AppendFormat("Called from %s\n", func->PrintableName.GetChars()); - VMThrowException(std::current_exception()); - return 0; - } - catch (...) - { - VMThrowException(std::current_exception()); - return 0; + throw; } } @@ -534,32 +539,6 @@ VMFrame *VMFrameStack::PopFrame() return parent; } -//=========================================================================== -// -// The jitted code does not implement C++ exception handling. -// Catch them, longjmp out of the jitted functions, perform vmframe cleanup -// then rethrow the C++ exception -// -//=========================================================================== - -thread_local JitExceptionInfo *CurrentJitExceptInfo; - -void VMThrowException(std::exception_ptr cppException) -{ - CurrentJitExceptInfo->cppException = cppException; - longjmp(CurrentJitExceptInfo->sjljbuf, 1); -} - -static void VMRethrowException(JitExceptionInfo *exceptInfo) -{ - int c = exceptInfo->vmframes; - VMFrameStack *stack = &GlobalVMStack; - for (int i = 0; i < c; i++) - stack->PopFrame(); - - std::rethrow_exception(exceptInfo->cppException); -} - //=========================================================================== // // VMFrameStack :: Call @@ -601,25 +580,12 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, { VMCycles[0].Clock(); - JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo; - JitExceptionInfo newExceptInfo; - CurrentJitExceptInfo = &newExceptInfo; - if (setjmp(CurrentJitExceptInfo->sjljbuf) == 0) - { - auto sfunc = static_cast(func); - int numret = sfunc->ScriptCall(sfunc, params, numparams, results, numresults); - CurrentJitExceptInfo = prevExceptInfo; - VMCycles[0].Unclock(); - return numret; - } - else - { - VMCycles[0].Unclock(); - auto exceptInfo = CurrentJitExceptInfo; - CurrentJitExceptInfo = prevExceptInfo; - VMRethrowException(exceptInfo); - return 0; - } + auto sfunc = static_cast(func); + beginVM(); + int numret = sfunc->ScriptCall(sfunc, params, numparams, results, numresults); + endVM(); + VMCycles[0].Unclock(); + return numret; } } } @@ -733,7 +699,18 @@ void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException { va_list ap; va_start(ap, moreinfo); + CVMAbortException err(reason, moreinfo, ap); + +#ifndef _WIN32) + + // Without a usable unwinder, aborting is the only real option here. :( + if (jit_frames) + { + I_FatalError("VM error: %s", err.what()); + } +#endif + err.stacktrace.AppendFormat("Called from %s at %s, line %d\n", sfunc->PrintableName.GetChars(), sfunc->SourceFileName.GetChars(), sfunc->PCToLine(line)); throw err; va_end(ap); diff --git a/src/scripting/vm/vmintern.h b/src/scripting/vm/vmintern.h index 5d4ace94c..feb422528 100644 --- a/src/scripting/vm/vmintern.h +++ b/src/scripting/vm/vmintern.h @@ -439,17 +439,6 @@ extern thread_local VMFrameStack GlobalVMStack; typedef std::pair FTypeAndOffset; -struct JitExceptionInfo -{ - std::exception_ptr cppException; - std::jmp_buf sjljbuf; - int vmframes = 0; -}; - -extern thread_local JitExceptionInfo *CurrentJitExceptInfo; - -void VMThrowException(std::exception_ptr cppException); - typedef int(*JitFuncPtr)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); class VMScriptFunction : public VMFunction