Skip to content

Commit af7f969

Browse files
authored
[EH] Make getCppExceptionThrownValue return thrown value (#17157)
In Emscripten EH, the thrown value by the JS library is the original user-thrown value itself. But in Wasm EH, what we throw is an `WebAssembly.Exception` object. You can use getArg method to get the value thrown. But even this value is not the actual user-thrown value; in libc++abi, which Wasm EH uses, what we actually throw is a pointer to something called unwind header, represented by `_Unwind_Exception`: https://github.com/emscripten-core/emscripten/blob/1f6b136d75c9a6a89d07ed069b9cede80b39cebd/system/lib/libcxxabi/src/cxa_exception.cpp#L284 The current `getCppExceptionThrownValue` in our JS exception library returns the `_Unwind_Exception` pointer, which is the value actually thrown by libc++abi, and not the original user-thrown value. This is confusing in the first place, and I got requests from users who want to access the original user-thrown value. Because of `WebAssembly.Exception` object, we still have some discrepancy between Emscripten EH and Wasm EH, but I think this is more intuitive, and it actually simplifies libc++abi code as well. So the current state: - Emscripten EH: Throws original user-thrown value - Wasm EH: Throws `WebAssembly.Exception` object, which contains the pointer to the unwind header After this PR: - Emscripten EH: Throws original user-thrown value - Wasm EH: Throws `WebAssembly.Exception` object, which contains the original user-thrown value
1 parent 99f722a commit af7f969

File tree

3 files changed

+21
-47
lines changed

3 files changed

+21
-47
lines changed

src/library_exceptions.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -398,29 +398,32 @@ var LibraryExceptions = {
398398
return Module['asm']['__cpp_exception'];
399399
},
400400

401-
$getCppExceptionThrownValue__deps: ['$getCppExceptionTag'],
401+
$getCppExceptionThrownValue__deps: ['$getCppExceptionTag', '__thrown_object_from_unwind_exception'],
402402
$getCppExceptionThrownValue: function(ex) {
403-
return ex.getArg(getCppExceptionTag(), 0);
403+
// In Wasm EH, the value extracted from WebAssembly.Exception is a pointer
404+
// to the unwind header. Convert it to the actual thrown value.
405+
var unwind_ex = ex.getArg(getCppExceptionTag(), 0);
406+
return ___thrown_object_from_unwind_exception(unwind_ex);
404407
},
405408

406-
$incrementExceptionRefcount__deps: ['__increment_wasm_exception_refcount', '$getCppExceptionThrownValue'],
409+
$incrementExceptionRefcount__deps: ['__cxa_increment_exception_refcount', '$getCppExceptionThrownValue'],
407410
$incrementExceptionRefcount: function(obj) {
408411
var ptr = getCppExceptionThrownValue(obj);
409-
___increment_wasm_exception_refcount(ptr);
412+
___cxa_increment_exception_refcount(ptr);
410413
},
411414

412-
$decrementExceptionRefcount__deps: ['__decrement_wasm_exception_refcount', '$getCppExceptionThrownValue'],
415+
$decrementExceptionRefcount__deps: ['__cxa_decrement_exception_refcount', '$getCppExceptionThrownValue'],
413416
$decrementExceptionRefcount: function(obj) {
414417
var ptr = getCppExceptionThrownValue(obj);
415-
___decrement_wasm_exception_refcount(ptr);
418+
___cxa_decrement_exception_refcount(ptr);
416419
},
417420

418421
$getExceptionMessage__deps: ['__get_exception_message', 'free', '$getCppExceptionThrownValue'],
419-
$getExceptionMessage: function(ptr) {
422+
$getExceptionMessage: function(obj) {
420423
// In Wasm EH, the thrown object is a WebAssembly.Exception. Extract the
421424
// thrown value from it.
422-
var obj = getCppExceptionThrownValue(ptr);
423-
var utf8_addr = ___get_exception_message(obj);
425+
var ptr = getCppExceptionThrownValue(obj);
426+
var utf8_addr = ___get_exception_message(ptr);
424427
var result = UTF8ToString(utf8_addr);
425428
_free(utf8_addr);
426429
return result;

system/lib/libcxxabi/src/cxa_exception_emscripten.cpp

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,26 @@ thrown_object_from_cxa_exception(__cxa_exception* exception_header) {
3333
// Get the exception object from the unwind pointer.
3434
// Relies on the structure layout, where the unwind pointer is right in
3535
// front of the user's exception object
36-
static inline __cxa_exception* cxa_exception_from_exception_unwind_exception(
36+
static inline __cxa_exception* cxa_exception_from_unwind_exception(
3737
_Unwind_Exception* unwind_exception) {
3838
return cxa_exception_from_thrown_object(unwind_exception + 1);
3939
}
4040

41-
static inline void* thrown_object_from_exception_unwind_exception(
41+
static inline void* thrown_object_from_unwind_exception(
4242
_Unwind_Exception* unwind_exception) {
4343
__cxa_exception* exception_header =
44-
cxa_exception_from_exception_unwind_exception(unwind_exception);
44+
cxa_exception_from_unwind_exception(unwind_exception);
4545
return thrown_object_from_cxa_exception(exception_header);
4646
}
4747

4848
extern "C" {
4949

50+
void* __thrown_object_from_unwind_exception(
51+
_Unwind_Exception* unwind_exception) {
52+
return thrown_object_from_unwind_exception(unwind_exception);
53+
}
54+
5055
char* __get_exception_message(void* thrown_object, bool terminate=false) {
51-
#if __USING_WASM_EXCEPTIONS__
52-
// In Wasm EH, the thrown value is 'unwindHeader' field of __cxa_exception. So
53-
// we need to get the real pointer thrown by user.
54-
thrown_object = thrown_object_from_exception_unwind_exception(
55-
static_cast<_Unwind_Exception*>(thrown_object));
56-
#endif
5756
__cxa_exception* exception_header =
5857
cxa_exception_from_thrown_object(thrown_object);
5958
const __shim_type_info* thrown_type =
@@ -95,34 +94,6 @@ char* __get_exception_terminate_message(void *thrown_object) {
9594
return __get_exception_message(thrown_object, true);
9695
}
9796

98-
#if __USING_WASM_EXCEPTIONS__
99-
void __cxa_increment_exception_refcount(void* thrown_object) _NOEXCEPT;
100-
void __cxa_decrement_exception_refcount(void* thrown_object) _NOEXCEPT;
101-
102-
// These are wrappers for __cxa_increment_exception_refcount and
103-
// __cxa_decrement_exception_refcount so that these can be called from JS. When
104-
// you catch a Wasm exception from JS and do not rethrow it, its refcount is
105-
// still greater than 0 so memory is leaked; users call JS library functions
106-
// that call these to fix it.
107-
108-
void __increment_wasm_exception_refcount(
109-
void* unwind_exception) _NOEXCEPT {
110-
// In Wasm EH, the thrown value is 'unwindHeader' field of __cxa_exception. So
111-
// we need to get the real pointer thrown by user.
112-
void* thrown_object = thrown_object_from_exception_unwind_exception(
113-
static_cast<_Unwind_Exception*>(unwind_exception));
114-
__cxa_increment_exception_refcount(thrown_object);
115-
}
116-
117-
void __decrement_wasm_exception_refcount(
118-
void* unwind_exception) _NOEXCEPT {
119-
// In Wasm EH, the thrown value is 'unwindHeader' field of __cxa_exception. So
120-
// we need to get the real pointer thrown by user.
121-
void* thrown_object = thrown_object_from_exception_unwind_exception(
122-
static_cast<_Unwind_Exception*>(unwind_exception));
123-
__cxa_decrement_exception_refcount(thrown_object);
124-
}
125-
#endif // __USING_WASM_EXCEPTIONS__
12697
}
12798

12899
#endif // __USING_EMSCRIPTEN_EXCEPTIONS__ || __USING_WASM_EXCEPTIONS__

tests/test_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1640,7 +1640,7 @@ def test_exception_message(self):
16401640
self.set_setting('EXPORTED_FUNCTIONS', ['_main', 'getExceptionMessage', '___get_exception_message'])
16411641
if '-fwasm-exceptions' in self.emcc_args:
16421642
exports = self.get_setting('EXPORTED_FUNCTIONS')
1643-
self.set_setting('EXPORTED_FUNCTIONS', exports + ['___cpp_exception', '___increment_wasm_exception_refcount', '___decrement_wasm_exception_refcount'])
1643+
self.set_setting('EXPORTED_FUNCTIONS', exports + ['___cpp_exception', '___cxa_increment_exception_refcount', '___cxa_decrement_exception_refcount', '___thrown_object_from_unwind_exception'])
16441644

16451645
# FIXME Temporary workaround. See 'FIXME' in the test source code below for
16461646
# details.

0 commit comments

Comments
 (0)