Skip to content

Commit 8551146

Browse files
authored
[embind] Update exceptionLast in _emval_throw (#26276)
When `_emval_throw` throws an instance of C++ exception in Emscripten EH mode, it needs to update `exceptionLast` accordingly, as we do in `__cxa_throw` and `__cxa_rethrow` in `src/lib/libexceptions.js`: https://github.com/emscripten-core/emscripten/blob/2d257177915c49c807a3e03d6f404173bb4e5e72/src/lib/libexceptions.js#L100 Here we can't use `storeException` because we are not creating a C++ exception from scratch; we are throwing an already created C++ exception. Fixes #26213.
1 parent 7e9b3ba commit 8551146

File tree

4 files changed

+69
-1
lines changed

4 files changed

+69
-1
lines changed

src/lib/libemval.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,37 @@ ${functionBody}
392392
return delete object[property];
393393
},
394394

395-
_emval_throw__deps: ['$Emval'],
395+
_emval_throw__deps: ['$Emval',
396+
#if !WASM_EXCEPTIONS
397+
'$exceptionLast',
398+
#endif
399+
],
396400
_emval_throw: (object) => {
397401
object = Emval.toValue(object);
402+
#if !WASM_EXCEPTIONS
403+
// If we are throwing Emcripten C++ exception, set exceptionLast, as we do
404+
// in __cxa_throw. When EXCEPTION_STACK_TRACES is set, a C++ exception will
405+
// be an instance of EmscriptenEH, and when EXCEPTION_STACK_TRACES is not
406+
// set, it will be a pointer (number).
407+
//
408+
// This is different from __cxa_throw() in libexception.js because
409+
// __cxa_throw() is called from the user C++ code when the 'throw' keyword
410+
// is used, and the value thrown is a C++ pointer. When
411+
// EXCEPTION_STACK_TRACES is true, we wrap it with CppException. But this
412+
// _emval_throw is called when we throw whatever is contained in 'object',
413+
// which can be anything including a CppException object, or a number, or
414+
// other JS object. So we don't use storeException() wrapper here and we
415+
// throw it as is.
416+
#if EXCEPTION_STACK_TRACES
417+
if (object instanceof CppException) {
418+
exceptionLast = object;
419+
}
420+
#else
421+
if (object === object+0) { // Check if it is a number
422+
exceptionLast = object;
423+
}
424+
#endif
425+
#endif
398426
throw object;
399427
},
400428

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <emscripten/val.h>
2+
#include <iostream>
3+
using namespace emscripten;
4+
5+
int main() {
6+
val error;
7+
try {
8+
throw std::runtime_error("oopsie");
9+
} catch (const std::runtime_error&) {
10+
error = val::take_ownership(
11+
emscripten::internal::_emval_from_current_cxa_exception());
12+
}
13+
try {
14+
error.throw_();
15+
} catch (const std::runtime_error& ex) {
16+
std::clog << "Caught1: " << ex.what() << '\n';
17+
}
18+
19+
try {
20+
throw std::runtime_error("oopsie");
21+
} catch (const std::runtime_error&) {
22+
error = val::take_ownership(
23+
emscripten::internal::_emval_from_current_cxa_exception());
24+
}
25+
try {
26+
throw std::runtime_error("this is bad");
27+
} catch (const std::runtime_error&) {
28+
try {
29+
error.throw_(); // Rethrow oopsie
30+
} catch (const std::runtime_error& ex) {
31+
std::clog << "Caught2: " << ex.what() << '\n';
32+
}
33+
}
34+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Caught1: oopsie
2+
Caught2: oopsie

test/test_core.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7741,6 +7741,10 @@ def test_embind_no_rtti_followed_by_rtti(self):
77417741
def test_embind_wasm_workers(self):
77427742
self.do_run_in_out_file_test('embind/test_embind_wasm_workers.cpp', cflags=['-lembind', '-sWASM_WORKERS'])
77437743

7744+
@with_all_eh_sjlj
7745+
def test_embind_throw_cpp_exception(self):
7746+
self.do_run_in_out_file_test('embind/test_embind_throw_cpp_exception.cpp', cflags=['-lembind', '-std=c++20'])
7747+
77447748
@parameterized({
77457749
'': ('DEFAULT', False),
77467750
'all': ('ALL', False),

0 commit comments

Comments
 (0)