-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Debugging: Add a description of how to handle exceptions. #11073
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,6 +137,59 @@ Debug printouts can even execute arbitrary JavaScript. For example:: | |
| } | ||
|
|
||
|
|
||
| Handling C++ exceptions from javascript | ||
| ======================================= | ||
|
|
||
| C++ exceptions are thrown from WebAssembly using exception pointers, which means | ||
| that try/catch/finally blocks in JavaScript will only receive a number, which | ||
| represents a pointer into linear memory. In order to get the exception message, | ||
| the user will need to create some WASM code which will extract the meaning from | ||
| the exception. In the example code below we created a function that receives the | ||
| address of a ``std::exception``, and by casting the pointer | ||
| returns the ``what`` function call result. | ||
|
|
||
| .. code-block:: cpp | ||
|
|
||
| #include <bind.h> | ||
|
|
||
| std::string getExceptionMessage(intptr_t exceptionPtr) { | ||
| return std::string(reinterpret_cast<std::exception *>(exceptionPtr)->what()); | ||
| } | ||
|
|
||
| EMSCRIPTEN_BINDINGS(Bindings) { | ||
| emscripten::function("getExceptionMessage", &getExceptionMessage); | ||
sbc100 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| Once such a function has been created, exception handling code in javascript | ||
| can call it when receiving an exception from WASM. Here the function is used | ||
| in order to log the thrown exception. | ||
|
|
||
| .. code-block:: javascript | ||
|
|
||
| try { | ||
| ... // some code that calls WebAssembly | ||
| } catch (exception) { | ||
| console.error(Module.getExceptionMessage(exception)); | ||
| } finally { | ||
| ... | ||
| } | ||
|
|
||
| It's important to notice that this example code will work only for thrown | ||
| statically allocated exceptions. If your code throws other objects, such as | ||
| strings or dynamically allocated exceptions, the handling code will need to | ||
| take that into account. For example, if your code needs to handle both native | ||
| C++ exceptions and JavaScript exceptions you could use the following code to | ||
| distinguish between them: | ||
|
|
||
| .. code-block:: javascript | ||
|
|
||
| function getExceptionMessage(exception) { | ||
| return typeof exception === 'number' | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But since the exception is a pointer won't it always appear here as a number (i32) , regardless of what one throws? What other type could this be here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That depends on the code you wrap. I think that if you use embind you might also receive JS exceptions, and also - if your try clause wraps non-WASM code, you'll need some way to differentiate the errors. AFAIK, actual exceptions thrown from WASM will always be number.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is assuming that no JS code throws numbers as exceptions I guess? The implicit meaning of this function I guess is |
||
| ? Module.getExceptionMessage(exception) | ||
| : exception; | ||
| } | ||
|
|
||
|
|
||
| Disabling optimizations | ||
| ======================= | ||
|
|
||
|
|
@@ -285,4 +338,3 @@ Need help? | |
| The :ref:`Emscripten Test Suite <emscripten-test-suite>` contains good examples of almost all functionality offered by Emscripten. If you have a problem, it is a good idea to search the suite to determine whether test code with similar behavior is able to run. | ||
|
|
||
| If you've tried the ideas here and you need more help, please :ref:`contact`. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you do this without embind (which is fairly heavyweight construct).
How about:
Then have JS call
UTF8ToString(getExceptionMessage(exception))?Then you don't need EMSCRIPTEN_BINDINGS either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what sense is embind heavyweight? The doc seems to hint that it's reasonable, performance-wise.
This question isn't relevant to the PR - I'd just like to know, since I use embind throughout my project.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to me to demonstrate one feature in isolation, when possible. In order to understand this code one has to also know about how embind work. It might lead the reader to think that embind is somehow essential to what is going on here.
IMHO, just returning a
const char *would make the code here simpler, with less boilderplate. And there is no need to for the extra--bindlink flag.I think the reason I say heavyweight is because embind a whole bunch of C++ template magic that few people (myself included) have a good understanding of. I don't know how costly it is a runtime (maybe its not), but it is pretty complex at compile time. I'm not saying its bad or shouldn't be used, I'd just rather not use it when showing how other/simpler things work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel super strongly about not using embind here BTW. This is just my opinion. If you feel strongly that it helps here I'm not going block this PR. I do appreciate contributions to the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried recreating the code in your way, but couldn't get it to run.
I'm sorry - I assume I'm doing something wrong, but I can't really dig into it right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I think embind here is ok, if it makes things easier.
I can't seem to get this to work with embind, though. A full testcase might be helpful.