diff --git a/system/lib/libcxxabi/src/cxa_exception.cpp b/system/lib/libcxxabi/src/cxa_exception.cpp index 975c5bbbe9de..b609d44a900b 100644 --- a/system/lib/libcxxabi/src/cxa_exception.cpp +++ b/system/lib/libcxxabi/src/cxa_exception.cpp @@ -287,14 +287,10 @@ __cxa_throw(void *thrown_object, std::type_info *tinfo, void (_LIBCXXABI_DTOR_FU #ifdef __USING_SJLJ_EXCEPTIONS__ _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); -#elif __USING_WASM_EXCEPTIONS__ -#ifdef NDEBUG - _Unwind_RaiseException(&exception_header->unwindHeader); -#else +#elif defined(__USING_WASM_EXCEPTIONS__) && !defined(NDEBUG) // In debug mode, call a JS library function to use WebAssembly.Exception JS // API, which enables us to include stack traces __throw_exception_with_stack_trace(&exception_header->unwindHeader); -#endif #else _Unwind_RaiseException(&exception_header->unwindHeader); #endif @@ -644,6 +640,10 @@ void __cxa_rethrow() { } #ifdef __USING_SJLJ_EXCEPTIONS__ _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); +#elif defined(__USING_WASM_EXCEPTIONS__) && !defined(NDEBUG) + // In debug mode, call a JS library function to use WebAssembly.Exception JS + // API, which enables us to include stack traces + __throw_exception_with_stack_trace(&exception_header->unwindHeader); #else _Unwind_RaiseException(&exception_header->unwindHeader); #endif @@ -769,8 +769,13 @@ __cxa_rethrow_primary_exception(void* thrown_object) dep_exception_header->unwindHeader.exception_cleanup = dependent_exception_cleanup; #ifdef __USING_SJLJ_EXCEPTIONS__ _Unwind_SjLj_RaiseException(&dep_exception_header->unwindHeader); +#elif defined(__USING_WASM_EXCEPTIONS__) && !defined(NDEBUG) + // In debug mode, call a JS library function to use + // WebAssembly.Exception JS API, which enables us to include stack + // traces + __throw_exception_with_stack_trace(&exception_header->unwindHeader); #else - _Unwind_RaiseException(&dep_exception_header->unwindHeader); + _Unwind_RaiseException(&exception_header->unwindHeader); #endif // Some sort of unwinding error. Note that terminate is a handler. __cxa_begin_catch(&dep_exception_header->unwindHeader); diff --git a/test/test_other.py b/test/test_other.py index ef6abe3c1e12..2213231cc99b 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -8529,7 +8529,7 @@ def test_exceptions_stack_trace_and_message(self, wasm_eh): return 0; } ''' - emcc_args = ['-g'] + self.emcc_args = ['-g'] # Stack trace and message example for this example code: # exiting due to exception: [object WebAssembly.Exception],Error: std::runtime_error,my message @@ -8557,9 +8557,9 @@ def test_exceptions_stack_trace_and_message(self, wasm_eh): # self.require_wasm_eh() if this issue is fixed later. self.require_v8() - emcc_args += ['-fwasm-exceptions'] + self.emcc_args += ['-fwasm-exceptions'] else: - emcc_args += ['-fexceptions'] + self.emcc_args += ['-fexceptions'] # Stack traces are enabled when either of ASSERTIONS or # EXCEPTION_STACK_TRACES is enabled. You can't disable @@ -8568,16 +8568,14 @@ def test_exceptions_stack_trace_and_message(self, wasm_eh): # Prints stack traces self.set_setting('ASSERTIONS', 1) self.set_setting('EXCEPTION_STACK_TRACES', 1) - self.do_run(src, emcc_args=emcc_args, assert_all=True, - assert_returncode=NON_ZERO, expected_output=stack_trace_checks, - regex=True) + self.do_run(src, assert_all=True, assert_returncode=NON_ZERO, + expected_output=stack_trace_checks, regex=True) # Prints stack traces self.set_setting('ASSERTIONS', 0) self.set_setting('EXCEPTION_STACK_TRACES', 1) - self.do_run(src, emcc_args=emcc_args, assert_all=True, - assert_returncode=NON_ZERO, expected_output=stack_trace_checks, - regex=True) + self.do_run(src, assert_all=True, assert_returncode=NON_ZERO, + expected_output=stack_trace_checks, regex=True) # Not allowed self.set_setting('ASSERTIONS', 1) @@ -8589,10 +8587,82 @@ def test_exceptions_stack_trace_and_message(self, wasm_eh): # Doesn't print stack traces self.set_setting('ASSERTIONS', 0) self.set_setting('EXCEPTION_STACK_TRACES', 0) - err = self.do_run(src, emcc_args=emcc_args, assert_returncode=NON_ZERO) + err = self.do_run(src, assert_returncode=NON_ZERO) for check in stack_trace_checks: self.assertFalse(re.search(check, err), 'Expected regex "%s" to not match on:\n%s' % (check, err)) + @parameterized({ + '': (False,), + 'wasm': (True,), + }) + def test_exceptions_rethrow_stack_trace_and_message(self, wasm_eh): + self.emcc_args = ['-g'] + if wasm_eh: + # FIXME Node v18.13 (LTS as of Jan 2023) has not yet implemented the new + # optional 'traceStack' option in WebAssembly.Exception constructor + # (https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Exception/Exception) + # and embeds stack traces unconditionally. Change this back to + # self.require_wasm_eh() if this issue is fixed later. + self.require_v8() + self.emcc_args += ['-fwasm-exceptions'] + else: + self.emcc_args += ['-fexceptions'] + + # Rethrowing exception currently loses the stack trace before the rethrowing + # due to how rethrowing is implemented. So in the examples below we don't + # print 'bar' at the moment. + # TODO Make rethrow preserve stack traces before rethrowing? + rethrow_src1 = r''' + #include + + void bar() { + throw std::runtime_error("my message"); + } + void foo() { + try { + bar(); + } catch (...) { + throw; // rethrowing by throw; + } + } + int main() { + foo(); + return 0; + } + ''' + rethrow_src2 = r''' + #include + + void bar() { + throw std::runtime_error("my message"); + } + void foo() { + try { + bar(); + } catch (...) { + auto e = std::current_exception(); + std::rethrow_exception(e); // rethrowing by std::rethrow_exception + } + } + int main() { + foo(); + return 0; + } + ''' + rethrow_stack_trace_checks = [ + 'std::runtime_error[:,][ ]?my message', # 'std::runtime_error: my message' for Emscripten EH + 'at ((src.wasm.)?_?__cxa_rethrow|___resumeException)', # '___resumeException' (JS symbol) for Emscripten EH + 'at (src.wasm.)?foo', + 'at (src.wasm.)?main'] + + self.set_setting('ASSERTIONS', 1) + err = self.do_run(rethrow_src1, assert_all=True, assert_returncode=NON_ZERO, + expected_output=rethrow_stack_trace_checks, regex=True) + self.assertNotContained('bar', err) + err = self.do_run(rethrow_src2, assert_all=True, assert_returncode=NON_ZERO, + expected_output=rethrow_stack_trace_checks, regex=True) + self.assertNotContained('bar', err) + @requires_node def test_jsrun(self): print(config.NODE_JS)