Identity of exception objects across frames
Should exceptions remain strictly equal across call frames?
let wasm_module = /* instantiate a module with appropriate functions */
var saved_exception = undefined
function called_by_wasm() {
try {
wasm_module.exports.function_that_throws_exception();
} catch(e) {
saved_exception = e
throw e
}
}
function call_wasm() {
try {
wasm_module.exports.function_that_calls_called_by_wasm();
} catch (e) {
console.info(e === saved_exception)
}
}
Let's assume that wasm_module.exports.function_that_calls_called_by_wasm either does not contain a catch block or catches and rethrows the exception.
Should the console.info line be guaranteed to print true?
One could imagine an implementation strategy for rethrow that reconstructs an equivalent exception rather than throwing the exact same one. Do we want to require rethrow to reuse the same exception object?
I think this constraint would only apply to the JavaScript embedding. I imagine there exist platforms where "reuse the same exception object" doesn't mean much.
Note that exceptions are opague. Hence, you can't save it in a local variable. However, since (Wasm origin) exceptions are a tuple of values that are pushed back on the stack when caught, one could easily rebuild the exception and throw it.
On the other hand, the exception could be coming from some other source (such as JavaScript) and you catch it with a "catch_all" clause. In this context, the exception is completely opague, and can't be represented in Wasm. As such, this latter case is the main reason for considering a "rethrow" instruciton.
Oh sorry, the code snippets I included in my post were in JavaScript, and I left the Wasm code to the reader's imagination.
Here's a less imaginary version of the Wasm module:
(module
(type $func_v_v)
(import "env" "called_by_wasm" (func $called_by_wasm (type $func_v_v)))
(exception $e (type $func_v_v))
(export "function_that_throws_exception" (func $function_that_throws_exception))
(func $function_that_throws_exception (type $func_v_v)
(throw $e))
(export "function_that_calls_called_by_wasm" (func $function_that_calls_called_by_wasm))
(func $function_that_calls_called_by_wasm (type $func_v_v)
(call $called_by_wasm)))
Alternatively, the last function could be instead:
(export "function_that_calls_called_by_wasm" (func $function_that_calls_called_by_wasm))
(func $function_that_calls_called_by_wasm (type $func_v_v)
(try
(call $called_by_wasm)
(catch_all
(rethrow))))
As such, this latter case is the main reason for considering a "rethrow" instruction.
Ah, thanks. I was wondering if rethrow was specifically needed. This is a strong motivator.
Yes, I think the assertion in the OP has to hold. This isn't even Wasm-specific, but a consequence of the JS semantics for exceptions, which are arbitrary values, so must not magically change their identity. This should also extend to rethrow in Wasm, which ought to treat a JS exception as an opaque value, as @KarlSchimpf points out.
@Ms2ger Again, I'm not sure what the relationship between the commit you referenced and this issue?