From d478e4773843d0afc02390916a6ee3cff6289959 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 28 Sep 2025 14:55:47 -0700 Subject: [PATCH] Initial support for futures/streams in wit-dylib Needs cleaning up/tests. --- Cargo.lock | 117 +----------------------- crates/wit-component/src/dummy.rs | 77 ++++++---------- crates/wit-dylib/ffi/src/ffi.rs | 50 ++++++++++ crates/wit-dylib/ffi/src/lib.rs | 117 ++++++++++++++++++++++-- crates/wit-dylib/ffi/src/types.rs | 86 +++++++++++++++++- crates/wit-dylib/src/bindgen.rs | 24 +++++ crates/wit-dylib/src/lib.rs | 146 ++++++++++++++++++++++++++++-- crates/wit-dylib/src/metadata.rs | 90 ++++++++++++++++++ crates/wit-dylib/wit_dylib.h | 88 +++++++++++++++++- crates/wit-parser/src/lib.rs | 2 - crates/wit-parser/src/resolve.rs | 114 +++++++++++++++++++++++ 11 files changed, 721 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08713b025d..963ee9b375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -793,95 +793,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "fuzz-stats" version = "0.0.0" @@ -1483,18 +1394,6 @@ dependencies = [ "indexmap 2.10.0", ] -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "portable-atomic" version = "1.11.1" @@ -1856,12 +1755,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - [[package]] name = "smallvec" version = "1.15.1" @@ -2941,17 +2834,15 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "wit-bindgen" version = "0.46.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen#7208f7b39751d2ed4147bd82787a393e82f57c0c" +source = "git+https://github.com/bytecodealliance/wit-bindgen#6f67b5c2fe63e2e417b552f9f7bb6edc26e4955c" dependencies = [ - "futures", - "once_cell", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" version = "0.46.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen#7208f7b39751d2ed4147bd82787a393e82f57c0c" +source = "git+https://github.com/bytecodealliance/wit-bindgen#6f67b5c2fe63e2e417b552f9f7bb6edc26e4955c" dependencies = [ "anyhow", "heck 0.5.0", @@ -2979,7 +2870,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.46.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen#7208f7b39751d2ed4147bd82787a393e82f57c0c" +source = "git+https://github.com/bytecodealliance/wit-bindgen#6f67b5c2fe63e2e417b552f9f7bb6edc26e4955c" dependencies = [ "anyhow", "heck 0.5.0", @@ -2994,7 +2885,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.46.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen#7208f7b39751d2ed4147bd82787a393e82f57c0c" +source = "git+https://github.com/bytecodealliance/wit-bindgen#6f67b5c2fe63e2e417b552f9f7bb6edc26e4955c" dependencies = [ "anyhow", "prettyplease", diff --git a/crates/wit-component/src/dummy.rs b/crates/wit-component/src/dummy.rs index 1c87f50148..068803f05f 100644 --- a/crates/wit-component/src/dummy.rs +++ b/crates/wit-component/src/dummy.rs @@ -1,7 +1,7 @@ use wit_parser::abi::WasmType; use wit_parser::{ - Function, LiftLowerAbi, ManglingAndAbi, Resolve, ResourceIntrinsic, TypeDefKind, TypeId, - WasmExport, WasmExportKind, WasmImport, WorldId, WorldItem, WorldKey, + AsyncIntrinsic, Function, LiftLowerAbi, ManglingAndAbi, Resolve, ResourceIntrinsic, + TypeDefKind, TypeId, WasmExport, WasmExportKind, WasmImport, WorldId, WorldItem, WorldKey, }; /// Generate a dummy implementation core Wasm module for a given WIT document @@ -98,7 +98,7 @@ fn push_imported_func( wat.push_str("))\n"); if mangling.is_async() { - push_imported_future_and_stream_intrinsics(wat, resolve, "", interface, func); + push_imported_future_and_stream_intrinsics(wat, resolve, interface, func, false, mangling); } } @@ -156,67 +156,44 @@ fn push_exported_func_intrinsics( push_tys(wat, "result", &sig.results); wat.push_str("))\n"); - push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func); + push_imported_future_and_stream_intrinsics(wat, resolve, interface, func, true, mangling); } fn push_imported_future_and_stream_intrinsics( wat: &mut String, resolve: &Resolve, - module_prefix: &str, interface: Option<&WorldKey>, func: &Function, + export: bool, + mangling: ManglingAndAbi, ) { - let module = match interface { - Some(key) => format!("{module_prefix}{}", resolve.name_world_key(key)), - None => format!("{module_prefix}$root"), - }; - let name = &func.name; - for (i, id) in func .find_futures_and_streams(resolve) .into_iter() .enumerate() { - match &resolve.types[id].kind { - TypeDefKind::Future(_) => { - wat.push_str(&format!( - r#" -(import {module:?} "[future-new-{i}]{name}" (func (result i64))) -(import {module:?} "[future-read-{i}]{name}" (func (param i32 i32) (result i32))) -(import {module:?} "[future-write-{i}]{name}" (func (param i32 i32) (result i32))) -(import {module:?} "[future-cancel-read-{i}]{name}" (func (param i32) (result i32))) -(import {module:?} "[future-cancel-write-{i}]{name}" (func (param i32) (result i32))) -(import {module:?} "[future-drop-readable-{i}]{name}" (func (param i32))) -(import {module:?} "[future-drop-writable-{i}]{name}" (func (param i32))) -(import {module:?} "[async-lower][future-read-{i}]{name}" (func (param i32 i32) (result i32))) -(import {module:?} "[async-lower][future-write-{i}]{name}" (func (param i32 i32) (result i32))) - -;; deferred behind 🚝 -;;(import {module:?} "[async-lower][future-cancel-read-{i}]{name}" (func (param i32) (result i32))) -;;(import {module:?} "[async-lower][future-cancel-write-{i}]{name}" (func (param i32) (result i32))) -"# - )); - } - TypeDefKind::Stream(_) => { - wat.push_str(&format!( - r#" -(import {module:?} "[stream-new-{i}]{name}" (func (result i64))) -(import {module:?} "[stream-read-{i}]{name}" (func (param i32 i32 i32) (result i32))) -(import {module:?} "[stream-write-{i}]{name}" (func (param i32 i32 i32) (result i32))) -(import {module:?} "[stream-cancel-read-{i}]{name}" (func (param i32) (result i32))) -(import {module:?} "[stream-cancel-write-{i}]{name}" (func (param i32) (result i32))) -(import {module:?} "[stream-drop-readable-{i}]{name}" (func (param i32))) -(import {module:?} "[stream-drop-writable-{i}]{name}" (func (param i32))) -(import {module:?} "[async-lower][stream-read-{i}]{name}" (func (param i32 i32 i32) (result i32))) -(import {module:?} "[async-lower][stream-write-{i}]{name}" (func (param i32 i32 i32) (result i32))) - -;; deferred behind 🚝 -;;(import {module:?} "[async-lower][stream-cancel-read-{i}]{name}" (func (param i32) (result i32))) -;;(import {module:?} "[async-lower][stream-cancel-write-{i}]{name}" (func (param i32) (result i32))) -"# - )); - } + let is_future = match &resolve.types[id].kind { + TypeDefKind::Future(_) => true, + TypeDefKind::Stream(_) => false, _ => unreachable!(), + }; + for intrinsic in AsyncIntrinsic::ALL.iter().copied() { + let (module, name) = resolve.wasm_import_name( + mangling, + WasmImport::AsyncIntrinsic { + interface, + func, + intrinsic, + index: i, + is_future, + export, + }, + ); + let sig = intrinsic.signature(is_future); + wat.push_str(&format!("(import {module:?} {name:?} (func")); + push_tys(wat, "param", &sig.params); + push_tys(wat, "result", &sig.results); + wat.push_str("))\n"); } } } diff --git a/crates/wit-dylib/ffi/src/ffi.rs b/crates/wit-dylib/ffi/src/ffi.rs index d911c33e9b..d62fee491c 100644 --- a/crates/wit-dylib/ffi/src/ffi.rs +++ b/crates/wit-dylib/ffi/src/ffi.rs @@ -166,20 +166,70 @@ pub struct wit_fixed_size_list { pub ty: wit_type_t, } pub type wit_fixed_size_list_t = wit_fixed_size_list; +pub type wit_async_type_lift_t = ::std::option::Option< + unsafe extern "C" fn(cx: *mut ::std::os::raw::c_void, ptr: *const ::std::os::raw::c_void), +>; +pub type wit_async_type_lower_t = ::std::option::Option< + unsafe extern "C" fn(cx: *mut ::std::os::raw::c_void, ptr: *mut ::std::os::raw::c_void), +>; +pub type wit_future_new_t = ::std::option::Option u64>; +pub type wit_future_read_t = ::std::option::Option< + unsafe extern "C" fn(handle: u32, ptr: *mut ::std::os::raw::c_void) -> u32, +>; +pub type wit_future_write_t = ::std::option::Option< + unsafe extern "C" fn(handle: u32, ptr: *const ::std::os::raw::c_void) -> u32, +>; +pub type wit_future_cancel_t = ::std::option::Option u32>; +pub type wit_future_drop_t = ::std::option::Option; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct wit_future { pub interface: *const ::std::os::raw::c_char, pub name: *const ::std::os::raw::c_char, pub ty: wit_type_t, + pub new: wit_future_new_t, + pub read_async: wit_future_read_t, + pub write_async: wit_future_write_t, + pub read_sync: wit_future_read_t, + pub write_sync: wit_future_write_t, + pub cancel_read: wit_future_cancel_t, + pub cancel_write: wit_future_cancel_t, + pub drop_read: wit_future_drop_t, + pub drop_write: wit_future_drop_t, + pub lift: wit_async_type_lift_t, + pub lower: wit_async_type_lower_t, + pub elem_size: usize, + pub elem_align: usize, } pub type wit_future_t = wit_future; +pub type wit_stream_new_t = ::std::option::Option u64>; +pub type wit_stream_read_t = ::std::option::Option< + unsafe extern "C" fn(handle: u32, ptr: *mut ::std::os::raw::c_void, len: usize) -> u32, +>; +pub type wit_stream_write_t = ::std::option::Option< + unsafe extern "C" fn(handle: u32, ptr: *const ::std::os::raw::c_void, len: usize) -> u32, +>; +pub type wit_stream_cancel_t = ::std::option::Option u32>; +pub type wit_stream_drop_t = ::std::option::Option; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct wit_stream { pub interface: *const ::std::os::raw::c_char, pub name: *const ::std::os::raw::c_char, pub ty: wit_type_t, + pub new: wit_stream_new_t, + pub read_async: wit_stream_read_t, + pub write_async: wit_stream_write_t, + pub read_sync: wit_stream_read_t, + pub write_sync: wit_stream_write_t, + pub cancel_read: wit_stream_cancel_t, + pub cancel_write: wit_stream_cancel_t, + pub drop_read: wit_stream_drop_t, + pub drop_write: wit_stream_drop_t, + pub lift: wit_async_type_lift_t, + pub lower: wit_async_type_lower_t, + pub elem_size: usize, + pub elem_align: usize, } pub type wit_stream_t = wit_stream; #[repr(C)] diff --git a/crates/wit-dylib/ffi/src/lib.rs b/crates/wit-dylib/ffi/src/lib.rs index 132e0841f6..3fa08a19bf 100644 --- a/crates/wit-dylib/ffi/src/lib.rs +++ b/crates/wit-dylib/ffi/src/lib.rs @@ -1,12 +1,16 @@ //! A helper crate for providing a more ergonomic and safe Rust API over the //! `wit_dylib.h` interface. //! -//! The `ffi` module in this crate is the raw `bindgen`-generated Rust bindings -//! for the C header file. The rest of the crate is the built on top of that. +//! The [`ffi`] module in this crate is the raw `bindgen`-generated Rust +//! bindings for the `wit_dylib.h` C header file. The rest of the crate is the +//! built on top of that. //! -//! A `test_util` module provides a sample implementation of an "interpreter" -//! which is used for test cases here and can also be a possibly-helpful -//! reference to an implementation. +//! The main items in this crate are: +//! +//! * [`Interpreter`] - you'll implement this and fill out functionality +//! accordingly. +//! * [`export!`] - used to specify the type that implements [`Interpreter`] and +//! defines a bunch of `no_mangle` functions. #![allow(unsafe_code)] #![allow(clippy::allow_attributes_without_reason)] @@ -20,7 +24,7 @@ use std::ptr; /// through the trait methods here. #[macro_export] macro_rules! export { - ($name:ident) => { + ($name:ty) => { #[no_mangle] pub unsafe extern "C" fn wit_dylib_initialize(ptr: *const u8) { unsafe { <$name as $crate::RawInterpreter>::raw_initialize(ptr) } @@ -356,42 +360,123 @@ macro_rules! export { }; } +/// Implementation of an interpreter or the dynamic WIT context. +/// +/// The type that implements this trait is fed into the [`export!`] macro. This +/// trait does not have `&self` methods as it is invoked ambiently as wasm +/// exports with no additional context provided. +/// /// # Safety /// /// `Self::Borrow` and `Self::Own` must have same in-memory representation and /// generally represent correct ownership semantics. pub trait Interpreter: 'static { + /// Contextual state for an export or import call. + /// + /// This state uses the [`Call`] trait to convert WIT values into the + /// interpreter language's values. type CallCx<'a>: Call; /// Startup hook if necessary. + /// + /// An example of using this would be to read the `wit` provided and define + /// language-specific structures in the interpreter such as classes. fn initialize(wit: Wit) { let _ = wit; } + /// Entrypoint for starting an export call. + /// + /// This function is invoked at the start of all export being invoked from + /// the outside world. The `wit` and `func` arguments provided indicate + /// which export is being called. A `Self::CallCx` is returned which is used + /// to push/pop values for the rest of the call. fn export_start<'a>(wit: Wit, func: Function) -> Box>; + /// Implementation of dispatcihng an export. + /// + /// This is called after [`Self::export_start`] and after all arguments have + /// been pushed into the `cx` argument via the `wit-dylib`-generated + /// function. + /// + /// This function should dispatch the export call to the language in + /// question. When this function returns it must arrange for the language's + /// return value, if any, to be present in `cx`. The values will be removed + /// and converted to the canonical ABI after this function returns. fn export_call(wit: Wit, func: Function, cx: &mut Self::CallCx<'_>); + /// Async equivalent of [`Self::export_call`]. + /// + /// The main difference with [`Self::export_call`] is that the `cx` argument + /// is provided with ownership here. It is the responsibility of this + /// function to invoke the `task.return` function when appropriate. This can + /// be accessed with [`Function::task_return`]. Note that when doing so it's + /// expected that this function's return value will be present in `cx`. + /// + /// This function returns a future which resolves to no value. The return + /// value is signaled through invoking [`Function::task_return`]. The future + /// will be executed to represent the component model task being executed. fn export_call_async( wit: Wit, func: Function, cx: Box>, ) -> impl std::future::Future; + /// Optional hook to dispose of a `CallCx`. + /// + /// The default implementation is to just drop and deallocate it, but the + /// interpreter could cache it if desired. fn export_finish(cx: Box>, func: Function) { let _ = func; let _ = cx; } + /// Helper method to dispatch a call to an imported function. + /// + /// The `interface` and `func` are used to lookup a function within `wit` + /// using [`Wit::unwrap_import`]. Afterwards `cx` is then used to pass to + /// [`Function::call_import_sync`]. fn import_call(wit: Wit, interface: Option<&str>, func: &str, cx: &mut Self::CallCx<'_>) { let func = wit.unwrap_import(interface, func); func.call_import_sync(cx) } + /// Entrypoint for resource destruction. + /// + /// This is invoked whenever a resource defined by this component is + /// destroyed. The `handle` provided is the "rep" internal representation + /// that was provided to the resource constructor. This could be, for + /// example, an allocated pointer. The `ty` contextual information is also + /// provided as for what type of resource is being destroyed. fn resource_dtor(ty: Resource, handle: usize); } +/// Implementation of a "call context" used for import/export calls. +/// +/// Invocations of a WIT export or WIT import with `wit-dylib` is modeled as a +/// "stack" which this trait represents. For example arguments to an exported +/// function, when called, are pushed into a `Call` implementation. Results are +/// then popped from the stack. When invoking a WIT import arguments are also +/// pushed onto the stack, initially, popped by `wit-dylib` intrinsics, and then +/// the result is pushed via `wit-dylib` intrinsics and made available to the +/// guest. +/// +/// Each function here is effectively converting between the canonical ABI of +/// the component model and this trait's internal representation. +/// +/// For more documentation of individual functions see the [`wit_dylib.h`] +/// header file. +/// +/// [`wit_dylib.h`]: https://github.com/bytecodealliance/wasm-tools/blob/main/crates/wit-dylib/wit_dylib.h pub trait Call { + /// Defers deallocation of `ptr`, with allocation parameters `layout`, to + /// when `Self` is destroyed. + /// + /// This is required when the interpreters representation of a list doesn't + /// match the canonical ABI. For example when passing a `list` to an + /// imported function a temporary allocation will be required to convert it + /// to the canonical ABI format. This function defers deallocation until the + /// call is complete. unsafe fn defer_deallocate(&mut self, ptr: *mut u8, layout: Layout); fn pop_u8(&mut self) -> u8; @@ -419,6 +504,12 @@ pub trait Call { fn pop_record(&mut self, ty: Record); fn pop_tuple(&mut self, ty: Tuple); + /// Attempts to pop a list in canonical ABI form from the stack of the `ty` + /// provided. + /// + /// Returns `None` if the list is not in canonical ABI format. Returns + /// `Some` with the pointer/length that match the canonical ABI of `ty`. If + /// `None` is returned then `Self::pop_list` will be invoked next. unsafe fn maybe_pop_list(&mut self, ty: List) -> Option<(*const u8, usize)> { let _ = ty; None @@ -451,6 +542,15 @@ pub trait Call { fn push_variant(&mut self, ty: Variant, discr: u32); fn push_option(&mut self, ty: WitOption, is_some: bool); fn push_result(&mut self, ty: WitResult, is_err: bool); + + /// Attempts to pushes a raw list onto this stack. + /// + /// If `ptr` and `len` doesn't match this language's representation then + /// `false` is returned. Otherwise `true` is returned and `ptr` and `len` + /// are an owned allocation now owned by this stack. + /// + /// If `false` is returned then `push_list` will be invoked next and + /// `list_append` will be invoked one-by-one for each element. unsafe fn push_raw_list(&mut self, ty: List, ptr: *mut u8, len: usize) -> bool { let _ = (ty, ptr, len); false @@ -940,8 +1040,9 @@ pub trait RawInterpreter: Interpreter { impl RawInterpreter for T {} -#[allow(dead_code, non_camel_case_types)] -mod ffi; +/// Raw `bindgen`-generated bindings for `wit_dylib.h`. +#[allow(non_camel_case_types)] +pub mod ffi; mod types; pub use self::types::*; diff --git a/crates/wit-dylib/ffi/src/types.rs b/crates/wit-dylib/ffi/src/types.rs index 76a419e6bd..1d4651666d 100644 --- a/crates/wit-dylib/ffi/src/types.rs +++ b/crates/wit-dylib/ffi/src/types.rs @@ -292,6 +292,10 @@ impl Wit { .iter() .map(|e| Alias { wit: *self, ptr: e }) } + + pub fn raw(&self) -> &'static ffi::wit_t { + self.ptr + } } unsafe fn slice(ptr: *const T, len: usize) -> &'static [T] { @@ -353,6 +357,18 @@ impl Function { Type::from_raw_opt(self.wit, self.ptr.result) } + /// Invokes this imported function, synchronously, with the `cx` provided. + /// + /// The `cx` must have all arguments for this function already pushed on + /// it, in reverse order (so the first argument is the top of the stack). + /// The arguments will be popped from `cx` as they're translated into the + /// canonical ABI. This will then block synchronously on invoking the + /// imported function. Upon returning the return value, if any, will have + /// been pushed onto `cx`. + /// + /// # Panics + /// + /// Panics if this function is not imported synchronously. pub fn call_import_sync(&self, cx: &mut impl Call) { let import_impl = self.sync_import_impl().unwrap(); unsafe { @@ -361,6 +377,14 @@ impl Function { } } + /// Async version of [`Self::call_import_sync`]. + /// + /// Arguments should already be in `cx` and this won't return until the + /// result has been pushed to `cx`. + /// + /// # Panics + /// + /// Panics if this function is not imported asynchronously. #[cfg(feature = "async")] pub async fn call_import_async(&self, cx: &mut impl Call) { use core::alloc::Layout; @@ -406,6 +430,10 @@ impl Function { } } } + + pub fn raw(&self) -> &'static ffi::wit_func_t { + self.ptr + } } impl fmt::Debug for Function { @@ -500,7 +528,7 @@ impl Type { } fn from_raw_opt(wit: Wit, raw: ffi::wit_type_t) -> Option { - if raw & 0xff == 0xff { + if raw & 0xff == ffi::WIT_TYPE_EMPTY { None } else { Some(Self::from_raw(wit, raw)) @@ -532,6 +560,10 @@ impl Record { .map(|field| (to_str(field.name), Type::from_raw(self.wit, field.ty))) } } + + pub fn raw(&self) -> &'static ffi::wit_record_t { + self.ptr + } } impl fmt::Debug for Record { @@ -571,6 +603,10 @@ impl Resource { pub fn rep(&self) -> Option usize> { self.ptr.rep } + + pub fn raw(&self) -> &'static ffi::wit_resource_t { + self.ptr + } } impl fmt::Debug for Resource { @@ -605,6 +641,10 @@ impl Flags { .map(|name| to_str(*name)) } } + + pub fn raw(&self) -> &'static ffi::wit_flags_t { + self.ptr + } } impl fmt::Debug for Flags { @@ -641,6 +681,10 @@ impl Tuple { .map(|name| Type::from_raw(self.wit, *name)) } } + + pub fn raw(&self) -> &'static ffi::wit_tuple_t { + self.ptr + } } impl fmt::Debug for Tuple { @@ -677,6 +721,10 @@ impl Variant { .map(|case| (to_str(case.name), Type::from_raw_opt(self.wit, case.ty))) } } + + pub fn raw(&self) -> &'static ffi::wit_variant_t { + self.ptr + } } impl fmt::Debug for Variant { @@ -712,6 +760,10 @@ impl Enum { .map(|name| to_str(*name)) } } + + pub fn raw(&self) -> &'static ffi::wit_enum_t { + self.ptr + } } impl fmt::Debug for Enum { @@ -744,6 +796,10 @@ impl WitOption { pub fn ty(&self) -> Type { Type::from_raw(self.wit, self.ptr.ty) } + + pub fn raw(&self) -> &'static ffi::wit_option_t { + self.ptr + } } impl fmt::Debug for WitOption { @@ -780,6 +836,10 @@ impl WitResult { pub fn err(&self) -> Option { Type::from_raw_opt(self.wit, self.ptr.err) } + + pub fn raw(&self) -> &'static ffi::wit_result_t { + self.ptr + } } impl fmt::Debug for WitResult { @@ -813,6 +873,10 @@ impl List { pub fn ty(&self) -> Type { Type::from_raw(self.wit, self.ptr.ty) } + + pub fn raw(&self) -> &'static ffi::wit_list_t { + self.ptr + } } impl fmt::Debug for List { @@ -849,6 +913,10 @@ impl FixedSizeList { pub fn ty(&self) -> Type { Type::from_raw(self.wit, self.ptr.ty) } + + pub fn raw(&self) -> &'static ffi::wit_fixed_size_list_t { + self.ptr + } } impl fmt::Debug for FixedSizeList { @@ -882,6 +950,10 @@ impl Future { pub fn ty(&self) -> Option { Type::from_raw_opt(self.wit, self.ptr.ty) } + + pub fn raw(&self) -> &'static ffi::wit_future_t { + self.ptr + } } impl fmt::Debug for Future { @@ -890,7 +962,7 @@ impl fmt::Debug for Future { .field("interface", &self.interface()) .field("name", &self.name()) .field("ty", &self.ty()) - .finish() + .finish_non_exhaustive() } } @@ -914,6 +986,10 @@ impl Stream { pub fn ty(&self) -> Option { Type::from_raw_opt(self.wit, self.ptr.ty) } + + pub fn raw(&self) -> &'static ffi::wit_stream_t { + self.ptr + } } impl fmt::Debug for Stream { @@ -922,7 +998,7 @@ impl fmt::Debug for Stream { .field("interface", &self.interface()) .field("name", &self.name()) .field("ty", &self.ty()) - .finish() + .finish_non_exhaustive() } } @@ -946,6 +1022,10 @@ impl Alias { pub fn ty(&self) -> Type { Type::from_raw(self.wit, self.ptr.ty) } + + pub fn raw(&self) -> &'static ffi::wit_alias_t { + self.ptr + } } impl fmt::Debug for Alias { diff --git a/crates/wit-dylib/src/bindgen.rs b/crates/wit-dylib/src/bindgen.rs index d19d310b5e..3fdfd836d0 100644 --- a/crates/wit-dylib/src/bindgen.rs +++ b/crates/wit-dylib/src/bindgen.rs @@ -425,6 +425,30 @@ pub fn task_return( ) } +pub fn lift_from_memory(adapter: &mut Adapter, resolve: &Resolve, ty: Type) -> Function { + let mut c = FunctionCompiler::new(adapter, resolve, 2); + c.ctx = Some(TempLocal::new(0, ValType::I32)); + let src = AbiLoc::Memory(Memory { + addr: TempLocal::new(1, ValType::I32), + offset: 0, + }); + + c.lift(&src, ty); + c.finish() +} + +pub fn lower_to_memory(adapter: &mut Adapter, resolve: &Resolve, ty: Type) -> Function { + let mut c = FunctionCompiler::new(adapter, resolve, 2); + c.ctx = Some(TempLocal::new(0, ValType::I32)); + let dst = AbiLoc::Memory(Memory { + addr: TempLocal::new(1, ValType::I32), + offset: 0, + }); + + c.lower(ty, &dst); + c.finish() +} + struct FunctionCompiler<'a> { adapter: &'a mut Adapter, resolve: &'a Resolve, diff --git a/crates/wit-dylib/src/lib.rs b/crates/wit-dylib/src/lib.rs index 62288165ac..66ec23e22c 100644 --- a/crates/wit-dylib/src/lib.rs +++ b/crates/wit-dylib/src/lib.rs @@ -9,9 +9,9 @@ use wasm_encoder::{ }; use wit_parser::abi::{WasmSignature, WasmType}; use wit_parser::{ - Handle, LiftLowerAbi, LiveTypes, ManglingAndAbi, Resolve, ResourceIntrinsic, SizeAlign, Type, - TypeDefKind, TypeId, TypeOwner, WasmExport, WasmExportKind, WasmImport, WorldId, WorldItem, - WorldKey, + AsyncIntrinsic, Handle, LiftLowerAbi, LiveTypes, ManglingAndAbi, Resolve, ResourceIntrinsic, + SizeAlign, Type, TypeDefKind, TypeId, TypeOwner, WasmExport, WasmExportKind, WasmImport, + WorldId, WorldItem, WorldKey, }; mod async_; @@ -81,6 +81,7 @@ struct Adapter { struct Imports<'a> { wit_imports: Vec>, wit_exports: Vec>, + wit_async_types: HashMap, } struct WitImport<'a> { @@ -95,6 +96,18 @@ struct WitExport<'a> { async_task_return_index: Option, } +struct WitAsyncTypeImports { + new: u32, + read_async: u32, + write_async: u32, + read_sync: u32, + write_sync: u32, + cancel_read: u32, + cancel_write: u32, + drop_read: u32, + drop_write: u32, +} + impl Adapter { pub fn encode(&mut self, resolve: &Resolve, world_id: WorldId) -> Vec { self.sizes.fill(resolve); @@ -272,6 +285,8 @@ impl Adapter { interface, import_index, }); + + self.add_imported_async_type_intrinsics(resolve, interface, func, true, imports); } fn add_imported_type_intrinsics<'a>( @@ -393,6 +408,66 @@ impl Adapter { func, async_task_return_index, }); + + self.add_imported_async_type_intrinsics(resolve, interface, func, false, ret); + } + + fn add_imported_async_type_intrinsics( + &mut self, + resolve: &Resolve, + interface: Option<&WorldKey>, + func: &wit_parser::Function, + is_import: bool, + ret: &mut Imports<'_>, + ) { + let mangling = self.mangling(resolve, interface, func, is_import); + for (i, id) in func + .find_futures_and_streams(resolve) + .into_iter() + .enumerate() + { + if ret.wit_async_types.contains_key(&id) { + continue; + } + + let is_future = match &resolve.types[id].kind { + TypeDefKind::Future(_) => true, + TypeDefKind::Stream(_) => false, + _ => unreachable!(), + }; + + let mut import = |intrinsic: AsyncIntrinsic| { + let sig = intrinsic.signature(is_future); + let ty = self.define_wasm_sig(sig); + let (module, name) = resolve.wasm_import_name( + mangling, + WasmImport::AsyncIntrinsic { + interface, + func, + export: !is_import, + index: i, + intrinsic, + is_future, + }, + ); + self.import_func(&module, &name, ty) + }; + + let ty = WitAsyncTypeImports { + new: import(AsyncIntrinsic::New), + read_sync: import(AsyncIntrinsic::ReadSync), + read_async: import(AsyncIntrinsic::ReadAsync), + write_sync: import(AsyncIntrinsic::WriteSync), + write_async: import(AsyncIntrinsic::WriteAsync), + cancel_read: import(AsyncIntrinsic::CancelRead), + cancel_write: import(AsyncIntrinsic::CancelWrite), + drop_read: import(AsyncIntrinsic::DropReadable), + drop_write: import(AsyncIntrinsic::DropWritable), + }; + + let prev = ret.wit_async_types.insert(id, ty); + assert!(prev.is_none()); + } } fn bindgen_world(&mut self, resolve: &Resolve, world_id: WorldId, imports: &Imports<'_>) { @@ -421,7 +496,7 @@ impl Adapter { TypeOwner::Interface(id) => Some(interface_names[&id]), _ => None, }; - self.register_type(resolve, key, ty); + self.register_type(resolve, imports, key, ty); } // Using the populated type map for imports generate functions to invoke @@ -461,7 +536,7 @@ impl Adapter { TypeOwner::Interface(id) => Some(export_names[&id]), _ => None, }; - self.register_type(resolve, key, ty); + self.register_type(resolve, imports, key, ty); if let Some(index) = self.resource_map.get(&ty) { self.bindgen_world_export_resource_dtor(resolve, key.unwrap(), ty, *index); @@ -480,7 +555,13 @@ impl Adapter { /// This will insert `id` into metadata and build up the interpreter data /// structures for it. The end-result is the population of `self.type_map` /// here. - fn register_type(&mut self, resolve: &Resolve, key: Option<&WorldKey>, id: TypeId) { + fn register_type( + &mut self, + resolve: &Resolve, + imports: &Imports<'_>, + key: Option<&WorldKey>, + id: TypeId, + ) { let ty = &resolve.types[id]; let interface = key.map(|key| resolve.name_world_key(key)); let name = ty.name.clone(); @@ -585,20 +666,56 @@ impl Adapter { } TypeDefKind::Future(t) => { let index = self.metadata.futures.len(); - self.metadata.futures.push(metadata::Future { + let elem_size = t.map(|t| self.sizes.size(&t).size_wasm32()).unwrap_or(0); + let elem_align = t.map(|t| self.sizes.align(&t).align_wasm32()).unwrap_or(1); + let imports = &imports.wit_async_types[&id]; + let lift_lower_elem_indexes = + t.map(|t| self.define_async_type_lift_lower(resolve, t)); + let metadata = metadata::Future { interface, name, ty: t.map(|t| self.lookup_ty(&t)), - }); + new_elem_index: self.push_elem(imports.new), + read_async_elem_index: self.push_elem(imports.read_async), + write_async_elem_index: self.push_elem(imports.write_async), + read_sync_elem_index: self.push_elem(imports.read_sync), + write_sync_elem_index: self.push_elem(imports.write_sync), + cancel_read_elem_index: self.push_elem(imports.cancel_read), + cancel_write_elem_index: self.push_elem(imports.cancel_write), + drop_read_elem_index: self.push_elem(imports.drop_read), + drop_write_elem_index: self.push_elem(imports.drop_write), + lift_lower_elem_indexes, + elem_size, + elem_align, + }; + self.metadata.futures.push(metadata); metadata::Type::Future(index) } TypeDefKind::Stream(t) => { let index = self.metadata.streams.len(); - self.metadata.streams.push(metadata::Stream { + let elem_size = t.map(|t| self.sizes.size(&t).size_wasm32()).unwrap_or(0); + let elem_align = t.map(|t| self.sizes.align(&t).align_wasm32()).unwrap_or(1); + let imports = &imports.wit_async_types[&id]; + let lift_lower_elem_indexes = + t.map(|t| self.define_async_type_lift_lower(resolve, t)); + let metadata = metadata::Stream { interface, name, ty: t.map(|t| self.lookup_ty(&t)), - }); + new_elem_index: self.push_elem(imports.new), + read_async_elem_index: self.push_elem(imports.read_async), + write_async_elem_index: self.push_elem(imports.write_async), + read_sync_elem_index: self.push_elem(imports.read_sync), + write_sync_elem_index: self.push_elem(imports.write_sync), + cancel_read_elem_index: self.push_elem(imports.cancel_read), + cancel_write_elem_index: self.push_elem(imports.cancel_write), + drop_read_elem_index: self.push_elem(imports.drop_read), + drop_write_elem_index: self.push_elem(imports.drop_write), + lift_lower_elem_indexes, + elem_size, + elem_align, + }; + self.metadata.streams.push(metadata); metadata::Type::Stream(index) } TypeDefKind::Type(t) => { @@ -648,6 +765,15 @@ impl Adapter { } } + fn define_async_type_lift_lower(&mut self, resolve: &Resolve, ty: Type) -> (u32, u32) { + let lift = bindgen::lift_from_memory(self, resolve, ty); + let lower = bindgen::lower_to_memory(self, resolve, ty); + let ty = self.define_ty([ValType::I32; 2], []); + let lift = self.define_func("lift-one", ty, lift, false); + let lower = self.define_func("lower-one", ty, lower, false); + (self.push_elem(lift), self.push_elem(lower)) + } + fn bindgen_world_func_import(&mut self, resolve: &Resolve, import: &WitImport<'_>) { let func = import.func; let mangling = self.mangling(resolve, import.interface, func, true); diff --git a/crates/wit-dylib/src/metadata.rs b/crates/wit-dylib/src/metadata.rs index 1d031ad272..d6c7250800 100644 --- a/crates/wit-dylib/src/metadata.rs +++ b/crates/wit-dylib/src/metadata.rs @@ -102,12 +102,36 @@ pub struct Future { pub interface: Option, pub name: Option, pub ty: Option, + pub new_elem_index: u32, + pub read_async_elem_index: u32, + pub write_async_elem_index: u32, + pub read_sync_elem_index: u32, + pub write_sync_elem_index: u32, + pub cancel_read_elem_index: u32, + pub cancel_write_elem_index: u32, + pub drop_read_elem_index: u32, + pub drop_write_elem_index: u32, + pub lift_lower_elem_indexes: Option<(u32, u32)>, + pub elem_size: usize, + pub elem_align: usize, } pub struct Stream { pub interface: Option, pub name: Option, pub ty: Option, + pub new_elem_index: u32, + pub read_async_elem_index: u32, + pub write_async_elem_index: u32, + pub read_sync_elem_index: u32, + pub write_sync_elem_index: u32, + pub cancel_read_elem_index: u32, + pub cancel_write_elem_index: u32, + pub drop_read_elem_index: u32, + pub drop_write_elem_index: u32, + pub lift_lower_elem_indexes: Option<(u32, u32)>, + pub elem_size: usize, + pub elem_align: usize, } pub struct Alias { @@ -481,10 +505,43 @@ impl Encoder { interface, name, ty, + new_elem_index, + read_async_elem_index, + write_async_elem_index, + read_sync_elem_index, + write_sync_elem_index, + cancel_read_elem_index, + cancel_write_elem_index, + drop_read_elem_index, + drop_write_elem_index, + lift_lower_elem_indexes, + elem_size, + elem_align, } = future; self.opt_string_ptr(interface.as_deref()); self.opt_string_ptr(name.as_deref()); self.opt_ty(ty.as_ref()); + self.elem_index(*new_elem_index); + self.elem_index(*read_async_elem_index); + self.elem_index(*write_async_elem_index); + self.elem_index(*read_sync_elem_index); + self.elem_index(*write_sync_elem_index); + self.elem_index(*cancel_read_elem_index); + self.elem_index(*cancel_write_elem_index); + self.elem_index(*drop_read_elem_index); + self.elem_index(*drop_write_elem_index); + match lift_lower_elem_indexes { + Some((lift, lower)) => { + self.elem_index(*lift); + self.elem_index(*lower); + } + None => { + self.put_usize(0); + self.put_usize(0); + } + } + self.put_usize(*elem_size); + self.put_usize(*elem_align); } } @@ -494,10 +551,43 @@ impl Encoder { interface, name, ty, + new_elem_index, + read_async_elem_index, + write_async_elem_index, + read_sync_elem_index, + write_sync_elem_index, + cancel_read_elem_index, + cancel_write_elem_index, + drop_read_elem_index, + drop_write_elem_index, + lift_lower_elem_indexes, + elem_size, + elem_align, } = stream; self.opt_string_ptr(interface.as_deref()); self.opt_string_ptr(name.as_deref()); self.opt_ty(ty.as_ref()); + self.elem_index(*new_elem_index); + self.elem_index(*read_async_elem_index); + self.elem_index(*write_async_elem_index); + self.elem_index(*read_sync_elem_index); + self.elem_index(*write_sync_elem_index); + self.elem_index(*cancel_read_elem_index); + self.elem_index(*cancel_write_elem_index); + self.elem_index(*drop_read_elem_index); + self.elem_index(*drop_write_elem_index); + match lift_lower_elem_indexes { + Some((lift, lower)) => { + self.elem_index(*lift); + self.elem_index(*lower); + } + None => { + self.put_usize(0); + self.put_usize(0); + } + } + self.put_usize(*elem_size); + self.put_usize(*elem_align); } } diff --git a/crates/wit-dylib/wit_dylib.h b/crates/wit-dylib/wit_dylib.h index 247681ecf0..64201e2e69 100644 --- a/crates/wit-dylib/wit_dylib.h +++ b/crates/wit-dylib/wit_dylib.h @@ -10,8 +10,8 @@ // invoke functions. Various other `wit_dylib_*` intrinsics will receive indices // relative to the original `wit_t` value. -#ifndef WIT_INTERPRETER_H -#define WIT_INTERPRETER_H +#ifndef WIT_DYLIB_H +#define WIT_DYLIB_H #include #include @@ -198,18 +198,98 @@ typedef struct wit_fixed_size_list { wit_type_t ty; } wit_fixed_size_list_t; +// These callbacks are used in `wit_future_t` and `wit_stream_t` below to +// lift/lower a single element into a canonical ABI location in memory. +// +// The `elem_{size,align}` fields of `wit_{future,stream}_t` dictate the +// size/alignment required of `ptr`. The `cx` field is forwarded to +// `wit_dylib_{push,pop}_*` functions as-is. +// +// The `wit_async_type_lift_t` type will "lift" a value from the canonical ABI +// representation into the interpreter's representation. This will be done by +// callling the appropriate `wit_dylib_push_*` intrinsic. +// +// The `wit_async_type_lower_t` type will "lower" a value from the +// interpreter's representation into the canonical ABI representation. This +// will be done by callling the appropriate `wit_dylib_pop_*` intrinsic. +typedef void(*wit_async_type_lift_t)(void *cx, const void *ptr); +typedef void(*wit_async_type_lower_t)(void *cx, void *ptr); + +typedef uint64_t(*wit_future_new_t)(void); +typedef uint32_t(*wit_future_read_t)(uint32_t handle, void *ptr); +typedef uint32_t(*wit_future_write_t)(uint32_t handle, const void *ptr); +typedef uint32_t(*wit_future_cancel_t)(uint32_t handle); +typedef void(*wit_future_drop_t)(uint32_t handle); + typedef struct wit_future { const char *interface; const char *name; wit_type_t ty; - // TODO: include future-related intrinsics for reading/writing + + // Raw component model intrinsics for this future. Note that these + // signatures are dictated by the component model itself. + wit_future_new_t new; + wit_future_read_t read_async; + wit_future_write_t write_async; + wit_future_read_t read_sync; + wit_future_write_t write_sync; + wit_future_cancel_t cancel_read; + wit_future_cancel_t cancel_write; + wit_future_drop_t drop_read; + wit_future_drop_t drop_write; + + // Individual element lift/lower for reading/writing to this future. + // + // These are `NULL` when `ty` is `WIT_TYPE_EMPTY`. + wit_async_type_lift_t lift; + wit_async_type_lower_t lower; + + // Size, in bytes, of the canonical ABI of `ty`. If `ty` is WIT_TYPE_EMPTY + // then this is 0. + size_t elem_size; + // Alignment, in bytes, of the canonical ABI of `ty`. If `ty` is + // WIT_TYPE_EMPTY then this is 1. + size_t elem_align; } wit_future_t; +typedef uint64_t(*wit_stream_new_t)(void); +typedef uint32_t(*wit_stream_read_t)(uint32_t handle, void *ptr, size_t len); +typedef uint32_t(*wit_stream_write_t)(uint32_t handle, const void *ptr, size_t len); +typedef uint32_t(*wit_stream_cancel_t)(uint32_t handle); +typedef void(*wit_stream_drop_t)(uint32_t handle); + typedef struct wit_stream { const char *interface; const char *name; wit_type_t ty; - // TODO: include stream-related intrinsics for reading/writing + + // Raw component model intrinsics for this future. Note that these + // signatures are dictated by the component model itself. + wit_stream_new_t new; + wit_stream_read_t read_async; + wit_stream_write_t write_async; + wit_stream_read_t read_sync; + wit_stream_write_t write_sync; + wit_stream_cancel_t cancel_read; + wit_stream_cancel_t cancel_write; + wit_stream_drop_t drop_read; + wit_stream_drop_t drop_write; + + // Individual element lift/lower for reading/writing to this stream. + // + // If multiple items are read or multiple items are written then these + // functions need to be invoked multiple times. + // + // These are `NULL` when `ty` is `WIT_TYPE_EMPTY`. + wit_async_type_lift_t lift; + wit_async_type_lower_t lower; + + // Size, in bytes, of the canonical ABI of `ty`. If `ty` is WIT_TYPE_EMPTY + // then this is 0. + size_t elem_size; + // Alignment, in bytes, of the canonical ABI of `ty`. If `ty` is + // WIT_TYPE_EMPTY then this is 1. + size_t elem_align; } wit_stream_t; typedef struct wit_alias { diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 7274758491..310a93f06d 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -1236,8 +1236,6 @@ impl Function { let sig = resolve.wasm_signature(AbiVariant::GuestImport, &func_tmp); (module, name, sig) } - - // push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func); } fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec) { diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 3da24d6675..e7c84a411c 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -12,6 +12,7 @@ use semver::Version; #[cfg(feature = "serde")] use serde_derive::Serialize; +use crate::abi::{WasmSignature, WasmType}; use crate::ast::lex::Span; use crate::ast::{ParsedUsePath, parse_use_path}; #[cfg(feature = "serde")] @@ -2545,6 +2546,7 @@ package {name} is defined in two different locations:\n\ }; (module, name) } + WasmImport::AsyncIntrinsic { .. } => todo!(), }, ManglingAndAbi::Legacy(abi) => match import { WasmImport::Func { interface, func } => { @@ -2581,6 +2583,43 @@ package {name} is defined in two different locations:\n\ }; (module, format!("{}{name}", abi.import_prefix())) } + WasmImport::AsyncIntrinsic { + interface, + func, + export, + index, + intrinsic, + is_future, + } => { + let prefix = if export { "[export]" } else { "" }; + let module = match interface { + Some(key) => self.name_world_key(key), + None => format!("$root"), + }; + let (async_prefix, intrinsic) = match intrinsic { + AsyncIntrinsic::New => ("", "new"), + AsyncIntrinsic::ReadSync => ("", "read"), + AsyncIntrinsic::WriteSync => ("", "write"), + AsyncIntrinsic::ReadAsync => ("[async-lower]", "read"), + AsyncIntrinsic::WriteAsync => ("[async-lower]", "write"), + AsyncIntrinsic::CancelRead => ("", "cancel-read"), + AsyncIntrinsic::CancelWrite => ("", "cancel-write"), + AsyncIntrinsic::DropReadable => ("", "drop-readable"), + AsyncIntrinsic::DropWritable => ("", "drop-writable"), + }; + let ty = if is_future { "future" } else { "stream" }; + ( + format!("{prefix}{module}"), + format!( + "\ + {async_prefix}\ + [{ty}-{intrinsic}-{index}]\ + {}\ + ", + func.name + ), + ) + } }, } } @@ -2690,6 +2729,28 @@ pub enum WasmImport<'a> { /// The intrinsic that's being imported. intrinsic: ResourceIntrinsic, }, + + /// A WIT function is being imported. Optionally from an interface. + AsyncIntrinsic { + /// The name of the interface that the function is being imported from. + interface: Option<&'a WorldKey>, + + /// The function that `index` points within. + func: &'a Function, + + /// Whether or not `func` is an exported function or not. + export: bool, + + /// The index of the future or stream, as indicated by + /// `Function::find_futures_and_streams`. + index: usize, + + /// The intrinsic being imported. + intrinsic: AsyncIntrinsic, + + /// Whether or not this intrinsic is for a future or a stream. + is_future: bool, + }, } /// Intrinsic definitions to go with [`WasmImport::ResourceIntrinsic`] which @@ -2702,6 +2763,59 @@ pub enum ResourceIntrinsic { ExportedRep, } +/// Intrinsic definitions to go with [`WasmImport::AsyncIntrinsic`] which +/// also goes with [`Resolve::wasm_import_name`]. +#[derive(Debug, Copy, Clone)] +pub enum AsyncIntrinsic { + New, + ReadSync, + WriteSync, + ReadAsync, + WriteAsync, + CancelRead, + CancelWrite, + DropReadable, + DropWritable, +} + +impl AsyncIntrinsic { + pub const ALL: &[AsyncIntrinsic] = &[ + Self::New, + Self::ReadSync, + Self::WriteSync, + Self::ReadAsync, + Self::WriteAsync, + Self::CancelRead, + Self::CancelWrite, + Self::DropReadable, + Self::DropWritable, + ]; +} + +impl AsyncIntrinsic { + pub fn signature(&self, is_future: bool) -> WasmSignature { + let sig = |params: &[WasmType], results: &[WasmType]| WasmSignature { + params: params.to_vec(), + results: results.to_vec(), + indirect_params: false, + retptr: false, + }; + let op_params = if is_future { + &[WasmType::I32; 2][..] + } else { + &[WasmType::I32; 3][..] + }; + match self { + Self::New => sig(&[], &[WasmType::I64]), + Self::ReadSync | Self::WriteSync | Self::ReadAsync | Self::WriteAsync => { + sig(op_params, &[WasmType::I32]) + } + Self::CancelRead | Self::CancelWrite => sig(&[WasmType::I32], &[WasmType::I32]), + Self::DropReadable | Self::DropWritable => sig(&[WasmType::I32], &[]), + } + } +} + /// Indicates whether a function export is a normal export, a post-return /// function, or a callback function. #[derive(Debug)]