Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ See docs/process.md for more on how version tagging works.

4.0.23 (in development)
-----------------------
- The inconsistency of incrementing / decrementing refcounts between Wasm EH and
Emscripten EH has been fixed. See `test_EXPORT_EXCEPTION_HANDLING_HELPERS` in
`test_core.py` to see the usage. (#25988)


4.0.22 - 12/18/25
-----------------
Expand Down
14 changes: 4 additions & 10 deletions site/source/docs/porting/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,10 @@ leaked.

.. note:: If you catch a Wasm exception and do not rethrow it, you need to free
the storage associated with the exception in JS using
``decrementExceptionRefcount`` method because the exception
catching code in Wasm does not have a chance to free it. But currently due to
an implementation issue that Wasm EH and Emscripten (JS-based) EH, you need
to call incrementExceptionRefcount additionally in case of Emscripten EH. See
https://github.com/emscripten-core/emscripten/issues/17115 for details and a
code example.

.. todo:: Fix the above-mentinoed `inconsistency
<https://github.com/emscripten-core/emscripten/issues/17115>`_ between Wasm
EH and Emscripten EH, on the reference counting.
``decrementExceptionRefcount`` method because the exception catching code in
Wasm does not have a chance to free it. See
``test_EXPORT_EXCEPTION_HANDLING_HELPERS`` in test/test_core.py for an
example usage.


Using Exceptions and setjmp-longjmp Together
Expand Down
4 changes: 1 addition & 3 deletions site/source/docs/tools_reference/settings_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1127,9 +1127,7 @@ the JS library:
Setting this option also adds refcount increasing and decreasing functions
('incrementExceptionRefcount' and 'decrementExceptionRefcount') in the JS
library because if you catch an exception from JS, you may need to manipulate
the refcount manually not to leak memory. What you need to do is different
depending on the kind of EH you use
(https://github.com/emscripten-core/emscripten/issues/17115).
the refcount manually not to leak memory.

See test_EXPORT_EXCEPTION_HANDLING_HELPERS in test/test_core.py for an
example usage.
Expand Down
22 changes: 17 additions & 5 deletions src/lib/libexceptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,21 @@ var LibraryExceptions = {

// Here, we throw an exception after recording a couple of values that we need to remember
// We also remember that it was the last exception thrown as we need to know that later.
__cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount'],
__cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount'
#if !DISABLE_EXCEPTION_CATCHING
, '__cxa_increment_exception_refcount'
#endif
],
__cxa_throw: (ptr, type, destructor) => {
#if EXCEPTION_DEBUG
dbg('__cxa_throw: ' + [ptrToString(ptr), type, ptrToString(destructor)]);
#endif
var info = new ExceptionInfo(ptr);
// Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception.
info.init(type, destructor);
#if !DISABLE_EXCEPTION_CATCHING
___cxa_increment_exception_refcount(ptr);
#endif
{{{ storeException('exceptionLast', 'ptr') }}}
uncaughtExceptionCount++;
{{{ makeThrow('exceptionLast') }}}
Expand All @@ -98,7 +105,11 @@ var LibraryExceptions = {
// This exception will be caught twice, but while begin_catch runs twice,
// we early-exit from end_catch when the exception has been rethrown, so
// pop that here from the caught exceptions.
__cxa_rethrow__deps: ['$exceptionCaught', '$exceptionLast', '$uncaughtExceptionCount'],
__cxa_rethrow__deps: ['$exceptionCaught', '$exceptionLast', '$uncaughtExceptionCount'
#if !DISABLE_EXCEPTION_CATCHING
, '__cxa_increment_exception_refcount'
#endif
],
__cxa_rethrow: () => {
var info = exceptionCaught.pop();
if (!info) {
Expand All @@ -112,6 +123,9 @@ var LibraryExceptions = {
info.set_caught(false);
uncaughtExceptionCount++;
}
#if !DISABLE_EXCEPTION_CATCHING
___cxa_increment_exception_refcount(ptr);
#endif
#if EXCEPTION_DEBUG
dbg('__cxa_rethrow, popped ' +
[ptrToString(ptr), exceptionLast, 'stack', exceptionCaught]);
Expand All @@ -122,8 +136,7 @@ var LibraryExceptions = {

llvm_eh_typeid_for: (type) => type,

__cxa_begin_catch__deps: ['$exceptionCaught', '__cxa_increment_exception_refcount',
'__cxa_get_exception_ptr',
__cxa_begin_catch__deps: ['$exceptionCaught', '__cxa_get_exception_ptr',
'$uncaughtExceptionCount'],
__cxa_begin_catch: (ptr) => {
var info = new ExceptionInfo(ptr);
Expand All @@ -136,7 +149,6 @@ var LibraryExceptions = {
#if EXCEPTION_DEBUG
dbg('__cxa_begin_catch ' + [ptrToString(ptr), 'stack', exceptionCaught]);
#endif
___cxa_increment_exception_refcount(ptr);
return ___cxa_get_exception_ptr(ptr);
},

Expand Down
4 changes: 1 addition & 3 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -761,9 +761,7 @@ var DISABLE_EXCEPTION_THROWING = false;
// Setting this option also adds refcount increasing and decreasing functions
// ('incrementExceptionRefcount' and 'decrementExceptionRefcount') in the JS
// library because if you catch an exception from JS, you may need to manipulate
// the refcount manually not to leak memory. What you need to do is different
// depending on the kind of EH you use
// (https://github.com/emscripten-core/emscripten/issues/17115).
// the refcount manually not to leak memory.
//
// See test_EXPORT_EXCEPTION_HANDLING_HELPERS in test/test_core.py for an
// example usage.
Expand Down
14 changes: 2 additions & 12 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1551,18 +1551,8 @@ class myexception : public exception {
} catch(p) {
// Because we are catching and handling the exception in JS, the normal
// exception catching C++ code doesn't kick in, so we need to make sure we free
// the exception, if necessary. By incrementing and decrementing the refcount
// we trigger the free'ing of the exception if its refcount was zero.
#ifdef __USING_EMSCRIPTEN_EXCEPTION__
// FIXME Currently Wasm EH and Emscripten EH increases
// refcounts in different places. Wasm EH sets the refcount to
// 1 when throwing, and decrease it in __cxa_end_catch.
// Emscripten EH sets the refcount to 0 when throwing, and
// increase it in __cxa_begin_catch, and decrease it in
// __cxa_end_catch. Fix this inconsistency later.
// https://github.com/emscripten-core/emscripten/issues/17115
incrementExceptionRefcount(p);
#endif
// the exception, if necessary. By decrementing the refcount we trigger the
// free'ing of the exception.
out(getExceptionMessage(p).toString());
decrementExceptionRefcount(p);
}
Expand Down
Loading