diff --git a/doc/extensions.html b/doc/extensions.html
index eb591d1e..a4f20841 100644
--- a/doc/extensions.html
+++ b/doc/extensions.html
@@ -426,9 +426,7 @@ the toolchain used to compile LuaJIT:
on the C stack. The contents of the C++ exception object
pass through unmodified.
Lua errors can be caught on the C++ side with catch(...).
-The corresponding Lua error message can be retrieved from the Lua stack.
-For MSVC for Windows 64 bit this requires compilation of your C++ code
-with /EHa.
+The corresponding Lua error message can be retrieved from the Lua stack.
Throwing Lua errors across C++ frames is safe. C++ destructors
will be called.
diff --git a/src/lj_err.c b/src/lj_err.c
index 9677a1b0..cadc76bd 100644
--- a/src/lj_err.c
+++ b/src/lj_err.c
@@ -209,11 +209,6 @@ static void *err_unwind(lua_State *L, void *stopcf, int errcode)
** from 3rd party docs or must be found by trial-and-error. They really
** don't want you to write your own language-specific exception handler
** or to interact gracefully with MSVC. :-(
-**
-** Apparently MSVC doesn't call C++ destructors for foreign exceptions
-** unless you compile your C++ code with /EHa. Unfortunately this means
-** catch (...) also catches things like access violations. The use of
-** _set_se_translator doesn't really help, because it requires /EHa, too.
*/
#define WIN32_LEAN_AND_MEAN
@@ -270,11 +265,25 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec,
int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ?
LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN;
if ((rec->ExceptionFlags & 6)) { /* EH_UNWINDING|EH_EXIT_UNWIND */
+ if (rec->ExceptionCode == STATUS_LONGJUMP &&
+ rec->ExceptionRecord &&
+ LJ_EXCODE_CHECK(rec->ExceptionRecord->ExceptionCode)) {
+ errcode = LJ_EXCODE_ERRCODE(rec->ExceptionRecord->ExceptionCode);
+ if ((rec->ExceptionFlags & 0x20)) { /* EH_TARGET_UNWIND */
+ /* Unwinding is about to finish; revert the ExceptionCode so that
+ ** RtlRestoreContext does not try to restore from a _JUMP_BUFFER.
+ */
+ rec->ExceptionCode = 0;
+ }
+ }
/* Unwind internal frames. */
err_unwind(L, cf, errcode);
} else {
void *cf2 = err_unwind(L, cf, 0);
if (cf2) { /* We catch it, so start unwinding the upper frames. */
+#if !LJ_TARGET_X86
+ EXCEPTION_RECORD rec2;
+#endif
if (rec->ExceptionCode == LJ_MSVC_EXCODE ||
rec->ExceptionCode == LJ_GCC_EXCODE) {
#if !LJ_TARGET_CYGWIN
@@ -285,8 +294,8 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec,
/* Don't catch access violations etc. */
return 1; /* ExceptionContinueSearch */
}
- UNUSED(ctx);
#if LJ_TARGET_X86
+ UNUSED(ctx);
UNUSED(dispatch);
/* Call all handlers for all lower C frames (including ourselves) again
** with EH_UNWINDING set. Then call the specified function, passing cf
@@ -297,6 +306,20 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec,
(void *)lj_vm_unwind_ff : (void *)lj_vm_unwind_c, errcode);
/* lj_vm_rtlunwind does not return. */
#else
+ if (LJ_EXCODE_CHECK(rec->ExceptionCode)) {
+ /* For unwind purposes, wrap the EXCEPTION_RECORD in something that
+ ** looks like a longjmp, so that MSVC will execute C++ destructors in
+ ** the frames we unwind over. ExceptionInformation[0] should really
+ ** contain a _JUMP_BUFFER*, but hopefully nobody is looking too closely
+ ** at this point.
+ */
+ rec2.ExceptionCode = STATUS_LONGJUMP;
+ rec2.ExceptionRecord = rec;
+ rec2.ExceptionAddress = 0;
+ rec2.NumberParameters = 1;
+ rec2.ExceptionInformation[0] = (ULONG_PTR)ctx;
+ rec = &rec2;
+ }
/* Unwind the stack and call all handlers for all lower C frames
** (including ourselves) again with EH_UNWINDING set. Then set
** stack pointer = f, result = errcode and jump to the specified target.