Re-wrapping with mismatched signatures
I would appreciate some more clarity (and perhaps also discussion...) on the expected behavior when using new WebAssembly.Function(inner_func, $sig0) for the case where inner_func has a signature (because it's another WebAssembly.Function, or an actual exported Wasm function) that doesn't match $sig0.
I understand that there is some limited coverage of this scenario in the existing tests, from which it can be inferred that currently the intention is that this should throw an error at runtime. If that is indeed intended, I would appreciate it if it was called out explicitly somewhere. If my understanding is wrong, then I would appreciate some clarification even more.
I also have some doubts whether that intention makes sense -- wouldn't it make more sense to make the WebAssembly.Function constructor itself throw immediately when someone attempts to construct something that can't possibly work at runtime? In other words, wouldn't it make sense if new WebAssembly.Function(inner_func, $sig0) took some inspiration from what happens when you do new WebAssembly.Instance(... { import_with_$sig0: inner_func } ...)?
Perhaps re-wrapping could even be disallowed entirely, on the grounds of being useless?
As a pragmatic observation, the current state of things has a tendency to cause confusion in humans and bugs in implementations -- with the latter being time-consuming to resolve due to the former.
My understanding is that nothing special ought to happen in that case — the already wrapped inner function is treated as any old JS function. That simply results in the consecutive application of ToWAValue(ToJSValue(ToWasmValue((x))) for each argument passed through, with whatever effect that might have for the types involved. An engine is of course free to optimise certain cases of this, but if I was an engine implementer, I would spend my resources elsewhere.
IIRC, the reason that we concluded to allow this wrapping was (1) to avoid special cases (and extra checks) and (2) that disallowing it would violate the idea that WA.Function is a subclass of Function. Though you are right that it could be argued that the latter is already the case for instantiation. Technically, in that case, it's rejected on the level of Wasm and its linking semantics, not in the JS API, but for a JS user that probably isn't a relevant difference. Then again, JS is full of inconsistencies like that, so ultimately, I wouldn't care. It just seems like not treating this case specially is the simplest choice.
IIRC, the reason that we concluded to allow this wrapping was (1) to avoid special cases (and extra checks) and (2) that disallowing it would violate the idea that
WA.Functionis a subclass ofFunction. Though you are right that it could be argued that the latter is already the case for instantiation. Technically, in that case, it's rejected on the level of Wasm and its linking semantics, not in the JS API, but for a JS user that probably isn't a relevant difference. Then again, JS is full of inconsistencies like that, so ultimately, I wouldn't care. It just seems like not treating this case specially is the simplest choice.
My rationale for this odd behavior is that it matches how instantiate treats imported globals. Importing a global does:
if importValue instanceof WebAssembly.Global
globalObj = importValue
else
globalObj = new WebAssembly.Global(importValue)
linkCheck(globalObj, importedGlobalType)
Importing a function now does the same behavior.
My understanding is that nothing special ought to happen in that case — the already wrapped inner function is treated as any old JS function. That simply results in the consecutive application of ToWAValue(ToJSValue(ToWasmValue((x))) for each argument passed through, with whatever effect that might have for the types involved. An engine is of course free to optimise certain cases of this, but if I was an engine implementer, I would spend my resources elsewhere.
It was exactly the reason to change the behavior from throwing immediately on construction to throw at runtime. We assume WebAssembly.Function is subclass of JSFunction and should behave in the same way without forcing user to insert if (fn instanceof WebAssembly.Function) checks where it is irrelevant.