diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index 2a9a92b98125..b81b75295241 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -47,7 +47,7 @@ pub trait WasiHttpView: Send { std::time::Duration::from_millis(600 * 1000), ); let incoming_req = HostIncomingRequest::new(self, parts, Some(body)); - Ok(self.table().push(incoming_req)?) + Ok(self.table().push_host(incoming_req)?) } fn new_response_outparam( @@ -56,7 +56,7 @@ pub trait WasiHttpView: Send { Result, types::ErrorCode>, >, ) -> wasmtime::Result> { - let id = self.table().push(HostResponseOutparam { result })?; + let id = self.table().push_host(HostResponseOutparam { result })?; Ok(id) } @@ -134,7 +134,9 @@ pub fn default_send_request( Ok(resp) }); - let fut = view.table().push(HostFutureIncomingResponse::new(handle))?; + let fut = view + .table() + .push_host(HostFutureIncomingResponse::new(handle))?; Ok(fut) } diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index 351f8f59d775..63aad756a4f6 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -93,7 +93,7 @@ impl crate::bindings::http::types::HostFields for T { fn new(&mut self) -> wasmtime::Result> { let id = self .table() - .push(HostFields::Owned { + .push_host(HostFields::Owned { fields: hyper::HeaderMap::new(), }) .context("[new_fields] pushing fields")?; @@ -127,7 +127,7 @@ impl crate::bindings::http::types::HostFields for T { let id = self .table() - .push(HostFields::Owned { fields }) + .push_host(HostFields::Owned { fields }) .context("[new_fields] pushing fields")?; Ok(Ok(id)) @@ -269,7 +269,7 @@ impl crate::bindings::http::types::HostFields for T { let id = self .table() - .push(HostFields::Owned { fields }) + .push_host(HostFields::Owned { fields }) .context("[fields_clone] pushing fields")?; Ok(id) @@ -329,7 +329,7 @@ impl crate::bindings::http::types::HostIncomingRequest for T { .headers } - let headers = self.table().push_child( + let headers = self.table().push_host_child( HostFields::Ref { parent: id.rep(), get_fields, @@ -347,7 +347,7 @@ impl crate::bindings::http::types::HostIncomingRequest for T { let req = self.table().get_mut(&id)?; match req.body.take() { Some(body) => { - let id = self.table().push(body)?; + let id = self.table().push_host(body)?; Ok(Ok(id)) } @@ -369,7 +369,7 @@ impl crate::bindings::http::types::HostOutgoingRequest for T { let headers = move_fields(self.table(), headers)?; self.table() - .push(HostOutgoingRequest { + .push_host(HostOutgoingRequest { path_with_query: None, authority: None, method: types::Method::Get, @@ -404,7 +404,7 @@ impl crate::bindings::http::types::HostOutgoingRequest for T { // The output stream will necessarily outlive the request, because we could be still // writing to the stream after `outgoing-handler.handle` is called. - let outgoing_body = self.table().push(host_body)?; + let outgoing_body = self.table().push_host(host_body)?; Ok(Ok(outgoing_body)) } @@ -535,7 +535,7 @@ impl crate::bindings::http::types::HostOutgoingRequest for T { .headers } - let id = self.table().push_child( + let id = self.table().push_host_child( HostFields::Ref { parent: request.rep(), get_fields, @@ -600,7 +600,7 @@ impl crate::bindings::http::types::HostIncomingResponse for T { &mut elem.downcast_mut::().unwrap().headers } - let id = self.table().push_child( + let id = self.table().push_host_child( HostFields::Ref { parent: response.rep(), get_fields, @@ -622,7 +622,7 @@ impl crate::bindings::http::types::HostIncomingResponse for T { match r.body.take() { Some(body) => { - let id = self.table().push(body)?; + let id = self.table().push_host(body)?; Ok(Ok(id)) } @@ -672,7 +672,7 @@ impl crate::bindings::http::types::HostFutureTrailers for T { remove_forbidden_headers(self, &mut fields); - let ts = self.table().push(HostFields::Owned { fields })?; + let ts = self.table().push_host(HostFields::Owned { fields })?; Ok(Some(Ok(Ok(Some(ts))))) } @@ -687,7 +687,7 @@ impl crate::bindings::http::types::HostIncomingBody for T { if let Some(stream) = body.take_stream() { let stream = InputStream::Host(Box::new(stream)); - let stream = self.table().push_child(stream, &id)?; + let stream = self.table().push_host_child(stream, &id)?; return Ok(Ok(stream)); } @@ -699,7 +699,7 @@ impl crate::bindings::http::types::HostIncomingBody for T { id: Resource, ) -> wasmtime::Result> { let body = self.table().delete(id)?; - let trailers = self.table().push(body.into_future_trailers())?; + let trailers = self.table().push_host(body.into_future_trailers())?; Ok(trailers) } @@ -716,7 +716,7 @@ impl crate::bindings::http::types::HostOutgoingResponse for T { ) -> wasmtime::Result> { let fields = move_fields(self.table(), headers)?; - let id = self.table().push(HostOutgoingResponse { + let id = self.table().push_host(HostOutgoingResponse { status: http::StatusCode::OK, headers: fields, body: None, @@ -744,7 +744,7 @@ impl crate::bindings::http::types::HostOutgoingResponse for T { resp.body.replace(body); - let id = self.table().push(host)?; + let id = self.table().push_host(host)?; Ok(Ok(id)) } @@ -783,7 +783,7 @@ impl crate::bindings::http::types::HostOutgoingResponse for T { &mut resp.headers } - Ok(self.table().push_child( + Ok(self.table().push_host_child( HostFields::Ref { parent: id.rep(), get_fields, @@ -834,7 +834,7 @@ impl crate::bindings::http::types::HostFutureIncomingResponse f remove_forbidden_headers(self, &mut parts.headers); - let resp = self.table().push(HostIncomingResponse { + let resp = self.table().push_host(HostIncomingResponse { status: parts.status.as_u16(), headers: parts.headers, body: Some({ @@ -863,7 +863,7 @@ impl crate::bindings::http::types::HostOutgoingBody for T { ) -> wasmtime::Result, ()>> { let body = self.table().get_mut(&id)?; if let Some(stream) = body.body_output_stream.take() { - let id = self.table().push_child(stream, &id)?; + let id = self.table().push_host_child(stream, &id)?; Ok(Ok(id)) } else { Ok(Err(())) @@ -894,7 +894,7 @@ impl crate::bindings::http::types::HostOutgoingBody for T { impl crate::bindings::http::types::HostRequestOptions for T { fn new(&mut self) -> wasmtime::Result> { - let id = self.table().push(types::RequestOptions::default())?; + let id = self.table().push_host(types::RequestOptions::default())?; Ok(id) } diff --git a/crates/wasi-http/tests/all/main.rs b/crates/wasi-http/tests/all/main.rs index f078a30dece4..9f4e5b55430a 100644 --- a/crates/wasi-http/tests/all/main.rs +++ b/crates/wasi-http/tests/all/main.rs @@ -279,7 +279,7 @@ async fn do_wasi_http_hash_all(override_send_request: bool) -> Result<()> { }); Ok(view .table() - .push(HostFutureIncomingResponse::Ready(response))?) + .push_host(HostFutureIncomingResponse::Ready(response))?) }, ) as RequestSender) } else { diff --git a/crates/wasi/src/preview2/host/clocks.rs b/crates/wasi/src/preview2/host/clocks.rs index 2f16b9e10490..a6aca0f95c8a 100644 --- a/crates/wasi/src/preview2/host/clocks.rs +++ b/crates/wasi/src/preview2/host/clocks.rs @@ -47,16 +47,16 @@ fn subscribe_to_duration( duration: tokio::time::Duration, ) -> anyhow::Result> { let sleep = if duration.is_zero() { - table.push(Deadline::Past)? + table.push_host(Deadline::Past)? } else if let Some(deadline) = tokio::time::Instant::now().checked_add(duration) { // NB: this resource created here is not actually exposed to wasm, it's // only an internal implementation detail used to match the signature // expected by `subscribe`. - table.push(Deadline::Instant(deadline))? + table.push_host(Deadline::Instant(deadline))? } else { // If the user specifies a time so far in the future we can't // represent it, wait forever rather than trap. - table.push(Deadline::Never)? + table.push_host(Deadline::Never)? }; subscribe(table, sleep) } diff --git a/crates/wasi/src/preview2/host/filesystem.rs b/crates/wasi/src/preview2/host/filesystem.rs index 4a70efee51e3..bcbadb3c2a06 100644 --- a/crates/wasi/src/preview2/host/filesystem.rs +++ b/crates/wasi/src/preview2/host/filesystem.rs @@ -20,7 +20,7 @@ impl preopens::Host for T { for (dir, name) in self.ctx().preopens.clone() { let fd = self .table_mut() - .push(Descriptor::Dir(dir)) + .push_host(Descriptor::Dir(dir)) .with_context(|| format!("failed to push preopen {name}"))?; results.push((fd, name)); } @@ -333,7 +333,7 @@ impl HostDescriptor for T { Err(ReaddirError::Io(e)) => Err(e.into()), Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()), }); - Ok(table.push(ReaddirIterator::new(entries))?) + Ok(table.push_host(ReaddirIterator::new(entries))?) } async fn sync(&mut self, fd: Resource) -> FsResult<()> { @@ -583,10 +583,10 @@ impl HostDescriptor for T { match opened { OpenResult::Dir(dir) => { - Ok(table.push(Descriptor::Dir(Dir::new(dir, d.perms, d.file_perms)))?) + Ok(table.push_host(Descriptor::Dir(Dir::new(dir, d.perms, d.file_perms)))?) } - OpenResult::File(file) => Ok(table.push(Descriptor::File(File::new( + OpenResult::File(file) => Ok(table.push_host(Descriptor::File(File::new( file, mask_file_perms(d.file_perms, flags), )))?), @@ -713,7 +713,7 @@ impl HostDescriptor for T { let reader = FileInputStream::new(clone, offset); // Insert the stream view into the table. Trap if the table is full. - let index = self.table_mut().push(InputStream::File(reader))?; + let index = self.table_mut().push_host(InputStream::File(reader))?; Ok(index) } @@ -738,7 +738,7 @@ impl HostDescriptor for T { let writer: OutputStream = Box::new(writer); // Insert the stream view into the table. Trap if the table is full. - let index = self.table_mut().push(writer)?; + let index = self.table_mut().push_host(writer)?; Ok(index) } @@ -761,7 +761,7 @@ impl HostDescriptor for T { let appender: OutputStream = Box::new(appender); // Insert the stream view into the table. Trap if the table is full. - let index = self.table_mut().push(appender)?; + let index = self.table_mut().push_host(appender)?; Ok(index) } @@ -1061,7 +1061,7 @@ mod test { fn table_readdir_works() { let mut table = ResourceTable::new(); let ix = table - .push(ReaddirIterator::new(std::iter::empty())) + .push_host(ReaddirIterator::new(std::iter::empty())) .unwrap(); let _ = table.get(&ix).unwrap(); table.delete(ix).unwrap(); diff --git a/crates/wasi/src/preview2/host/instance_network.rs b/crates/wasi/src/preview2/host/instance_network.rs index 382bae913c0e..907287b61410 100644 --- a/crates/wasi/src/preview2/host/instance_network.rs +++ b/crates/wasi/src/preview2/host/instance_network.rs @@ -9,7 +9,7 @@ impl instance_network::Host for T { socket_addr_check: self.ctx().socket_addr_check.clone(), allow_ip_name_lookup: self.ctx().allowed_network_uses.ip_name_lookup, }; - let network = self.table_mut().push(network)?; + let network = self.table_mut().push_host(network)?; Ok(network) } } diff --git a/crates/wasi/src/preview2/host/io.rs b/crates/wasi/src/preview2/host/io.rs index d04747fc6ddd..9a431d7c4133 100644 --- a/crates/wasi/src/preview2/host/io.rs +++ b/crates/wasi/src/preview2/host/io.rs @@ -13,7 +13,7 @@ impl streams::Host for T { match err { StreamError::Closed => Ok(streams::StreamError::Closed), StreamError::LastOperationFailed(e) => Ok(streams::StreamError::LastOperationFailed( - self.table_mut().push(e)?, + self.table_mut().push_host(e)?, )), StreamError::Trap(e) => Err(e), } @@ -303,13 +303,13 @@ pub mod sync { fn flush(&mut self, stream: Resource) -> StreamResult<()> { Ok(AsyncHostOutputStream::flush( self, - Resource::new_borrow(stream.rep()), + Resource::new_host_borrow(stream.rep()), )?) } fn blocking_flush(&mut self, stream: Resource) -> StreamResult<()> { in_tokio(async { - AsyncHostOutputStream::blocking_flush(self, Resource::new_borrow(stream.rep())) + AsyncHostOutputStream::blocking_flush(self, Resource::new_host_borrow(stream.rep())) .await }) } diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index b1d4ba31f28c..b121c288fabf 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -184,8 +184,8 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { socket.tcp_state = TcpState::Connected; let (input, output) = socket.as_split(); - let input_stream = self.table_mut().push_child(input, &this)?; - let output_stream = self.table_mut().push_child(output, &this)?; + let input_stream = self.table_mut().push_host_child(input, &this)?; + let output_stream = self.table_mut().push_host_child(output, &this)?; Ok((input_stream, output_stream)) } @@ -288,9 +288,9 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { let (input, output) = tcp_socket.as_split(); let output: OutputStream = output; - let tcp_socket = self.table_mut().push(tcp_socket)?; - let input_stream = self.table_mut().push_child(input, &tcp_socket)?; - let output_stream = self.table_mut().push_child(output, &tcp_socket)?; + let tcp_socket = self.table_mut().push_host(tcp_socket)?; + let input_stream = self.table_mut().push_host_child(input, &tcp_socket)?; + let output_stream = self.table_mut().push_host_child(output, &tcp_socket)?; Ok((tcp_socket, input_stream, output_stream)) } diff --git a/crates/wasi/src/preview2/host/tcp_create_socket.rs b/crates/wasi/src/preview2/host/tcp_create_socket.rs index 3018ef709f8a..bb2eed4c3f03 100644 --- a/crates/wasi/src/preview2/host/tcp_create_socket.rs +++ b/crates/wasi/src/preview2/host/tcp_create_socket.rs @@ -9,7 +9,7 @@ impl tcp_create_socket::Host for T { address_family: IpAddressFamily, ) -> SocketResult> { let socket = TcpSocket::new(address_family.into())?; - let socket = self.table_mut().push(socket)?; + let socket = self.table_mut().push_host(socket)?; Ok(socket) } } diff --git a/crates/wasi/src/preview2/host/udp.rs b/crates/wasi/src/preview2/host/udp.rs index 09ca8b5b9875..8f564ba0eefa 100644 --- a/crates/wasi/src/preview2/host/udp.rs +++ b/crates/wasi/src/preview2/host/udp.rs @@ -167,8 +167,8 @@ impl udp::HostUdpSocket for T { }; Ok(( - self.table_mut().push_child(incoming_stream, &this)?, - self.table_mut().push_child(outgoing_stream, &this)?, + self.table_mut().push_host_child(incoming_stream, &this)?, + self.table_mut().push_host_child(outgoing_stream, &this)?, )) } diff --git a/crates/wasi/src/preview2/host/udp_create_socket.rs b/crates/wasi/src/preview2/host/udp_create_socket.rs index 23edbddf949b..92b9410bac4b 100644 --- a/crates/wasi/src/preview2/host/udp_create_socket.rs +++ b/crates/wasi/src/preview2/host/udp_create_socket.rs @@ -9,7 +9,7 @@ impl udp_create_socket::Host for T { address_family: IpAddressFamily, ) -> SocketResult> { let socket = UdpSocket::new(address_family.into())?; - let socket = self.table_mut().push(socket)?; + let socket = self.table_mut().push_host(socket)?; Ok(socket) } } diff --git a/crates/wasi/src/preview2/ip_name_lookup.rs b/crates/wasi/src/preview2/ip_name_lookup.rs index ab69f8f1bf9c..474553aa5c4d 100644 --- a/crates/wasi/src/preview2/ip_name_lookup.rs +++ b/crates/wasi/src/preview2/ip_name_lookup.rs @@ -34,7 +34,9 @@ impl Host for T { } let task = spawn_blocking(move || blocking_resolve(&host)); - let resource = self.table_mut().push(ResolveAddressStream::Waiting(task))?; + let resource = self + .table_mut() + .push_host(ResolveAddressStream::Waiting(task))?; Ok(resource) } } diff --git a/crates/wasi/src/preview2/poll.rs b/crates/wasi/src/preview2/poll.rs index 4fba2a9940e0..ed7f728b3d9a 100644 --- a/crates/wasi/src/preview2/poll.rs +++ b/crates/wasi/src/preview2/poll.rs @@ -50,7 +50,7 @@ where index: resource.rep(), remove_index_on_delete: if resource.owned() { Some(|table, idx| { - let resource = Resource::::new_own(idx); + let resource = Resource::::new_host_own(idx); table.delete(resource)?; Ok(()) }) @@ -60,7 +60,7 @@ where make_future: make_future::, }; - Ok(table.push_child(pollable, &resource)?) + Ok(table.push_host_child(pollable, &resource)?) } #[async_trait::async_trait] diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index 8a0c64bd713c..5bca68504c54 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -2343,6 +2343,6 @@ trait ResourceExt { impl ResourceExt for Resource { fn borrowed(&self) -> Resource { - Resource::new_borrow(self.rep()) + Resource::new_borrow(self.rep(), self.ty()) } } diff --git a/crates/wasi/src/preview2/stdio.rs b/crates/wasi/src/preview2/stdio.rs index f99a5d2a245c..4696d19c0b26 100644 --- a/crates/wasi/src/preview2/stdio.rs +++ b/crates/wasi/src/preview2/stdio.rs @@ -192,21 +192,23 @@ pub enum IsATTY { impl stdin::Host for T { fn get_stdin(&mut self) -> Result, anyhow::Error> { let stream = self.ctx_mut().stdin.stream(); - Ok(self.table_mut().push(streams::InputStream::Host(stream))?) + Ok(self + .table_mut() + .push_host(streams::InputStream::Host(stream))?) } } impl stdout::Host for T { fn get_stdout(&mut self) -> Result, anyhow::Error> { let stream = self.ctx_mut().stdout.stream(); - Ok(self.table_mut().push(stream)?) + Ok(self.table_mut().push_host(stream)?) } } impl stderr::Host for T { fn get_stderr(&mut self) -> Result, anyhow::Error> { let stream = self.ctx_mut().stderr.stream(); - Ok(self.table_mut().push(stream)?) + Ok(self.table_mut().push_host(stream)?) } } @@ -230,7 +232,7 @@ impl terminal_output::HostTerminalOutput for T { impl terminal_stdin::Host for T { fn get_terminal_stdin(&mut self) -> anyhow::Result>> { if self.ctx().stdin.isatty() { - let fd = self.table_mut().push(TerminalInput)?; + let fd = self.table_mut().push_host(TerminalInput)?; Ok(Some(fd)) } else { Ok(None) @@ -240,7 +242,7 @@ impl terminal_stdin::Host for T { impl terminal_stdout::Host for T { fn get_terminal_stdout(&mut self) -> anyhow::Result>> { if self.ctx().stdout.isatty() { - let fd = self.table_mut().push(TerminalOutput)?; + let fd = self.table_mut().push_host(TerminalOutput)?; Ok(Some(fd)) } else { Ok(None) @@ -250,7 +252,7 @@ impl terminal_stdout::Host for T { impl terminal_stderr::Host for T { fn get_terminal_stderr(&mut self) -> anyhow::Result>> { if self.ctx().stderr.isatty() { - let fd = self.table_mut().push(TerminalOutput)?; + let fd = self.table_mut().push_host(TerminalOutput)?; Ok(Some(fd)) } else { Ok(None) diff --git a/crates/wasi/tests/all/api.rs b/crates/wasi/tests/all/api.rs index 413713aa1f65..33cfc4b97cd5 100644 --- a/crates/wasi/tests/all/api.rs +++ b/crates/wasi/tests/all/api.rs @@ -184,7 +184,7 @@ async fn api_reactor() -> Result<()> { // `host` crate for `streams`, not because of `with` in the bindgen macro. let writepipe = preview2::pipe::MemoryOutputPipe::new(4096); let stream: preview2::OutputStream = Box::new(writepipe.clone()); - let table_ix = store.data_mut().table_mut().push(stream)?; + let table_ix = store.data_mut().table_mut().push_host(stream)?; let r = reactor.call_write_strings_to(&mut store, table_ix).await?; assert_eq!(r, Ok(())); diff --git a/crates/wasmtime/src/component/resource_table.rs b/crates/wasmtime/src/component/resource_table.rs index fe5244d36660..54d842e289f9 100644 --- a/crates/wasmtime/src/component/resource_table.rs +++ b/crates/wasmtime/src/component/resource_table.rs @@ -1,4 +1,4 @@ -use super::Resource; +use super::{Resource, ResourceType}; use std::any::Any; use std::collections::{BTreeSet, HashMap}; @@ -111,14 +111,23 @@ impl ResourceTable { } } - /// Inserts a new value `T` into this table, returning a corresponding + /// Inserts a new value `T` of type `ty` into this table, returning a corresponding /// `Resource` which can be used to refer to it after it was inserted. - pub fn push(&mut self, entry: T) -> Result, ResourceTableError> + pub fn push(&mut self, entry: T, ty: ResourceType) -> Result, ResourceTableError> where T: Send + Sync + 'static, { let idx = self.push_(TableEntry::new(Box::new(entry), None))?; - Ok(Resource::new_own(idx)) + Ok(Resource::new_own(idx, ty)) + } + + /// This is like [`Self::push`], but assumes host-constructed resource type. + pub fn push_host(&mut self, entry: T) -> Result, ResourceTableError> + where + T: Send + Sync + 'static, + { + let idx = self.push_(TableEntry::new(Box::new(entry), None))?; + Ok(Resource::new_host_own(idx)) } /// Pop an index off of the free list, if it's not empty. @@ -183,7 +192,7 @@ impl ResourceTable { .ok_or(ResourceTableError::NotPresent) } - /// Insert a resource at the next available index, and track that it has a + /// Insert a resource of type `ty` at the next available index, and track that it has a /// parent resource. /// /// The parent must exist to create a child. All children resources must @@ -204,6 +213,24 @@ impl ResourceTable { /// methods except for erroring on deletion, or the [`std::fmt::Debug`] /// impl. pub fn push_child( + &mut self, + entry: T, + ty: ResourceType, + parent: &Resource, + ) -> Result, ResourceTableError> + where + T: Send + Sync + 'static, + U: 'static, + { + let parent = parent.rep(); + self.occupied(parent)?; + let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?; + self.occupied_mut(parent)?.add_child(child); + Ok(Resource::new_own(child, ty)) + } + + /// This is like [`Self::push_child`], but assumes host-constructed resource types. + pub fn push_host_child( &mut self, entry: T, parent: &Resource, @@ -216,7 +243,7 @@ impl ResourceTable { self.occupied(parent)?; let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?; self.occupied_mut(parent)?.add_child(child); - Ok(Resource::new_own(child)) + Ok(Resource::new_host_own(child)) } /// Get an immutable reference to a resource of a given type at a given @@ -323,28 +350,28 @@ impl Default for ResourceTable { pub fn test_free_list() { let mut table = ResourceTable::new(); - let x = table.push(()).unwrap(); + let x = table.push_host(()).unwrap(); assert_eq!(x.rep(), 0); - let y = table.push(()).unwrap(); + let y = table.push_host(()).unwrap(); assert_eq!(y.rep(), 1); // Deleting x should put it on the free list, so the next entry should have the same rep. table.delete(x).unwrap(); - let x = table.push(()).unwrap(); + let x = table.push_host(()).unwrap(); assert_eq!(x.rep(), 0); // Deleting x and then y should yield indices 1 and then 0 for new entries. table.delete(x).unwrap(); table.delete(y).unwrap(); - let y = table.push(()).unwrap(); + let y = table.push_host(()).unwrap(); assert_eq!(y.rep(), 1); - let x = table.push(()).unwrap(); + let x = table.push_host(()).unwrap(); assert_eq!(x.rep(), 0); // As the free list is empty, this entry will have a new id. - let x = table.push(()).unwrap(); + let x = table.push_host(()).unwrap(); assert_eq!(x.rep(), 2); } diff --git a/crates/wasmtime/src/component/resources.rs b/crates/wasmtime/src/component/resources.rs index 9ed13faf3208..71a0e8da0f3f 100644 --- a/crates/wasmtime/src/component/resources.rs +++ b/crates/wasmtime/src/component/resources.rs @@ -3,7 +3,7 @@ use crate::component::matching::InstanceType; use crate::component::{ComponentType, Lift, Lower}; use crate::store::{StoreId, StoreOpaque}; use crate::{AsContextMut, StoreContextMut, Trap}; -use anyhow::{bail, Result}; +use anyhow::{bail, ensure, Result}; use std::any::TypeId; use std::fmt; use std::marker; @@ -80,7 +80,7 @@ enum ResourceTypeKind { }, } -/// A host-defined resource in the component model. +/// A host-defined resource in the component model of either guest or host-defined type. /// /// This type can be thought of as roughly a newtype wrapper around `u32` for /// use as a resource with the component model. The main guarantee that the @@ -196,6 +196,8 @@ pub struct Resource { /// The host-defined 32-bit representation of this resource. rep: u32, + ty: ResourceType, + /// Dear rust please consider `T` used even though it's not actually used. _marker: marker::PhantomData T>, @@ -249,33 +251,76 @@ impl Resource where T: 'static, { - /// Creates a new owned resource with the `rep` specified. + /// Creates a new owned host-constructed resource with the `rep` and `ty` specified. + /// + /// The returned value is suitable for passing to a guest as either a + /// `(borrow $t)` or `(own $t)`. + pub fn new_own(rep: u32, ty: ResourceType) -> Resource { + Resource { + state: AtomicU32::new(NOT_IN_TABLE), + rep, + ty, + _marker: marker::PhantomData, + } + } + + /// Creates a new owned host-constructed resource with the `rep` specified. /// /// The returned value is suitable for passing to a guest as either a /// `(borrow $t)` or `(own $t)`. - pub fn new_own(rep: u32) -> Resource { + pub fn new_host_own(rep: u32) -> Resource { Resource { state: AtomicU32::new(NOT_IN_TABLE), rep, + ty: ResourceType::host::(), _marker: marker::PhantomData, } } - /// Creates a new borrowed resource which isn't actually rooted in any + /// Creates a new borrowed resource of type `ty` which isn't actually rooted in any /// ownership. /// /// This can be used to pass to a guest as a borrowed resource and the /// embedder will know that the `rep` won't be in use by the guest /// afterwards. Exactly how the lifetime of `rep` works is up to the /// embedder. - pub fn new_borrow(rep: u32) -> Resource { + pub fn new_borrow(rep: u32, ty: ResourceType) -> Resource { Resource { state: AtomicU32::new(BORROW), rep, + ty, _marker: marker::PhantomData, } } + /// Creates a new borrowed host-constructed resource which isn't actually rooted in any + /// ownership. + /// + /// This can be used to pass to a guest as a borrowed resource and the + /// embedder will know that the `rep` won't be in use by the guest + /// afterwards. Exactly how the lifetime of `rep` works is up to the + /// embedder. + pub fn new_host_borrow(rep: u32) -> Resource { + Resource { + state: AtomicU32::new(BORROW), + rep, + ty: ResourceType::host::(), + _marker: marker::PhantomData, + } + } + + /// Returns the corresponding type associated with this resource, either a + /// host-defined type or a guest-defined type. + /// + /// This can be compared against [`ResourceType::host`] for example to see + /// if it's a host-resource or against a type extracted with + /// [`Instance::get_resource`] to see if it's a guest-defined resource. + /// + /// [`Instance::get_resource`]: crate::component::Instance::get_resource + pub fn ty(&self) -> ResourceType { + self.ty + } + /// Returns the underlying 32-bit representation used to originally create /// this resource. pub fn rep(&self) -> u32 { @@ -297,6 +342,7 @@ where fn lower_to_index(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result { match ty { InterfaceType::Own(t) => { + ensure!(cx.resource_type(t) == self.ty, "mismatched resource types"); let rep = match self.state.load(Relaxed) { // If this is a borrow resource then this is a dynamic // error on behalf of the embedder. @@ -326,6 +372,7 @@ where Ok(cx.guest_resource_lower_own(t, rep)) } InterfaceType::Borrow(t) => { + ensure!(cx.resource_type(t) == self.ty, "mismatched resource types"); let rep = match self.state.load(Relaxed) { // If this is already a borrowed resource, nothing else to // do and the rep is passed through. @@ -360,18 +407,21 @@ where } fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result { - let (state, rep) = match ty { + let (state, rep, ty) = match ty { // Ownership is being transferred from a guest to the host, so move // it from the guest table into a new `Resource`. Note that this // isn't immediately inserted into the host table and that's left // for the future if it's necessary to take a borrow from this owned // resource. InterfaceType::Own(t) => { - debug_assert!(cx.resource_type(t) == ResourceType::host::()); + let ty = cx.resource_type(t); + if let ResourceTypeKind::Host(..) = ty.kind { + ensure!(ty == ResourceType::host::(), "mismatched resource types") + } let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?; assert!(dtor.is_some()); assert!(flags.is_none()); - (AtomicU32::new(NOT_IN_TABLE), rep) + (AtomicU32::new(NOT_IN_TABLE), rep, ty) } // The borrow here is lifted from the guest, but note the lack of @@ -382,15 +432,19 @@ where // This effectively mirrors that even though the canonical ABI // isn't really all that applicable in host context here. InterfaceType::Borrow(t) => { - debug_assert!(cx.resource_type(t) == ResourceType::host::()); + let ty = cx.resource_type(t); + if let ResourceTypeKind::Host(..) = ty.kind { + ensure!(ty == ResourceType::host::(), "mismatched resource types") + } let rep = cx.guest_resource_lift_borrow(t, index)?; - (AtomicU32::new(BORROW), rep) + (AtomicU32::new(BORROW), rep, ty) } _ => bad_type_info(), }; Ok(Resource { state, rep, + ty, _marker: marker::PhantomData, }) } @@ -468,9 +522,7 @@ impl fmt::Debug for Resource { /// or a host-defined resource. /// /// This type is similar to [`Resource`] except that it can be used to represent -/// any resource, either host or guest. This type cannot be directly constructed -/// and is only available if the guest returns it to the host (e.g. a function -/// returning a guest-defined resource). This type also does not carry a static +/// any resource, either host or guest. This type also does not carry a static /// type parameter `T` for example and does not have as much information about /// its type. This means that it's possible to get runtime type-errors when /// using this type because it cannot statically prevent mismatching resource @@ -500,6 +552,16 @@ struct OwnState { dtor: Option>, } +impl From> for ResourceAny { + fn from(Resource { rep, ty, .. }: Resource) -> Self { + Self { + idx: rep, + ty, + own_state: None, + } + } +} + impl ResourceAny { /// Returns the corresponding type associated with this resource, either a /// host-defined type or a guest-defined type. @@ -609,16 +671,12 @@ impl ResourceAny { fn lower_to_index(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result { match ty { InterfaceType::Own(t) => { - if cx.resource_type(t) != self.ty { - bail!("mismatched resource types") - } + ensure!(cx.resource_type(t) == self.ty, "mismatched resource types"); let rep = cx.host_resource_lift_own(self.idx)?; Ok(cx.guest_resource_lower_own(t, rep)) } InterfaceType::Borrow(t) => { - if cx.resource_type(t) != self.ty { - bail!("mismatched resource types") - } + ensure!(cx.resource_type(t) == self.ty, "mismatched resource types"); let rep = cx.host_resource_lift_borrow(self.idx)?; Ok(cx.guest_resource_lower_borrow(t, rep)) } diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 5f00ba73ac87..31a845089a8a 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -118,7 +118,7 @@ pub fn link_component_spectest(linker: &mut component::Linker) -> Result<( )?; i.func_wrap("[constructor]resource1", |_cx, (rep,): (u32,)| { - Ok((Resource::::new_own(rep),)) + Ok((Resource::::new_host_own(rep),)) })?; i.func_wrap( "[static]resource1.assert", diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index a73f6bcca4e5..c877915b3c9d 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -811,7 +811,7 @@ impl Wasmtime { \"{name}\", wasmtime::component::ResourceType::host::<{camel}>(), move |mut store, rep| -> wasmtime::Result<()> {{ - Host{camel}::drop(get(store.data_mut()), wasmtime::component::Resource::new_own(rep)) + Host{camel}::drop(get(store.data_mut()), wasmtime::component::Resource::new_own(rep, wasmtime::component::ResourceType::host::<{camel}>())) }}, )?;" ) @@ -1609,7 +1609,7 @@ impl<'a> InterfaceGenerator<'a> { \"{name}\", wasmtime::component::ResourceType::host::<{camel}>(), move |mut store, rep| -> wasmtime::Result<()> {{ - Host{camel}::drop(get(store.data_mut()), wasmtime::component::Resource::new_own(rep)) + Host{camel}::drop(get(store.data_mut()), wasmtime::component::Resource::new_own(rep, wasmtime::component::ResourceType::host::<{camel}>())) }}, )?;" ) diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs index 1c0b713a1c6e..9b0cebb5a4b2 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -183,7 +183,7 @@ mod resources_at_world_level { impl HostX for MyImports { fn new(&mut self) -> Result> { self.ctor_hit = true; - Ok(Resource::new_own(80)) + Ok(Resource::new_host_own(80)) } fn drop(&mut self, val: Resource) -> Result<()> { @@ -203,7 +203,7 @@ mod resources_at_world_level { Resources::add_to_linker(&mut linker, |f: &mut MyImports| f)?; let mut store = Store::new(&engine, MyImports::default()); let (one_import, _) = Resources::instantiate(&mut store, &component, &linker)?; - one_import.call_y(&mut store, Resource::new_own(40))?; + one_import.call_y(&mut store, Resource::new_host_own(40))?; assert!(store.data().ctor_hit); assert_eq!(store.data().drops, 2); Ok(()) @@ -288,7 +288,7 @@ mod resources_at_interface_level { impl foo::foo::def::HostX for MyImports { fn new(&mut self) -> Result> { self.ctor_hit = true; - Ok(Resource::new_own(80)) + Ok(Resource::new_host_own(80)) } fn drop(&mut self, val: Resource) -> Result<()> { @@ -310,7 +310,7 @@ mod resources_at_interface_level { let (one_import, _) = Resources::instantiate(&mut store, &component, &linker)?; one_import .foo_foo_user() - .call_y(&mut store, Resource::new_own(40))?; + .call_y(&mut store, Resource::new_host_own(40))?; assert!(store.data().ctor_hit); assert_eq!(store.data().drops, 2); Ok(()) @@ -472,7 +472,7 @@ mod exported_resources { let rep = self.next_a_x; self.next_a_x += 1; self.hostcalls.push(Hostcall::NewA); - Ok(Resource::new_own(rep)) + Ok(Resource::new_host_own(rep)) } fn drop(&mut self, val: Resource) -> Result<()> { @@ -608,13 +608,21 @@ mod exported_resources { let (i, _) = Resources::instantiate(&mut store, &component, &linker)?; // call the root export `f` twice - let ret = i.call_f(&mut store, Resource::new_own(1), Resource::new_own(2))?; + let ret = i.call_f( + &mut store, + Resource::new_host_own(1), + Resource::new_host_own(2), + )?; assert_eq!(ret.rep(), 2); assert_eq!( mem::take(&mut store.data_mut().hostcalls), [Hostcall::DropRootX(1)] ); - let ret = i.call_f(&mut store, Resource::new_own(3), Resource::new_own(4))?; + let ret = i.call_f( + &mut store, + Resource::new_host_own(3), + Resource::new_host_own(4), + )?; assert_eq!(ret.rep(), 4); assert_eq!( mem::take(&mut store.data_mut().hostcalls), @@ -623,7 +631,9 @@ mod exported_resources { // interact with the `b` export let b = i.b(); - let b_x = b.x().call_constructor(&mut store, Resource::new_own(5))?; + let b_x = b + .x() + .call_constructor(&mut store, Resource::new_host_own(5))?; assert_eq!( mem::take(&mut store.data_mut().hostcalls), [Hostcall::DropAX(5), Hostcall::NewA] diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs index ae3893b12d19..d8181a35b71c 100644 --- a/tests/all/component_model/resources.rs +++ b/tests/all/component_model/resources.rs @@ -377,7 +377,7 @@ fn drop_host_twice() -> Result<()> { let i = linker.instantiate(&mut store, &c)?; let dtor = i.get_typed_func::<(&Resource,), ()>(&mut store, "dtor")?; - let t = Resource::new_own(100); + let t = Resource::new_host_own(100); dtor.call(&mut store, (&t,))?; dtor.post_return(&mut store)?; @@ -452,7 +452,7 @@ fn manually_destroy() -> Result<()> { let t1_pass = i.get_typed_func::<(Resource,), (ResourceAny,)>(&mut store, "t1-pass")?; // Host resources can be destroyed through `resource_drop` - let t1 = Resource::new_own(100); + let t1 = Resource::new_host_own(100); let (t1,) = t1_pass.call(&mut store, (t1,))?; t1_pass.post_return(&mut store)?; assert_eq!(store.data().drops, 0); @@ -568,7 +568,7 @@ fn dynamic_val() -> Result<()> { let b = i.get_func(&mut store, "b").unwrap(); let t2 = i.get_resource(&mut store, "t2").unwrap(); - let t1 = Resource::new_own(100); + let t1 = Resource::new_host_own(100); let (t1,) = a_typed.call(&mut store, (t1,))?; a_typed.post_return(&mut store)?; assert_eq!(t1.ty(), ResourceType::host::()); @@ -684,7 +684,7 @@ fn active_borrows_at_end_of_call() -> Result<()> { let f = i.get_typed_func::<(&Resource,), ()>(&mut store, "f")?; - let resource = Resource::new_own(1); + let resource = Resource::new_host_own(1); f.call(&mut store, (&resource,))?; let err = f.post_return(&mut store).unwrap_err(); assert_eq!( @@ -748,7 +748,7 @@ fn thread_through_borrow() -> Result<()> { let f = i.get_typed_func::<(&Resource,), ()>(&mut store, "f2")?; - let resource = Resource::new_own(100); + let resource = Resource::new_host_own(100); f.call(&mut store, (&resource,))?; f.post_return(&mut store)?; Ok(()) @@ -787,7 +787,7 @@ fn cannot_use_borrow_for_own() -> Result<()> { let f = i.get_typed_func::<(&Resource,), (Resource,)>(&mut store, "f")?; - let resource = Resource::new_own(100); + let resource = Resource::new_host_own(100); let err = f.call(&mut store, (&resource,)).unwrap_err(); assert_eq!(err.to_string(), "cannot lift own resource from a borrow"); Ok(()) @@ -837,7 +837,7 @@ fn passthrough_wrong_type() -> Result<()> { let f = i.get_typed_func::<(&Resource,), ()>(&mut store, "f2")?; - let resource = Resource::new_own(100); + let resource = Resource::new_host_own(100); let err = f.call(&mut store, (&resource,)).unwrap_err(); assert!( format!("{err:?}").contains("cannot lower a `borrow` resource into an `own`"), @@ -876,7 +876,7 @@ fn pass_moved_resource() -> Result<()> { let f = i.get_typed_func::<(&Resource, &Resource), ()>(&mut store, "f")?; - let resource = Resource::new_own(100); + let resource = Resource::new_host_own(100); let err = f.call(&mut store, (&resource, &resource)).unwrap_err(); assert!( format!("{err:?}").contains("host resource already consumed"), @@ -1015,7 +1015,7 @@ fn host_borrow_as_resource_any() -> Result<()> { let f = i.get_typed_func::<(&Resource,), ()>(&mut store, "f2")?; - let resource = Resource::new_own(100); + let resource = Resource::new_host_own(100); f.call(&mut store, (&resource,))?; } @@ -1033,7 +1033,7 @@ fn host_borrow_as_resource_any() -> Result<()> { let f = i.get_typed_func::<(&Resource,), ()>(&mut store, "f2")?; - let resource = Resource::new_own(100); + let resource = Resource::new_host_own(100); let err = f.call(&mut store, (&resource,)).unwrap_err(); assert!( format!("{err:?}").contains("borrow handles still remain at the end of the call"), @@ -1139,7 +1139,7 @@ fn pass_host_borrow_to_guest() -> Result<()> { let i = linker.instantiate(&mut store, &c)?; let take = i.get_typed_func::<(&Resource,), ()>(&mut store, "take")?; - let resource = Resource::new_borrow(100); + let resource = Resource::new_host_borrow(100); take.call(&mut store, (&resource,))?; take.post_return(&mut store)?; @@ -1212,7 +1212,7 @@ fn drop_on_owned_resource() -> Result<()> { .root() .resource("t", ResourceType::host::(), |_, _| Ok(()))?; linker.root().func_wrap("[constructor]t", |_cx, ()| { - Ok((Resource::::new_own(300),)) + Ok((Resource::::new_host_own(300),)) })?; linker .root() @@ -1311,7 +1311,7 @@ fn guest_different_host_same() -> Result<()> { assert_eq!(t1, t2); assert_eq!(t1, ResourceType::host::()); - let resource = Resource::new_own(100); + let resource = Resource::new_host_own(100); f.call(&mut store, (&resource, &resource))?; f.post_return(&mut store)?;