Skip to content

Commit bb021dd

Browse files
committed
Add a first-class way of accessing caller's exports
This commit is a continuation of #1237 and updates the API of `Func` to allow defining host functions which have easy access to a caller's memory in particular. The new APIs look like so: * The `Func::wrap*` family of functions was condensed into one `Func::wrap` function. * The ABI layer of conversions in `WasmTy` were removed * An optional `Caller<'_>` argument can be at the front of all host-defined functions now. The old way the wasi bindings looked up memory has been removed and is now replaced with the `Caller` type. The `Caller` type has a `get_export` method on it which allows looking up a caller's export by name, allowing you to get access to the caller's memory easily, and even during instantiation.
1 parent 6e55c54 commit bb021dd

10 files changed

Lines changed: 503 additions & 465 deletions

File tree

crates/api/src/func.rs

Lines changed: 376 additions & 348 deletions
Large diffs are not rendered by default.

crates/api/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ mod values;
2424
pub use crate::callable::Callable;
2525
pub use crate::externals::*;
2626
pub use crate::frame_info::FrameInfo;
27-
pub use crate::func::{Func, WasmRet, WasmTy};
27+
pub use crate::func::*;
2828
pub use crate::instance::Instance;
2929
pub use crate::module::Module;
3030
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};

crates/api/tests/externals.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn cross_store() -> anyhow::Result<()> {
6262

6363
// ============ Cross-store instantiation ==============
6464

65-
let func = Func::wrap0(&store2, || {});
65+
let func = Func::wrap(&store2, || {});
6666
let ty = GlobalType::new(ValType::I32, Mutability::Const);
6767
let global = Global::new(&store2, ty, Val::I32(0))?;
6868
let ty = MemoryType::new(Limits::new(1, None));
@@ -84,8 +84,8 @@ fn cross_store() -> anyhow::Result<()> {
8484

8585
// ============ Cross-store globals ==============
8686

87-
let store1val = Val::FuncRef(Func::wrap0(&store1, || {}));
88-
let store2val = Val::FuncRef(Func::wrap0(&store2, || {}));
87+
let store1val = Val::FuncRef(Func::wrap(&store1, || {}));
88+
let store2val = Val::FuncRef(Func::wrap(&store2, || {}));
8989

9090
let ty = GlobalType::new(ValType::FuncRef, Mutability::Var);
9191
assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err());

crates/api/tests/func.rs

Lines changed: 102 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
use anyhow::Result;
22
use std::rc::Rc;
33
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
4-
use wasmtime::{Callable, Func, FuncType, Instance, Module, Store, Trap, Val, ValType};
4+
use wasmtime::*;
55

66
#[test]
77
fn func_constructors() {
88
let store = Store::default();
9-
Func::wrap0(&store, || {});
10-
Func::wrap1(&store, |_: i32| {});
11-
Func::wrap2(&store, |_: i32, _: i64| {});
12-
Func::wrap2(&store, |_: f32, _: f64| {});
13-
Func::wrap0(&store, || -> i32 { 0 });
14-
Func::wrap0(&store, || -> i64 { 0 });
15-
Func::wrap0(&store, || -> f32 { 0.0 });
16-
Func::wrap0(&store, || -> f64 { 0.0 });
17-
18-
Func::wrap0(&store, || -> Result<(), Trap> { loop {} });
19-
Func::wrap0(&store, || -> Result<i32, Trap> { loop {} });
20-
Func::wrap0(&store, || -> Result<i64, Trap> { loop {} });
21-
Func::wrap0(&store, || -> Result<f32, Trap> { loop {} });
22-
Func::wrap0(&store, || -> Result<f64, Trap> { loop {} });
9+
Func::wrap(&store, || {});
10+
Func::wrap(&store, |_: i32| {});
11+
Func::wrap(&store, |_: i32, _: i64| {});
12+
Func::wrap(&store, |_: f32, _: f64| {});
13+
Func::wrap(&store, || -> i32 { 0 });
14+
Func::wrap(&store, || -> i64 { 0 });
15+
Func::wrap(&store, || -> f32 { 0.0 });
16+
Func::wrap(&store, || -> f64 { 0.0 });
17+
18+
Func::wrap(&store, || -> Result<(), Trap> { loop {} });
19+
Func::wrap(&store, || -> Result<i32, Trap> { loop {} });
20+
Func::wrap(&store, || -> Result<i64, Trap> { loop {} });
21+
Func::wrap(&store, || -> Result<f32, Trap> { loop {} });
22+
Func::wrap(&store, || -> Result<f64, Trap> { loop {} });
2323
}
2424

2525
#[test]
@@ -37,7 +37,7 @@ fn dtor_runs() {
3737
let store = Store::default();
3838
let a = A;
3939
assert_eq!(HITS.load(SeqCst), 0);
40-
Func::wrap0(&store, move || {
40+
Func::wrap(&store, move || {
4141
drop(&a);
4242
});
4343
assert_eq!(HITS.load(SeqCst), 1);
@@ -57,7 +57,7 @@ fn dtor_delayed() -> Result<()> {
5757

5858
let store = Store::default();
5959
let a = A;
60-
let func = Func::wrap0(&store, move || drop(&a));
60+
let func = Func::wrap(&store, move || drop(&a));
6161

6262
assert_eq!(HITS.load(SeqCst), 0);
6363
let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
@@ -73,27 +73,27 @@ fn dtor_delayed() -> Result<()> {
7373
fn signatures_match() {
7474
let store = Store::default();
7575

76-
let f = Func::wrap0(&store, || {});
76+
let f = Func::wrap(&store, || {});
7777
assert_eq!(f.ty().params(), &[]);
7878
assert_eq!(f.ty().results(), &[]);
7979

80-
let f = Func::wrap0(&store, || -> i32 { loop {} });
80+
let f = Func::wrap(&store, || -> i32 { loop {} });
8181
assert_eq!(f.ty().params(), &[]);
8282
assert_eq!(f.ty().results(), &[ValType::I32]);
8383

84-
let f = Func::wrap0(&store, || -> i64 { loop {} });
84+
let f = Func::wrap(&store, || -> i64 { loop {} });
8585
assert_eq!(f.ty().params(), &[]);
8686
assert_eq!(f.ty().results(), &[ValType::I64]);
8787

88-
let f = Func::wrap0(&store, || -> f32 { loop {} });
88+
let f = Func::wrap(&store, || -> f32 { loop {} });
8989
assert_eq!(f.ty().params(), &[]);
9090
assert_eq!(f.ty().results(), &[ValType::F32]);
9191

92-
let f = Func::wrap0(&store, || -> f64 { loop {} });
92+
let f = Func::wrap(&store, || -> f64 { loop {} });
9393
assert_eq!(f.ty().params(), &[]);
9494
assert_eq!(f.ty().results(), &[ValType::F64]);
9595

96-
let f = Func::wrap5(&store, |_: f32, _: f64, _: i32, _: i64, _: i32| -> f64 {
96+
let f = Func::wrap(&store, |_: f32, _: f64, _: i32, _: i64, _: i32| -> f64 {
9797
loop {}
9898
});
9999
assert_eq!(
@@ -144,23 +144,23 @@ fn import_works() -> Result<()> {
144144
Instance::new(
145145
&module,
146146
&[
147-
Func::wrap0(&store, || {
147+
Func::wrap(&store, || {
148148
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
149149
})
150150
.into(),
151-
Func::wrap1(&store, |x: i32| -> i32 {
151+
Func::wrap(&store, |x: i32| -> i32 {
152152
assert_eq!(x, 0);
153153
assert_eq!(HITS.fetch_add(1, SeqCst), 1);
154154
1
155155
})
156156
.into(),
157-
Func::wrap2(&store, |x: i32, y: i64| {
157+
Func::wrap(&store, |x: i32, y: i64| {
158158
assert_eq!(x, 2);
159159
assert_eq!(y, 3);
160160
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
161161
})
162162
.into(),
163-
Func::wrap5(&store, |a: i32, b: i64, c: i32, d: f32, e: f64| {
163+
Func::wrap(&store, |a: i32, b: i64, c: i32, d: f32, e: f64| {
164164
assert_eq!(a, 100);
165165
assert_eq!(b, 200);
166166
assert_eq!(c, 300);
@@ -177,7 +177,7 @@ fn import_works() -> Result<()> {
177177
#[test]
178178
fn trap_smoke() {
179179
let store = Store::default();
180-
let f = Func::wrap0(&store, || -> Result<(), Trap> { Err(Trap::new("test")) });
180+
let f = Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("test")) });
181181
let err = f.call(&[]).unwrap_err();
182182
assert_eq!(err.message(), "test");
183183
}
@@ -194,7 +194,7 @@ fn trap_import() -> Result<()> {
194194
let module = Module::new(&store, &wasm)?;
195195
let trap = Instance::new(
196196
&module,
197-
&[Func::wrap0(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
197+
&[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
198198
)
199199
.err()
200200
.unwrap()
@@ -206,7 +206,7 @@ fn trap_import() -> Result<()> {
206206
#[test]
207207
fn get_from_wrapper() {
208208
let store = Store::default();
209-
let f = Func::wrap0(&store, || {});
209+
let f = Func::wrap(&store, || {});
210210
assert!(f.get0::<()>().is_ok());
211211
assert!(f.get0::<i32>().is_err());
212212
assert!(f.get1::<(), ()>().is_ok());
@@ -216,23 +216,23 @@ fn get_from_wrapper() {
216216
assert!(f.get2::<i32, i32, ()>().is_err());
217217
assert!(f.get2::<i32, i32, i32>().is_err());
218218

219-
let f = Func::wrap0(&store, || -> i32 { loop {} });
219+
let f = Func::wrap(&store, || -> i32 { loop {} });
220220
assert!(f.get0::<i32>().is_ok());
221-
let f = Func::wrap0(&store, || -> f32 { loop {} });
221+
let f = Func::wrap(&store, || -> f32 { loop {} });
222222
assert!(f.get0::<f32>().is_ok());
223-
let f = Func::wrap0(&store, || -> f64 { loop {} });
223+
let f = Func::wrap(&store, || -> f64 { loop {} });
224224
assert!(f.get0::<f64>().is_ok());
225225

226-
let f = Func::wrap1(&store, |_: i32| {});
226+
let f = Func::wrap(&store, |_: i32| {});
227227
assert!(f.get1::<i32, ()>().is_ok());
228228
assert!(f.get1::<i64, ()>().is_err());
229229
assert!(f.get1::<f32, ()>().is_err());
230230
assert!(f.get1::<f64, ()>().is_err());
231-
let f = Func::wrap1(&store, |_: i64| {});
231+
let f = Func::wrap(&store, |_: i64| {});
232232
assert!(f.get1::<i64, ()>().is_ok());
233-
let f = Func::wrap1(&store, |_: f32| {});
233+
let f = Func::wrap(&store, |_: f32| {});
234234
assert!(f.get1::<f32, ()>().is_ok());
235-
let f = Func::wrap1(&store, |_: f64| {});
235+
let f = Func::wrap(&store, |_: f64| {});
236236
assert!(f.get1::<f64, ()>().is_ok());
237237
}
238238

@@ -289,3 +289,68 @@ fn get_from_module() -> anyhow::Result<()> {
289289
assert!(f2.get1::<i32, f32>().is_err());
290290
Ok(())
291291
}
292+
293+
#[test]
294+
fn caller_memory() -> anyhow::Result<()> {
295+
let store = Store::default();
296+
let f = Func::wrap(&store, |c: Caller<'_>| {
297+
assert!(c.get_export("x").is_none());
298+
assert!(c.get_export("y").is_none());
299+
assert!(c.get_export("z").is_none());
300+
});
301+
f.call(&[])?;
302+
303+
let f = Func::wrap(&store, |c: Caller<'_>| {
304+
assert!(c.get_export("x").is_none());
305+
});
306+
let module = Module::new(
307+
&store,
308+
r#"
309+
(module
310+
(import "" "" (func $f))
311+
(start $f)
312+
)
313+
314+
"#,
315+
)?;
316+
Instance::new(&module, &[f.into()])?;
317+
318+
let f = Func::wrap(&store, |c: Caller<'_>| {
319+
assert!(c.get_export("memory").is_some());
320+
});
321+
let module = Module::new(
322+
&store,
323+
r#"
324+
(module
325+
(import "" "" (func $f))
326+
(memory (export "memory") 1)
327+
(start $f)
328+
)
329+
330+
"#,
331+
)?;
332+
Instance::new(&module, &[f.into()])?;
333+
334+
let f = Func::wrap(&store, |c: Caller<'_>| {
335+
assert!(c.get_export("m").is_some());
336+
assert!(c.get_export("f").is_none());
337+
assert!(c.get_export("g").is_none());
338+
assert!(c.get_export("t").is_none());
339+
});
340+
let module = Module::new(
341+
&store,
342+
r#"
343+
(module
344+
(import "" "" (func $f))
345+
(memory (export "m") 1)
346+
(func (export "f"))
347+
(global (export "g") i32 (i32.const 0))
348+
(table (export "t") 1 funcref)
349+
(start $f)
350+
)
351+
352+
"#,
353+
)?;
354+
Instance::new(&module, &[f.into()])?;
355+
Ok(())
356+
}

crates/api/tests/traps.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ fn rust_panic_import() -> Result<()> {
278278
&module,
279279
&[
280280
func.into(),
281-
Func::wrap0(&store, || panic!("this is another panic")).into(),
281+
Func::wrap(&store, || panic!("this is another panic")).into(),
282282
],
283283
)?;
284284
let func = instance.exports()[0].func().unwrap().clone();
@@ -329,7 +329,7 @@ fn rust_panic_start_function() -> Result<()> {
329329
.unwrap_err();
330330
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
331331

332-
let func = Func::wrap0(&store, || panic!("this is another panic"));
332+
let func = Func::wrap(&store, || panic!("this is another panic"));
333333
let err = panic::catch_unwind(AssertUnwindSafe(|| {
334334
drop(Instance::new(&module, &[func.into()]));
335335
}))

crates/wasi-common/wig/src/wasi.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,24 +182,26 @@ pub fn define_struct(args: TokenStream) -> TokenStream {
182182
}
183183

184184
let format_str = format!("{}({})", name, formats.join(", "));
185-
let wrap = format_ident!("wrap{}", shim_arg_decls.len() + 1);
186185
ctor_externs.push(quote! {
187186
let my_cx = cx.clone();
188-
let #name_ident = wasmtime::Func::#wrap(
187+
let #name_ident = wasmtime::Func::wrap(
189188
store,
190-
move |mem: crate::WasiCallerMemory #(,#shim_arg_decls)*| -> #ret_ty {
189+
move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty {
191190
log::trace!(
192191
#format_str,
193192
#(#format_args),*
194193
);
195194
unsafe {
196-
let memory = match mem.get() {
197-
Ok(e) => e,
198-
Err(e) => #handle_early_error,
195+
let memory = match caller.get_export("memory") {
196+
Some(wasmtime::Extern::Memory(m)) => m,
197+
_ => {
198+
let e = wasi_common::wasi::__WASI_ERRNO_INVAL;
199+
#handle_early_error
200+
}
199201
};
200202
hostcalls::#name_ident(
201203
&mut my_cx.borrow_mut(),
202-
memory,
204+
memory.data_unchecked_mut(),
203205
#(#hostcall_args),*
204206
) #cvt_ret
205207
}

crates/wasi/src/lib.rs

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,60 +16,3 @@ pub fn is_wasi_module(name: &str) -> bool {
1616
// trick.
1717
name.starts_with("wasi")
1818
}
19-
20-
/// This is an internal structure used to acquire a handle on the caller's
21-
/// wasm memory buffer.
22-
///
23-
/// This exploits how we can implement `WasmTy` for ourselves locally even
24-
/// though crates in general should not be doing that. This is a crate in
25-
/// the wasmtime project, however, so we should be able to keep up with our own
26-
/// changes.
27-
///
28-
/// In general this type is wildly unsafe. We need to update the wasi crates to
29-
/// probably work with more `wasmtime`-like APIs to grip with the unsafety
30-
/// around dealing with caller memory.
31-
struct WasiCallerMemory {
32-
base: *mut u8,
33-
len: usize,
34-
}
35-
36-
impl wasmtime::WasmTy for WasiCallerMemory {
37-
type Abi = ();
38-
39-
fn push(_dst: &mut Vec<wasmtime::ValType>) {}
40-
41-
fn matches(_tys: impl Iterator<Item = wasmtime::ValType>) -> anyhow::Result<()> {
42-
Ok(())
43-
}
44-
45-
fn from_abi(vmctx: *mut wasmtime_runtime::VMContext, _abi: ()) -> Self {
46-
unsafe {
47-
match wasmtime_runtime::InstanceHandle::from_vmctx(vmctx).lookup("memory") {
48-
Some(wasmtime_runtime::Export::Memory {
49-
definition,
50-
vmctx: _,
51-
memory: _,
52-
}) => WasiCallerMemory {
53-
base: (*definition).base,
54-
len: (*definition).current_length,
55-
},
56-
_ => WasiCallerMemory {
57-
base: std::ptr::null_mut(),
58-
len: 0,
59-
},
60-
}
61-
}
62-
}
63-
64-
fn into_abi(self) {}
65-
}
66-
67-
impl WasiCallerMemory {
68-
unsafe fn get(&self) -> Result<&mut [u8], wasi_common::wasi::__wasi_errno_t> {
69-
if self.base.is_null() {
70-
Err(wasi_common::wasi::__WASI_ERRNO_INVAL)
71-
} else {
72-
Ok(std::slice::from_raw_parts_mut(self.base, self.len))
73-
}
74-
}
75-
}

0 commit comments

Comments
 (0)