-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[EH] Make getCppExceptionThrownValue return thrown value #17157
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
Conversation
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
|
|
||
| $getExceptionMessage__deps: ['__get_exception_message', 'free', '$getCppExceptionThrownValue'], | ||
| $getExceptionMessage: function(ptr) { | ||
| $getExceptionMessage: function(obj) { |
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.
Drive-by fix: the variable names (obj and ptr) were swapped in the previous code.
| // Relies on the structure layout, where the unwind pointer is right in | ||
| // front of the user's exception object | ||
| static inline __cxa_exception* cxa_exception_from_exception_unwind_exception( | ||
| static inline __cxa_exception* cxa_exception_from_unwind_exception( |
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.
This was named cxa_exception_from_exception_unwind_exception because it was copied from cxa_exception.cpp. I'm not sure why we need to repeat exception, so I shortened the name. The same with the function below.
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.
If this is more correct we should maybe propose this as an upstream change? I do think there is some value in matching these names with the upstream ones... but I don't feel strongly about it.
|
Gentle ping 😀 |
|
|
||
| $getCppExceptionThrownValue__deps: ['$getCppExceptionTag'], | ||
| $getCppExceptionThrownValue__deps: ['$getCppExceptionTag', '__thrown_object_from_unwind_exception'], | ||
| $getCppExceptionThrownValue: function(ex) { |
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.
Should we call this function getCppExceptionThrownObject? Or is thrown value a thing that is different?
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.
Yeah these are confusing. So far in library_exceptions.js, I tried to use 'object' for WebAssembly.Exception object thrown, and 'value' for the user-thrown value. So this function takes an object and returns the user-thrown value. This got more confusing by the param name for this function is ex while other functions' param name is obj, when they denote the same thing.
At the same time libc++abi uses 'object' to mean the user-thrown value, as you can see in the name of thrown_object_from_unwind_exception, which adds to the confusion. It's hard to rename it because that function is copied from cxa_exception.cpp, and changing the function name will make our libc++abi more confusing. I guess we should make these terms clearer.
How about this?
- object: the user-thrown value. So we rename this function to
getCppExceptionThrownObjectas you suggested. - value: let's not use this term at all
- ex/exception:
WebAssembly.Exception. So we rename currentobjparams in other functions, which meantWebAssembly.Exceptions, toex.
If you think this sound fine, I'll make this change as a followup PR.
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.
Maybe "JSObject" or "JSException"?
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.
What is the thing you want to represent with "JSObject" or "JSException"? WebAssembly.Exception or the user-thrown value(or object)? I guess the latter? In that case it's confusing in another way because this is a Wasm exception and that sounds like this is a JS exception thrown from JS, a foreign exception..
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.
WebAssembly.Exception is what I propose to refer to as JS object. The thrown object lives in wasm memory, as does the exception object, right? They are both allocated with malloc.
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.
For example a longer name for this function might be something like getCppThrownObjectFromJSCaughtObject?
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.
Not sure JSCaughtObject is clearer to someone who doesn't know this context. How about just getCppThrownObjectFromWebassemblyException, with comments clarifying what this means? This name is long but I guess at least not misleading..
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.
Sure, sounds reasonable. I think if was a user I would be looking for something do with the thing that I caught.. so I guess JSCaughtObject and WebassemblyException both describe that thing.
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'll explain in the comment that this is WebAssembly.Exception object that Wasm EH JS API provides.
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.
Anyway, will do this in a followup. Thanks for the discussion!
sbc100
left a comment
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.
lgtm % comments
|
Will land this for now, but I'm planning on changing some methods' names in library_exception.js, so please don't consider the current APIs as settled just yet! |
Terms like 'object' and 'value' were used interchangeably and inconsistently, causing confusion. This tries to make it more consistent. - Thrown object: A user-thrown value in C++, which is a C++ pointer. When you do `throw 3;`, libc++abi allocates a storage to to store an exception and store that value '3' within that storage. This is the pointer to that location. This is consistent with the term used by libc++abi; which also calls the pointer 'thrown object'. Emscripten EH throws this directly, so you get this value right away when you catch an exception with JS `catch`. In Wasm EH, which uses libc++abi, this is not the value libc++abi actually throws. See below. - Unwind header: The pointer that actually gets thrown in libc++abi. This pointer preceeds the thrown object: https://github.com/emscripten-core/emscripten/blob/24f765dc3545e89f232c9f8503f6426055271628/system/lib/libcxxabi/src/cxa_exception.cpp#L26-L28 This is an internal detail of libc++abi so we don't want to expose this to users. Emscripten EH doesn't use this concept. - WebAssembly exception: A native Wasm exception, represented by `WebAssembly.Exception` JS object. This is what is actually gets thrown from Wasm `throw` instruction. libunwind receives the unwind header pointer (see above) from libc++abi, and create an `WebAssembly.Exception` object and package the received value with the C++ exception tag, and throws it. So this is what you get when you catch a Wasm exception with JS `catch`. Relevant previous discussion: emscripten-core#17157 (review)
Terms like 'object' and 'value' were used interchangeably and inconsistently, causing confusion. This tries to make it more consistent. - Thrown object: A user-thrown value in C++, which is a C++ pointer. When you do `throw 3;`, libc++abi allocates a storage to to store an exception and store that value '3' within that storage. This is the pointer to that location. This is consistent with the term used by libc++abi; which also calls the pointer 'thrown object'. Emscripten EH throws this directly, so you get this value right away when you catch an exception with JS `catch`. In Wasm EH, which uses libc++abi, this is not the value libc++abi actually throws. See below. - Unwind header: The pointer that actually gets thrown in libc++abi. This pointer preceeds the thrown object: https://github.com/emscripten-core/emscripten/blob/24f765dc3545e89f232c9f8503f6426055271628/system/lib/libcxxabi/src/cxa_exception.cpp#L26-L28 This is an internal detail of libc++abi so we don't want to expose this to users. Emscripten EH doesn't use this concept. - WebAssembly exception: A native Wasm exception, represented by `WebAssembly.Exception` JS object. This is what is actually gets thrown from Wasm `throw` instruction. libunwind receives the unwind header pointer (see above) from libc++abi, and create an `WebAssembly.Exception` object and package the received value with the C++ exception tag, and throws it. So this is what you get when you catch a Wasm exception with JS `catch`. Relevant previous discussion: emscripten-core#17157 (review)
Terms like 'object' and 'value' were used interchangeably and inconsistently, causing confusion. This tries to make it more consistent. - Thrown object: A user-thrown value in C++, which is a C++ pointer. When you do `throw 3;`, libc++abi allocates a storage to store an exception and store that value '3' within that storage. This is the pointer to that location. This is consistent with the term used by libc++abi; which also calls the pointer 'thrown object'. Emscripten EH throws this directly, so you get this value right away when you catch an exception with JS `catch`. In Wasm EH, which uses libc++abi, this is not the value libc++abi actually throws. See below. - Unwind header: The pointer that actually gets thrown in libc++abi. This pointer preceeds the thrown object: https://github.com/emscripten-core/emscripten/blob/24f765dc3545e89f232c9f8503f6426055271628/system/lib/libcxxabi/src/cxa_exception.cpp#L26-L28 This is an internal detail of libc++abi so we don't want to expose this to users. Emscripten EH doesn't use this concept. - WebAssembly exception: A native Wasm exception, represented by `WebAssembly.Exception` JS object. This is what is actually gets thrown from Wasm `throw` instruction. libunwind receives the unwind header pointer (see above) from libc++abi, and create an `WebAssembly.Exception` object and package the received value with the C++ exception tag, and throws it. So this is what you get when you catch a Wasm exception with JS `catch`. Relevant previous discussion: #17157 (review)
After emscripten-core#17064 and emscripten-core#17157 exception message printing is working, but it still requires adding a lot of functions to `EXPORTED_FUNCTIONS` and `DEFAULT_LIBRARY_FUNCS_TO_INLCLUDE`, which is difficult to use and requires users to know unnecessary library internals: https://github.com/emscripten-core/emscripten/blob/8c0fe77d8946a7ec2f73dfa74779ac957dd24530/tests/test_core.py#L1639-L1643 This adds `EXCEPTION_PRINTING_SUPPORT` option, which adds necessary symbols to those settings for easier use. This also exports functions with which you can get the C++ tag and the thrown value from `WebAssembly.Exception` object. Fixes emscripten-core#17114.
After #17064 and #17157 exception message printing is working, but it still requires adding a lot of functions to `EXPORTED_FUNCTIONS` and `DEFAULT_LIBRARY_FUNCS_TO_INLCLUDE`, which is difficult to use and requires users to know unnecessary library internals: https://github.com/emscripten-core/emscripten/blob/8c0fe77d8946a7ec2f73dfa74779ac957dd24530/tests/test_core.py#L1639-L1643 This adds `EXPORT_EXCEPTION_HANDLING_HELPERS` option, which adds necessary symbols to those settings for easier use. This also exports functions with which you can get the C++ tag and the thrown value from `WebAssembly.Exception` object.
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.Exceptionobject. Youcan 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:emscripten/system/lib/libcxxabi/src/cxa_exception.cpp
Line 284 in 1f6b136
The current
getCppExceptionThrownValuein our JS exception libraryreturns the
_Unwind_Exceptionpointer, which is the value actuallythrown 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.Exceptionobject, we still have somediscrepancy 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:
WebAssembly.Exceptionobject, which contains thepointer to the unwind header
After this PR:
WebAssembly.Exceptionobject, which contains theoriginal user-thrown value
Addresses #16033.