Skip to content

Commit

Permalink
add Resource::take method to wit_bindgen::rt (#753)
Browse files Browse the repository at this point in the history
* add `Resource::take` method to `wit_bindgen::rt`

This allows the guest to take back ownership of an exported resource from the
host; you can think of it as the reverse of `Resource::new`.  It's a bit awkward
to do this for the time being;
WebAssembly/component-model#238 will improve the
situation if accepted.

Signed-off-by: Joel Dice <[email protected]>

* address review feedback

- add `type RawRep<T> = Option<T>` alias
- rename `Resource::take` to `Resource::into_inner`
- add `Resource::lift_borrow` and use it in code generator

Signed-off-by: Joel Dice <[email protected]>

---------

Signed-off-by: Joel Dice <[email protected]>
  • Loading branch information
dicej authored Nov 13, 2023
1 parent f30e6ac commit 5f90361
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 5 deletions.
29 changes: 25 additions & 4 deletions crates/guest-rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ pub mod rt {
}
}

type RawRep<T> = Option<T>;

/// A type which represents a component model resource, either imported or
/// exported into this component.
///
Expand Down Expand Up @@ -238,7 +240,7 @@ impl<T: WasmResource> Resource<T> {
where
T: RustResource,
{
let rep = Box::into_raw(Box::new(val)) as usize;
let rep = Box::into_raw(Box::new(Some(val))) as usize;
unsafe {
let handle = T::new(rep);
Resource::from_handle(handle)
Expand All @@ -250,7 +252,26 @@ impl<T: WasmResource> Resource<T> {
where
T: RustResource,
{
let _ = Box::from_raw(rep as *mut T);
let _ = Box::from_raw(rep as *mut RawRep<T>);
}

/// Takes back ownership of the object, dropping the resource handle.
pub fn into_inner(resource: Self) -> T
where
T: RustResource,
{
unsafe {
let rep = T::rep(resource.handle);
RawRep::take(&mut *(rep as *mut RawRep<T>)).unwrap()
}
}

#[doc(hidden)]
pub unsafe fn lift_borrow<'a>(rep: usize) -> &'a T
where
T: RustResource,
{
RawRep::as_ref(&*(rep as *const RawRep<T>)).unwrap()
}
}

Expand All @@ -260,7 +281,7 @@ impl<T: RustResource> Deref for Resource<T> {
fn deref(&self) -> &T {
unsafe {
let rep = T::rep(self.handle);
&*(rep as *const T)
RawRep::as_ref(&*(rep as *const RawRep<T>)).unwrap()
}
}
}
Expand All @@ -269,7 +290,7 @@ impl<T: RustResource> DerefMut for Resource<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe {
let rep = T::rep(self.handle);
&mut *(rep as *mut T)
RawRep::as_mut(&mut *(rep as *mut RawRep<T>)).unwrap()
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion crates/rust/src/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,10 @@ impl Bindgen for FunctionBindgen<'_, '_> {
.as_deref()
.unwrap()
.to_upper_camel_case();
format!("&*({op} as u32 as usize as *const {name})")
let rt = self.gen.gen.runtime_path();
format!(
"{rt}::Resource::<{name}>::lift_borrow({op} as u32 as usize)"
)
}
Handle::Own(_) => {
let name = self.gen.type_path(resource, true);
Expand Down
3 changes: 3 additions & 0 deletions crates/test-rust-wasm/src/bin/resource_into_inner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include!("../../../../tests/runtime/resource_into_inner/wasm.rs");

fn main() {}
1 change: 1 addition & 0 deletions tests/runtime/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod resource_borrow_import;
mod resource_borrow_in_record;
mod resource_floats;
mod resource_import_and_export;
mod resource_into_inner;
mod resource_with_lists;
mod resources;
mod smoke;
Expand Down
22 changes: 22 additions & 0 deletions tests/runtime/resource_into_inner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use wasmtime::Store;

wasmtime::component::bindgen!(in "tests/runtime/resource_into_inner");

use exports::test::resource_into_inner::test::Test;

#[test]
fn run() -> anyhow::Result<()> {
crate::run_test(
"resource_into_inner",
|_| Ok(()),
|store, component, linker| {
let (u, e) = ResourceIntoInner::instantiate(store, component, linker)?;
Ok((u.interface0, e))
},
run_test,
)
}

fn run_test(instance: Test, store: &mut Store<crate::Wasi<()>>) -> anyhow::Result<()> {
instance.call_test(&mut *store)
}
31 changes: 31 additions & 0 deletions tests/runtime/resource_into_inner/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
wit_bindgen::generate!({
path: "../../tests/runtime/resource_into_inner",
exports: {
world: Test,
"test:resource-into-inner/test": Test,
"test:resource-into-inner/test/thing": MyThing,
},
});

use exports::test::resource_into_inner::test::{Guest, GuestThing};
use wit_bindgen::rt::Resource;

pub struct Test;

impl Guest for Test {
fn test() {
let text = "Jabberwocky";
assert_eq!(
text,
&Resource::into_inner(Resource::new(MyThing(text.to_string()))).0
);
}
}

pub struct MyThing(String);

impl GuestThing for MyThing {
fn new(text: String) -> Self {
Self(text)
}
}
13 changes: 13 additions & 0 deletions tests/runtime/resource_into_inner/world.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package test:resource-into-inner;

interface test {
resource thing {
constructor(text: string);
}

test: func();
}

world resource-into-inner {
export test;
}

0 comments on commit 5f90361

Please sign in to comment.