-
Notifications
You must be signed in to change notification settings - Fork 72
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
Isorecursive types vs. JavaScript-created functions #292
Comments
The JS API generally wants the ability to accurately express all types expressible within Wasm. This includes recursive types, but also other GC types. So (2) is needed. Type exports may be an additional convenience in some cases, but are not a general replacement nor necessarily better – with only those, you'd need to construct and compile auxiliary Wasm modules to get types in certain scenarios, which is no less unwieldy, and exactly the kludge that the type reflection API is supposed to make unnecessary. |
Type exports seem the most convenient way to specify this. If a module expects to be handed a function of a particular type, it will have that type already defined and will know to export it. |
@askeksa-google, keep in mind that we don't have access to exports until we have instantiated a module. So you'd need a second module to instantiate and import the type from first if it's needed on the JS side to construct and supply a function import. |
For function imports we don't have this problem, do we? Since those already have a precise type specified in the module, the appropriate wrapper can be created directly from the supplied JS function at import time. |
True, but of course, there might be reasons to construct/wrap the function explicitly. And this won't apply to use cases like putting functions in tables or passing function references. |
Closing this in favor of more recent discussion in #338. |
This issue concerns the integration of the GC proposal and the Type Reflection proposal. (I suppose the discussion could happen in either's repository.)
With pre-GC Wasm, it's possible to export a function that takes another funcref as a parameter, typed as
anyref
orfuncref
orref $sig
for a specific signature$sig
. With "Type Reflection", matching function references can be constructed usingnew WebAssembly.Function(signature, js_callable)
, wheresignature
is a JavaScript object representing a description of the desired signature, e.g.{parameters: ["i32", "f64"], results: ["anyref"]}
. Since the signature is stored (internally) along with the funcref, the funcref can pass any required type checks (e.g. bycall_indirect
ortable.set
) and be called according to this signature. Engines can use this signature to compile an appropriate "wrapper" to make the underlying JS function callable from Wasm (which generally requires conversion of parameters/results, and depending on engine design choices also adaptation of the calling convention).With WasmGC and the isorecursive hybrid type system we are now pursuing, recursion groups matter: both for signatures themselves, and even more so for module-defined struct/array types that may be referenced from signatures. There is currently no way to specify rec-groups in
new WebAssembly.Function
, which significantly limits what can be done from JavaScript: specifically, it's impossible to refer to module-defined types.For the specific subset of cases where signatures only use generic types for all parameters and results, we can resolve this by treating each
WebAssembly.Function
call as creating an implicit one-element recgroup, which would be consistent with handling of legacy, non-recgroup-using type sections of modules. We may want to officially write down somewhere that that's the expected behavior. A (minor?) concern is that malicious or pathological JavaScript code could overwhelm the type canonicalization system/cache with an unbounded number of dynamically-generated types, and keeping track of when these can be cleaned up might be rather involved (i.e. have both an implementation complexity and a runtime performance cost).For full expressiveness, we'll need more. There are several options, including at least:
(1) Type exports. If there was a way to refer to types defined by a module, we could make it possible to use
WebAssembly.Function
to create functions whose signatures match that module's types. That could conceivably be either be the entire signature (example in strawman notation:new WebAssembly.Function(my_module.exports.signature1, js_callable)
), or it could be individual parameters and results (example:new WebAssembly.Function({parameters: [WebAssembly.optref(my_module.exports.my_type)], results: []}, js_callable)
).(2) Fully featured ability to construct types in JavaScript, in other words: extend the Type Reflection proposal by a facility to create entire rec-groups of types, and rely on implicit isorecursive canonicalization to have these externally-created types be identified with the module's own types. Note that this might get rather unwieldy for large rec-groups, especially when large modules choose to use fully nominal types by having a single huge rec-group. (We are expecting to see modules with tens of thousands of types, maybe more.)
(3) We could consider being more permissive and defining some kind of "on demand" system for adaptively "morphing" JS-provided functions to whatever signature they need in a given situation. That would be at odds with Wasm's generally strict approach to typing though, and I'm worried about performance costs (calls and/or typechecks could become significantly more expensive, this cost might fluctuate quite wildly depending on caching and overall app behavior), and I'm also worried about implementation complexity.
Any other ideas or concerns?
The text was updated successfully, but these errors were encountered: