blob: cfd96b506d7fcc77640f6f4ea17ab15bdb419697 [file]
.. _exceptions:
======================
C++ Exceptions Support
======================
By default, exception catching is disabled in Emscripten. For example, if you
compile the following program:
.. code-block:: cpp
#include <stdio.h>
int main() {
try {
puts("throw...");
throw 1;
puts("(never reached)");
} catch(...) {
puts("catch!");
}
return 0;
}
The first ``throw`` will abort the program and you'll see something like this in
the output:
.. code-block:: text
throw...
Aborted(Assertion failed: Exception thrown, but exception catching is not enabled. Compile with -sNO_DISABLE_EXCEPTION_CATCHING or -sEXCEPTION_CATCHING_ALLOWED=[..] to catch.)
If you want to opt-in, you have two following options.
.. _javascript-based-exception-support:
Emscripten (JavaScript-based) Exception Support
===============================================
First, you can enable exceptions via Emscripten's JavaScript-based support. To
enable it, pass ``-fexceptions`` at both compile time and link time.
When you rebuild the example above with this flag, the output will change to:
.. code-block:: text
throw...
catch!
Note that this option has relatively high overhead, but it will work on all
JavaScript engines with WebAssembly support. You can reduce the overhead by
specifying a list of allowed functions in which exceptions are enabled, see the
``EXCEPTION_CATCHING_ALLOWED`` setting.
.. _webassembly-exception-handling-based-support:
WebAssembly Exception Handling-based Support
============================================
Alternatively, you can opt-in to the `native WebAssembly exception handling
<https://github.com/WebAssembly/exception-handling/blob/master/proposals/exception-handling/Exceptions.md>`_
proposal. To enable it, pass ``-fwasm-exceptions`` at both compile time and link
time.
Rebuilding the example with this flag will result in the same output as with
``-fexceptions`` above:
.. code-block:: text
throw...
catch!
This option leverages a new feature that brings built-in instructions for
throwing and catching exceptions to WebAssembly. As a result, it can reduce code
size and performance overhead compared to the JavaScript-based implementation.
This option is currently supported in several major web browsers, but `may not
be supported in all WebAssembly engines yet
<https://webassembly.org/roadmap/>`_.
Debugging Exceptions
====================
Stack Traces
------------
For :ref:`native Wasm exceptions
<webassembly-exception-handling-based-support>`, when :ref:`ASSERTIONS
<debugging-ASSERTIONS>` is enabled, uncaught exceptions will print stack traces
for debugging. :ref:`ASSERTIONS <debugging-ASSERTIONS>` is enabled by default
in :ref:`-O0 <emcc-O0>` and disabled in optimized builds (:ref:`-O1 <emcc-O1>`
and above). You can enable it by passing ``-sASSERTIONS`` to the ``emcc``
command line in optimized builds as well. To display Wasm function names in
stack traces, you also need :ref:`--profiling-funcs <emcc-profiling-funcs>`
(or :ref:`-g <emcc-g>` or :ref:`-gsource-map<emcc-gsource-map>`).
In JavaScript, you can also examine the stack traces using
`WebAssembly.Exception.prototype.stack
<https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Exception/stack>`_
property. For example:
.. code-block:: javascript
try {
... // some code that calls WebAssembly
} catch (e) {
// Do something with e.stack
console.log(e.stack);
}
Stack traces within Wasm code are not supported in :ref:`JavaScript-based
exceptions <javascript-based-exception-support>`.
.. _handling-c-exceptions-from-javascript:
Handling C++ Exceptions from JavaScript
---------------------------------------
You can also catch and examine the type and the message of C++ exceptions from
JavaScript, in case they inherit from ``std::exception`` and thus have ``what``
method.
``getExceptionMessage`` returns a list of two strings: ``[type, message]``. the
``message`` is the result of calling ``what`` method in case the exception is a
subclass of ``std::exception``. Otherwise it will be just an empty string.
.. code-block:: javascript
var sp = stackSave();
try {
... // some code that calls WebAssembly
} catch (e) {
stackRestore(sp);
console.log(getExceptionMessage(e).toString());
} finally {
...
}
In case the thrown value is an integer 3, this will print ``int,``, because the
message part is empty. If the thrown value is an instance of ``MyException``
that is a subclass of ``std::exception`` and its ``what`` message is ``My
exception thrown``, this code will print ``MyException,My exception thrown``.
``getExceptionMessage`` is available when exceptions are used and either
``-sASSERTIONS`` or ``-sEXCEPTION_STACK_TRACES`` is set, which are by default
true at ``-O0``. At ``-O1`` or above, you can export it separately by
``-sEXPORTED_RUNTIME_METHODS=getExceptionMessage,decrementExceptionRefcount``.
If the stack pointer has been moved due to stack allocations within the Wasm
function before an exception is thrown, you can use ``stackSave()`` and
``stackRestore()`` to restore the stack pointer so that no stack memory is
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. See ``test_getExceptionMessage`` in
``test/test_core.py`` for an example usage.
Using Exceptions and setjmp-longjmp Together
============================================
See :ref:`using-exceptions-and-setjmp-longjmp-together`.
Limitations regarding std::terminate()
======================================
* Currently `std::set_terminate
<https://en.cppreference.com/w/cpp/error/set_terminate>`_ is NOT supported
when a thrown exception does not have a matching handler and unwinds all the
stack up to the topmost caller and crashes the program, i.e., there is no
``catch`` that catches it and the callers are not marked as ``noexcept``.
This applies to both Emscripten-style and WebAssembly exceptions. That
functionality requires `two-phase exception handling
<https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html>`_, which neither
supports. So the following program does NOT print ``my set_terminate``:
.. code-block:: cpp
#include <iostream>
#include <exception>
int main() {
std::set_terminate([] {
std::cerr << "my set_terminate" << std::endl;
std::abort();
});
throw 3;
}
* When the exception handling encounters a termination condition, libc++abi
spec says we call `__cxa_begin_catch()` to mark the exception as handled and
then call `std::terminate()`. But currently Wasm EH does not support calling
`__cxa_begin_catch()`. So the following program prints ``exception_ptr is
null``, where it is supposed to print ``exception_ptr is NOT null``; note
that the use of ``noexcept`` here means that the ``throw 3`` will turn into
a termination condition.
.. code-block:: cpp
#include <iostream>
#include <exception>
int main() noexcept {
std::set_terminate([] {
auto ptr = std::current_exception();
if (ptr)
std::cerr << "exception_ptr is NOT null" << std::endl;
else
std::cerr << "exception_ptr is null" << std::endl;
std::abort();
});
throw 3;
}
This can possibly be supported in the future.