| .. _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. |