From 9e75ec1a89883ec7f70dc5382e82bd6623c0cff8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 9 May 2021 18:41:55 -0700 Subject: [PATCH 01/90] wip --- .github/workflows/main.yml | 2 +- crates/cranelift/src/func_environ.rs | 30 +- crates/environ/src/vmoffsets.rs | 18 +- crates/fuzzing/src/oracles.rs | 107 +- crates/fuzzing/src/oracles/dummy.rs | 26 +- crates/runtime/src/export.rs | 28 +- crates/runtime/src/externref.rs | 288 ++-- crates/runtime/src/helpers.c | 7 +- crates/runtime/src/instance.rs | 319 ++--- crates/runtime/src/instance/allocator.rs | 140 +- .../runtime/src/instance/allocator/pooling.rs | 113 +- .../src/instance/allocator/pooling/uffd.rs | 14 +- crates/runtime/src/lib.rs | 46 +- crates/runtime/src/libcalls.rs | 50 +- crates/runtime/src/memory.rs | 246 ++-- crates/runtime/src/table.rs | 350 ++--- crates/runtime/src/traphandlers.rs | 112 +- crates/runtime/src/traphandlers/macos.rs | 2 +- crates/runtime/src/traphandlers/unix.rs | 2 +- crates/runtime/src/traphandlers/windows.rs | 3 +- crates/runtime/src/vmcontext.rs | 53 +- crates/wasi-common/cap-std-sync/src/dir.rs | 2 +- crates/wasi-common/cap-std-sync/src/file.rs | 6 +- crates/wasi-common/cap-std-sync/src/lib.rs | 8 +- crates/wasi-common/cap-std-sync/src/sched.rs | 2 +- crates/wasi-common/cap-std-sync/src/stdio.rs | 12 +- crates/wasi-common/src/ctx.rs | 28 +- crates/wasi-common/src/dir.rs | 18 +- crates/wasi-common/src/file.rs | 36 +- crates/wasi-common/src/pipe.rs | 8 +- crates/wasi-common/src/sched.rs | 6 +- crates/wasi-common/src/sched/subscription.rs | 26 +- crates/wasi-common/src/snapshots/preview_0.rs | 117 +- crates/wasi-common/src/snapshots/preview_1.rs | 139 +- crates/wasi-common/src/table.rs | 53 +- crates/wasi-common/tokio/src/file.rs | 8 +- crates/wasi-common/tokio/src/lib.rs | 4 +- crates/wasi-common/tokio/src/sched/unix.rs | 4 +- crates/wasi-crypto/src/lib.rs | 21 +- .../wiggle_interfaces/asymmetric_common.rs | 80 +- .../src/wiggle_interfaces/common.rs | 40 +- .../src/wiggle_interfaces/key_exchange.rs | 13 +- .../src/wiggle_interfaces/signatures.rs | 47 +- .../src/wiggle_interfaces/symmetric.rs | 108 +- crates/wasi-nn/src/impl.rs | 10 +- crates/wasi-nn/src/lib.rs | 2 +- crates/wasi-nn/src/witx.rs | 2 +- crates/wasi/src/lib.rs | 52 +- crates/wasmtime/src/config.rs | 210 --- crates/wasmtime/src/engine.rs | 70 +- crates/wasmtime/src/externals.rs | 375 ++--- crates/wasmtime/src/func.rs | 868 ++++++----- crates/wasmtime/src/func/typed.rs | 222 +-- crates/wasmtime/src/instance.rs | 356 ++--- crates/wasmtime/src/lib.rs | 31 +- crates/wasmtime/src/limits.rs | 14 +- crates/wasmtime/src/linker.rs | 415 ++++-- crates/wasmtime/src/memory.rs | 222 +-- crates/wasmtime/src/module/registry.rs | 276 ++-- crates/wasmtime/src/ref.rs | 2 +- crates/wasmtime/src/signatures.rs | 63 +- crates/wasmtime/src/store.rs | 1274 +++++++++-------- crates/wasmtime/src/store/context.rs | 224 +++ crates/wasmtime/src/store/data.rs | 255 ++++ crates/wasmtime/src/trampoline.rs | 73 +- crates/wasmtime/src/trampoline/func.rs | 94 +- crates/wasmtime/src/trampoline/global.rs | 21 +- crates/wasmtime/src/trampoline/memory.rs | 8 +- crates/wasmtime/src/trampoline/table.rs | 6 +- crates/wasmtime/src/trap.rs | 115 +- crates/wasmtime/src/types/matching.rs | 25 +- crates/wasmtime/src/unix.rs | 22 +- crates/wasmtime/src/values.rs | 58 +- crates/wasmtime/src/windows.rs | 16 +- crates/wast/src/spectest.rs | 31 +- crates/wast/src/wast.rs | 37 +- crates/wiggle/borrow/src/lib.rs | 21 +- crates/wiggle/generate/src/funcs.rs | 2 +- crates/wiggle/generate/src/lib.rs | 2 +- crates/wiggle/generate/src/module_trait.rs | 4 +- crates/wiggle/macro/src/lib.rs | 8 +- crates/wiggle/src/lib.rs | 41 +- crates/wiggle/test-helpers/src/lib.rs | 3 + crates/wiggle/tests/atoms.rs | 15 +- crates/wiggle/tests/atoms_async.rs | 12 +- crates/wiggle/tests/errors.rs | 22 +- crates/wiggle/tests/flags.rs | 6 +- crates/wiggle/tests/handles.rs | 12 +- crates/wiggle/tests/ints.rs | 6 +- crates/wiggle/tests/keywords.rs | 2 +- crates/wiggle/tests/lists.rs | 26 +- crates/wiggle/tests/pointers.rs | 6 +- crates/wiggle/tests/records.rs | 39 +- crates/wiggle/tests/strings.rs | 16 +- crates/wiggle/tests/variant.rs | 16 +- crates/wiggle/tests/wasi.rs | 90 +- crates/wiggle/wasmtime/macro/src/lib.rs | 278 +--- crates/wiggle/wasmtime/src/lib.rs | 12 +- examples/externref.rs | 26 +- examples/fib-debug/main.rs | 8 +- examples/fuel.rs | 8 +- examples/gcd.rs | 8 +- examples/hello.rs | 49 +- examples/interrupt.rs | 8 +- examples/linking.rs | 31 +- examples/memory.rs | 90 +- examples/multi.rs | 32 +- examples/serialize.rs | 10 +- examples/threads.rs | 86 +- examples/threads.wat | 5 +- examples/tokio/main.rs | 115 +- examples/wasi/main.rs | 46 +- src/commands/compile.rs | 8 +- src/commands/run.rs | 137 +- src/commands/wast.rs | 2 +- tests/all/async_functions.rs | 324 ++--- tests/all/custom_signal_handler.rs | 108 +- tests/all/externals.rs | 357 ++--- tests/all/fuel.rs | 8 +- tests/all/func.rs | 691 +++++---- tests/all/funcref.rs | 81 +- tests/all/gc.rs | 543 +++---- tests/all/globals.rs | 56 +- tests/all/host_funcs.rs | 645 ++++----- tests/all/iloop.rs | 50 +- tests/all/import_calling_export.rs | 59 +- tests/all/import_indexes.rs | 12 +- tests/all/instance.rs | 53 +- tests/all/invoke_func_via_table.rs | 11 +- tests/all/limits.rs | 236 +-- tests/all/linker.rs | 171 ++- tests/all/main.rs | 5 +- tests/all/memory_creator.rs | 46 +- tests/all/module.rs | 10 +- tests/all/module_linking.rs | 15 +- tests/all/module_serialize.rs | 16 +- tests/all/pooling_allocator.rs | 184 +-- tests/all/stack_overflow.rs | 10 +- tests/all/table.rs | 30 +- tests/all/traps.rs | 205 +-- tests/all/use_after_drop.rs | 21 - tests/all/wast.rs | 2 +- tests/host_segfault.rs | 26 +- 143 files changed, 6873 insertions(+), 6601 deletions(-) create mode 100644 crates/wasmtime/src/store/context.rs create mode 100644 crates/wasmtime/src/store/data.rs delete mode 100644 tests/all/use_after_drop.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 71dc0a3b3c19..bfc9e7807d02 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,7 @@ name: CI on: push: - branches: [main] + branches: [main, new-api] tags-ignore: [dev] pull_request: branches: [main] diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 29d58dac2045..6c093a2efe46 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -394,25 +394,18 @@ impl<'module_environment> FuncEnvironment<'module_environment> { debug_assert!(delta == -1 || delta == 1); let pointer_type = self.pointer_type(); - let ref_count_offset = ir::immediates::Offset32::new( - i32::try_from(VMOffsets::vm_extern_data_ref_count()).unwrap(), - ); - let old_ref_count = builder.ins().load( + // If this changes that's ok, the `atomic_rmw` below just needs to be + // preceded with an add instruction of `externref` and the offset. + assert_eq!(VMOffsets::vm_extern_data_ref_count(), 0); + let delta = builder.ins().iconst(pointer_type, delta); + builder.ins().atomic_rmw( pointer_type, ir::MemFlags::trusted(), + ir::AtomicRmwOp::Add, externref, - ref_count_offset, - ); - let new_ref_count = builder.ins().iadd_imm(old_ref_count, delta); - builder.ins().store( - ir::MemFlags::trusted(), - new_ref_count, - externref, - ref_count_offset, - ); - - new_ref_count + delta, + ) } fn get_global_location( @@ -1052,8 +1045,11 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m builder.ins().jump(continue_block, &[]); builder.switch_to_block(dec_ref_count_block); - let ref_count = self.mutate_extenref_ref_count(builder, current_elem, -1); - builder.ins().brz(ref_count, drop_block, &[]); + let prev_ref_count = self.mutate_extenref_ref_count(builder, current_elem, -1); + let one = builder.ins().iconst(pointer_type, 1); + builder + .ins() + .br_icmp(IntCC::Equal, one, prev_ref_count, drop_block, &[]); builder.ins().jump(continue_block, &[]); // Call the `drop_externref` builtin to (you guessed it) drop diff --git a/crates/environ/src/vmoffsets.rs b/crates/environ/src/vmoffsets.rs index 6fc68f5e285b..dd79b499e66d 100644 --- a/crates/environ/src/vmoffsets.rs +++ b/crates/environ/src/vmoffsets.rs @@ -6,7 +6,7 @@ // struct VMContext { // interrupts: *const VMInterrupts, // externref_activations_table: *mut VMExternRefActivationsTable, -// module_info_lookup: *const dyn ModuleInfoLookup, +// store: *mut dyn Store, // signature_ids: [VMSharedSignatureIndex; module.num_signature_ids], // imported_functions: [VMFunctionImport; module.num_imported_functions], // imported_tables: [VMTableImport; module.num_imported_tables], @@ -77,7 +77,7 @@ pub struct VMOffsets { // precalculated offsets of various member fields interrupts: u32, externref_activations_table: u32, - module_info_lookup: u32, + store: u32, signature_ids: u32, imported_functions: u32, imported_tables: u32, @@ -149,7 +149,7 @@ impl From for VMOffsets { num_defined_globals: fields.num_defined_globals, interrupts: 0, externref_activations_table: 0, - module_info_lookup: 0, + store: 0, signature_ids: 0, imported_functions: 0, imported_tables: 0, @@ -168,12 +168,12 @@ impl From for VMOffsets { .interrupts .checked_add(u32::from(fields.pointer_size)) .unwrap(); - ret.module_info_lookup = ret + ret.store = ret .externref_activations_table .checked_add(u32::from(fields.pointer_size)) .unwrap(); ret.signature_ids = ret - .module_info_lookup + .store .checked_add(u32::from(fields.pointer_size * 2)) .unwrap(); ret.imported_functions = ret @@ -501,16 +501,16 @@ impl VMOffsets { self.interrupts } - /// The offset of the `VMExternRefActivationsTable` member. + /// The offset of the `*mut VMExternRefActivationsTable` member. #[inline] pub fn vmctx_externref_activations_table(&self) -> u32 { self.externref_activations_table } - /// The offset of the `*const dyn ModuleInfoLookup` member. + /// The offset of the `*const dyn Store` member. #[inline] - pub fn vmctx_module_info_lookup(&self) -> u32 { - self.module_info_lookup + pub fn vmctx_store(&self) -> u32 { + self.store } /// The offset of the `signature_ids` array. diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 7b3cd5874f05..943037253752 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -15,8 +15,6 @@ pub mod dummy; use arbitrary::Arbitrary; use dummy::dummy_linker; use log::debug; -use std::cell::Cell; -use std::rc::Rc; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::{Arc, Condvar, Mutex}; use std::time::{Duration, Instant}; @@ -44,9 +42,9 @@ fn log_wasm(wasm: &[u8]) { } } -fn create_store(engine: &Engine) -> Store { - Store::new_with_limits( - &engine, +fn create_store(engine: &Engine) -> Store<()> { + let mut store = Store::new(&engine, ()); + store.limiter( StoreLimitsBuilder::new() // The limits here are chosen based on the default "maximum type size" // configured in wasm-smith, which is 1000. This means that instances @@ -56,7 +54,8 @@ fn create_store(engine: &Engine) -> Store { .tables(1100) .memories(1100) .build(), - ) + ); + store } /// Methods of timing out execution of a WebAssembly module @@ -110,7 +109,7 @@ pub fn instantiate_with_config( _ => false, }); let engine = Engine::new(&config).unwrap(); - let store = create_store(&engine); + let mut store = create_store(&engine); let mut timeout_state = SignalOnDrop::default(); match timeout { @@ -137,9 +136,9 @@ pub fn instantiate_with_config( Err(_) if !known_valid => return, Err(e) => panic!("failed to compile module: {:?}", e), }; - let linker = dummy_linker(&store, &module); + let linker = dummy_linker(&mut store, &module); - match linker.instantiate(&module) { + match linker.instantiate(&mut store, &module) { Ok(_) => {} Err(e) => { let string = e.to_string(); @@ -218,7 +217,7 @@ pub fn differential_execution( config.wasm_module_linking(false); let engine = Engine::new(&config).unwrap(); - let store = create_store(&engine); + let mut store = create_store(&engine); let module = Module::new(&engine, &wasm).unwrap(); @@ -227,13 +226,13 @@ pub fn differential_execution( // in and with what values. Like the results of exported functions, // calls to imports should also yield the same values for each // configuration, and we should assert that. - let linker = dummy_linker(&store, &module); + let linker = dummy_linker(&mut store, &module); // Don't unwrap this: there can be instantiation-/link-time errors that // aren't caught during validation or compilation. For example, an imported // table might not have room for an element segment that we want to // initialize into it. - let instance = match linker.instantiate(&module) { + let instance = match linker.instantiate(&mut store, &module) { Ok(instance) => instance, Err(e) => { eprintln!( @@ -244,30 +243,36 @@ pub fn differential_execution( } }; - for (name, f) in instance.exports().filter_map(|e| { - let name = e.name(); - e.into_func().map(|f| (name, f)) - }) { + let exports = instance + .exports(&mut store) + .filter_map(|e| { + let name = e.name().to_string(); + e.into_func().map(|f| (name, f)) + }) + .collect::>(); + for (name, f) in exports { // Always call the hang limit initializer first, so that we don't // infinite loop when calling another export. - init_hang_limit(&instance); + init_hang_limit(&mut store, instance); - let ty = f.ty(); + let ty = f.ty(&store); let params = dummy::dummy_values(ty.params()); - let this_result = f.call(¶ms).map_err(|e| e.downcast::().unwrap()); + let this_result = f + .call(&mut store, ¶ms) + .map_err(|e| e.downcast::().unwrap()); let existing_result = export_func_results .entry(name.to_string()) .or_insert_with(|| this_result.clone()); - assert_same_export_func_result(&existing_result, &this_result, name); + assert_same_export_func_result(&existing_result, &this_result, &name); } } - fn init_hang_limit(instance: &Instance) { - match instance.get_export("hangLimitInitializer") { + fn init_hang_limit(store: &mut Store<()>, instance: Instance) { + match instance.get_export(&mut *store, "hangLimitInitializer") { None => return, Some(Extern::Func(f)) => { - f.call(&[]) + f.call(store, &[]) .expect("initializing the hang limit should not fail"); } Some(_) => panic!("unexpected hangLimitInitializer export"), @@ -332,7 +337,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { let mut config: Option = None; let mut engine: Option = None; - let mut store: Option = None; + let mut store: Option> = None; let mut modules: HashMap = Default::default(); let mut instances: HashMap = Default::default(); @@ -390,14 +395,14 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { None => continue, }; - let store = store.as_ref().unwrap(); + let store = store.as_mut().unwrap(); let linker = dummy_linker(store, module); // Don't unwrap this: there can be instantiation-/link-time errors that // aren't caught during validation or compilation. For example, an imported // table might not have room for an element segment that we want to // initialize into it. - if let Ok(instance) = linker.instantiate(&module) { + if let Ok(instance) = linker.instantiate(store, &module) { instances.insert(id, instance); } } @@ -421,9 +426,10 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { continue; } }; + let store = store.as_mut().unwrap(); let funcs = instance - .exports() + .exports(&mut *store) .filter_map(|e| match e.into_extern() { Extern::Func(f) => Some(f.clone()), _ => None, @@ -436,9 +442,9 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { let nth = nth % funcs.len(); let f = &funcs[nth]; - let ty = f.ty(); + let ty = f.ty(&store); let params = dummy::dummy_values(ty.params()); - let _ = f.call(¶ms); + let _ = f.call(store, ¶ms); } } } @@ -454,7 +460,7 @@ pub fn spectest(fuzz_config: crate::generators::Config, test: crate::generators: config.wasm_reference_types(false); config.wasm_bulk_memory(false); config.wasm_module_linking(false); - let store = create_store(&Engine::new(&config).unwrap()); + let mut store = create_store(&Engine::new(&config).unwrap()); if fuzz_config.consume_fuel { store.add_fuel(u64::max_value()).unwrap(); } @@ -472,13 +478,13 @@ pub fn table_ops( ) { let _ = env_logger::try_init(); - let num_dropped = Rc::new(Cell::new(0)); + let num_dropped = Arc::new(AtomicUsize::new(0)); { let mut config = fuzz_config.to_wasmtime(); config.wasm_reference_types(true); let engine = Engine::new(&config).unwrap(); - let store = create_store(&engine); + let mut store = create_store(&engine); if fuzz_config.consume_fuel { store.add_fuel(u64::max_value()).unwrap(); } @@ -494,31 +500,30 @@ pub fn table_ops( // test case. const MAX_GCS: usize = 5; - let num_gcs = Cell::new(0); - let gc = Func::wrap(&store, move |caller: Caller| { - if num_gcs.get() < MAX_GCS { - caller.store().gc(); - num_gcs.set(num_gcs.get() + 1); + let num_gcs = AtomicUsize::new(0); + let gc = Func::wrap(&mut store, move |mut caller: Caller<'_, ()>| { + if num_gcs.fetch_add(1, SeqCst) < MAX_GCS { + caller.gc(); } }); - let instance = Instance::new(&store, &module, &[gc.into()]).unwrap(); - let run = instance.get_func("run").unwrap(); + let instance = Instance::new(&mut store, &module, &[gc.into()]).unwrap(); + let run = instance.get_func(&mut store, "run").unwrap(); let args: Vec<_> = (0..ops.num_params()) .map(|_| Val::ExternRef(Some(ExternRef::new(CountDrops(num_dropped.clone()))))) .collect(); - let _ = run.call(&args); + let _ = run.call(&mut store, &args); } - assert_eq!(num_dropped.get(), ops.num_params()); + assert_eq!(num_dropped.load(SeqCst), ops.num_params() as usize); return; - struct CountDrops(Rc>); + struct CountDrops(Arc); impl Drop for CountDrops { fn drop(&mut self) { - self.0.set(self.0.get().checked_add(1).unwrap()); + self.0.fetch_add(1, SeqCst); } } } @@ -593,13 +598,13 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con let mut wasmtime_config = config.to_wasmtime(); wasmtime_config.cranelift_nan_canonicalization(true); let wasmtime_engine = Engine::new(&wasmtime_config).unwrap(); - let wasmtime_store = create_store(&wasmtime_engine); + let mut wasmtime_store = create_store(&wasmtime_engine); if config.consume_fuel { wasmtime_store.add_fuel(u64::max_value()).unwrap(); } let wasmtime_module = Module::new(&wasmtime_engine, &wasm).expect("Wasmtime can compile module"); - let wasmtime_instance = Instance::new(&wasmtime_store, &wasmtime_module, &[]) + let wasmtime_instance = Instance::new(&mut wasmtime_store, &wasmtime_module, &[]) .expect("Wasmtime can instantiate module"); // Introspect wasmtime module to find name of an exported function and of an @@ -628,12 +633,12 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con let wasmi_val = wasmi::FuncInstance::invoke(&wasmi_main, &[], &mut wasmi::NopExternals); let wasmtime_mem = wasmtime_instance - .get_memory(&memory_name[..]) + .get_memory(&mut wasmtime_store, &memory_name[..]) .expect("memory export is present"); let wasmtime_main = wasmtime_instance - .get_func(&func_name[..]) + .get_func(&mut wasmtime_store, &func_name[..]) .expect("function export is present"); - let wasmtime_vals = wasmtime_main.call(&[]); + let wasmtime_vals = wasmtime_main.call(&mut wasmtime_store, &[]); let wasmtime_val = wasmtime_vals.map(|v| v.iter().next().cloned()); debug!( @@ -665,18 +670,18 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con } } - if wasmi_mem.current_size().0 != wasmtime_mem.size() as usize { + if wasmi_mem.current_size().0 != wasmtime_mem.size(&wasmtime_store) as usize { show_wat(); panic!("resulting memories are not the same size"); } // Wasmi memory may be stored non-contiguously; copy it out to a contiguous chunk. - let mut wasmi_buf: Vec = vec![0; wasmtime_mem.data_size()]; + let mut wasmi_buf: Vec = vec![0; wasmtime_mem.data_size(&wasmtime_store)]; wasmi_mem .get_into(0, &mut wasmi_buf[..]) .expect("can access wasmi memory"); - let wasmtime_slice = unsafe { wasmtime_mem.data_unchecked() }; + let wasmtime_slice = wasmtime_mem.data(&wasmtime_store); if wasmi_buf.len() >= 64 { debug!("-> First 64 bytes of wasmi heap: {:?}", &wasmi_buf[0..64]); diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs index 99b5be736bee..7a3099ff031d 100644 --- a/crates/fuzzing/src/oracles/dummy.rs +++ b/crates/fuzzing/src/oracles/dummy.rs @@ -4,8 +4,8 @@ use std::fmt::Write; use wasmtime::*; /// Create a set of dummy functions/globals/etc for the given imports. -pub fn dummy_linker<'module>(store: &Store, module: &Module) -> Linker { - let mut linker = Linker::new(store); +pub fn dummy_linker<'module>(store: &mut Store<()>, module: &Module) -> Linker<()> { + let mut linker = Linker::new(store.engine()); linker.allow_shadowing(true); for import in module.imports() { match import.name() { @@ -34,19 +34,19 @@ pub fn dummy_linker<'module>(store: &Store, module: &Module) -> Linker { } /// Construct a dummy `Extern` from its type signature -pub fn dummy_extern(store: &Store, ty: ExternType) -> Extern { +pub fn dummy_extern(store: &mut Store<()>, ty: ExternType) -> Extern { match ty { ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty)), ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)), ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_ty)), ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(store, mem_ty)), ExternType::Instance(instance_ty) => Extern::Instance(dummy_instance(store, instance_ty)), - ExternType::Module(module_ty) => Extern::Module(dummy_module(store, module_ty)), + ExternType::Module(module_ty) => Extern::Module(dummy_module(store.engine(), module_ty)), } } /// Construct a dummy function for the given function type -pub fn dummy_func(store: &Store, ty: FuncType) -> Func { +pub fn dummy_func(store: &mut Store<()>, ty: FuncType) -> Func { Func::new(store, ty.clone(), move |_, _, results| { for (ret_ty, result) in ty.results().zip(results) { *result = dummy_value(ret_ty); @@ -74,19 +74,19 @@ pub fn dummy_values(val_tys: impl IntoIterator) -> Vec { } /// Construct a dummy global for the given global type. -pub fn dummy_global(store: &Store, ty: GlobalType) -> Global { +pub fn dummy_global(store: &mut Store<()>, ty: GlobalType) -> Global { let val = dummy_value(ty.content().clone()); Global::new(store, ty, val).unwrap() } /// Construct a dummy table for the given table type. -pub fn dummy_table(store: &Store, ty: TableType) -> Table { +pub fn dummy_table(store: &mut Store<()>, ty: TableType) -> Table { let init_val = dummy_value(ty.element().clone()); Table::new(store, ty, init_val).unwrap() } /// Construct a dummy memory for the given memory type. -pub fn dummy_memory(store: &Store, ty: MemoryType) -> Memory { +pub fn dummy_memory(store: &mut Store<()>, ty: MemoryType) -> Memory { Memory::new(store, ty).unwrap() } @@ -94,7 +94,7 @@ pub fn dummy_memory(store: &Store, ty: MemoryType) -> Memory { /// /// This is done by using the expected type to generate a module on-the-fly /// which we the instantiate. -pub fn dummy_instance(store: &Store, ty: InstanceType) -> Instance { +pub fn dummy_instance(store: &mut Store<()>, ty: InstanceType) -> Instance { let mut wat = WatGenerator::new(); for ty in ty.exports() { wat.export(&ty); @@ -106,7 +106,7 @@ pub fn dummy_instance(store: &Store, ty: InstanceType) -> Instance { /// Construct a dummy module for the given module type. /// /// This is done by using the expected type to generate a module on-the-fly. -pub fn dummy_module(store: &Store, ty: ModuleType) -> Module { +pub fn dummy_module(engine: &Engine, ty: ModuleType) -> Module { let mut wat = WatGenerator::new(); for ty in ty.imports() { wat.import(&ty); @@ -114,7 +114,7 @@ pub fn dummy_module(store: &Store, ty: ModuleType) -> Module { for ty in ty.exports() { wat.export(&ty); } - Module::new(store.engine(), &wat.finish()).unwrap() + Module::new(engine, &wat.finish()).unwrap() } struct WatGenerator { @@ -378,12 +378,12 @@ mod tests { use super::*; use std::collections::HashSet; - fn store() -> Store { + fn store() -> Store<()> { let mut config = Config::default(); config.wasm_module_linking(true); config.wasm_multi_memory(true); let engine = wasmtime::Engine::new(&config).unwrap(); - Store::new(&engine) + Store::new(&engine, ()) } #[test] diff --git a/crates/runtime/src/export.rs b/crates/runtime/src/export.rs index b9564e571aa6..6c6f0a310adf 100644 --- a/crates/runtime/src/export.rs +++ b/crates/runtime/src/export.rs @@ -1,8 +1,6 @@ use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition, }; -use crate::RuntimeInstance; -use std::any::Any; use std::ptr::NonNull; use wasmtime_environ::wasm::Global; use wasmtime_environ::{MemoryPlan, TablePlan}; @@ -20,16 +18,10 @@ pub enum Export { /// A global export value. Global(ExportGlobal), - - /// An instance - Instance(RuntimeInstance), - - /// A module - Module(Box), } /// A function export value. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct ExportFunction { /// The `VMCallerCheckedAnyfunc` for this exported function. /// @@ -38,6 +30,12 @@ pub struct ExportFunction { pub anyfunc: NonNull, } +// It's part of the contract of using `ExportFunction` that synchronization +// properties are upheld, so declare that despite the raw pointers inside this +// is send/sync. +unsafe impl Send for ExportFunction {} +unsafe impl Sync for ExportFunction {} + impl From for Export { fn from(func: ExportFunction) -> Export { Export::Function(func) @@ -55,6 +53,10 @@ pub struct ExportTable { pub table: TablePlan, } +// See docs on send/sync for `ExportFunction` above. +unsafe impl Send for ExportTable {} +unsafe impl Sync for ExportTable {} + impl From for Export { fn from(func: ExportTable) -> Export { Export::Table(func) @@ -72,6 +74,10 @@ pub struct ExportMemory { pub memory: MemoryPlan, } +// See docs on send/sync for `ExportFunction` above. +unsafe impl Send for ExportMemory {} +unsafe impl Sync for ExportMemory {} + impl From for Export { fn from(func: ExportMemory) -> Export { Export::Memory(func) @@ -89,6 +95,10 @@ pub struct ExportGlobal { pub global: Global, } +// See docs on send/sync for `ExportFunction` above. +unsafe impl Send for ExportGlobal {} +unsafe impl Sync for ExportGlobal {} + impl From for Export { fn from(func: ExportGlobal) -> Export { Export::Global(func) diff --git a/crates/runtime/src/externref.rs b/crates/runtime/src/externref.rs index 4d7b8f9c47ce..4f9b17f13022 100644 --- a/crates/runtime/src/externref.rs +++ b/crates/runtime/src/externref.rs @@ -100,13 +100,14 @@ //! use std::any::Any; -use std::cell::{Cell, RefCell, UnsafeCell}; -use std::cmp::Ordering; +use std::cell::UnsafeCell; +use std::cmp; use std::collections::HashSet; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; use std::ptr::{self, NonNull}; +use std::sync::atomic::{self, AtomicUsize, Ordering}; use std::{alloc::Layout, sync::Arc}; use wasmtime_environ::ir::StackMap; @@ -145,17 +146,16 @@ use wasmtime_environ::ir::StackMap; /// let file = std::fs::File::create("some/file/path")?; /// /// // Wrap the file up as an `VMExternRef` that can be passed to Wasm. -/// let extern_ref_to_file = VMExternRef::new(RefCell::new(file)); +/// let extern_ref_to_file = VMExternRef::new(file); /// /// // `VMExternRef`s dereference to `dyn Any`, so you can use `Any` methods to /// // perform runtime type checks and downcasts. /// -/// assert!(extern_ref_to_file.is::>()); +/// assert!(extern_ref_to_file.is::()); /// assert!(!extern_ref_to_file.is::()); /// -/// if let Some(file) = extern_ref_to_file.downcast_ref::>() { +/// if let Some(mut file) = extern_ref_to_file.downcast_ref::() { /// use std::io::Write; -/// let mut file = file.borrow_mut(); /// writeln!(&mut file, "Hello, `VMExternRef`!")?; /// } /// # Ok(()) @@ -165,6 +165,10 @@ use wasmtime_environ::ir::StackMap; #[repr(transparent)] pub struct VMExternRef(NonNull); +// data contained is always Send+Sync so these should be safe +unsafe impl Send for VMExternRef {} +unsafe impl Sync for VMExternRef {} + #[repr(C)] pub(crate) struct VMExternData { // Implicit, dynamically-sized member that always preceded an @@ -180,11 +184,11 @@ pub(crate) struct VMExternData { /// Note: this field's offset must be kept in sync with /// `wasmtime_environ::VMOffsets::vm_extern_data_ref_count()` which is /// currently always zero. - ref_count: UnsafeCell, + ref_count: AtomicUsize, /// Always points to the implicit, dynamically-sized `value` member that /// precedes this `VMExternData`. - value_ptr: NonNull, + value_ptr: NonNull, } impl Clone for VMExternRef { @@ -199,13 +203,23 @@ impl Drop for VMExternRef { #[inline] fn drop(&mut self) { let data = self.extern_data(); - data.decrement_ref_count(); - if data.get_ref_count() == 0 { - // Drop our live reference to `data` before we drop it itself. - drop(data); - unsafe { - VMExternData::drop_and_dealloc(self.0); - } + + // Note that the memory orderings here also match the standard library + // itself. Documentation is more available in the implementation of + // `Arc`, but the general idea is that this is a special pattern allowed + // by the C standard with atomic orderings where we "release" for all + // the decrements and only the final decrementer performs an acquire + // fence. This properly ensures that the final thread, which actually + // destroys the data, sees all the updates from all other threads. + if data.ref_count.fetch_sub(1, Ordering::Release) != 1 { + return; + } + atomic::fence(Ordering::Acquire); + + // Drop our live reference to `data` before we drop it itself. + drop(data); + unsafe { + VMExternData::drop_and_dealloc(self.0); } } } @@ -262,23 +276,19 @@ impl VMExternData { #[inline] fn get_ref_count(&self) -> usize { - unsafe { *self.ref_count.get() } + self.ref_count.load(Ordering::SeqCst) } #[inline] fn increment_ref_count(&self) { - unsafe { - let count = self.ref_count.get(); - *count += 1; - } - } - - #[inline] - fn decrement_ref_count(&self) { - unsafe { - let count = self.ref_count.get(); - *count -= 1; - } + // This is only using during cloning operations, and like the standard + // library we use `Relaxed` here. The rationale is better documented in + // libstd's implementation of `Arc`, but the general gist is that we're + // creating a new pointer for our own thread, so there's no need to have + // any synchronization with orderings. The synchronization with other + // threads with respect to orderings happens when the pointer is sent to + // another thread. + self.ref_count.fetch_add(1, Ordering::Relaxed); } } @@ -293,7 +303,7 @@ impl VMExternRef { /// Wrap the given value inside an `VMExternRef`. pub fn new(value: T) -> VMExternRef where - T: 'static + Any, + T: 'static + Any + Send + Sync, { VMExternRef::new_with(|| value) } @@ -301,7 +311,7 @@ impl VMExternRef { /// Construct a new `VMExternRef` in place by invoking `make_value`. pub fn new_with(make_value: impl FnOnce() -> T) -> VMExternRef where - T: 'static + Any, + T: 'static + Any + Send + Sync, { unsafe { let (layout, footer_offset) = @@ -316,9 +326,9 @@ impl VMExternRef { ptr::write(value_ptr.as_ptr(), make_value()); let value_ref: &T = value_ptr.as_ref(); - let value_ref: &dyn Any = value_ref as _; - let value_ptr: *const dyn Any = value_ref as _; - let value_ptr: *mut dyn Any = value_ptr as _; + let value_ref: &(dyn Any + Send + Sync) = value_ref as _; + let value_ptr: *const (dyn Any + Send + Sync) = value_ref as _; + let value_ptr: *mut (dyn Any + Send + Sync) = value_ptr as _; let value_ptr = NonNull::new_unchecked(value_ptr); let extern_data_ptr = @@ -326,7 +336,7 @@ impl VMExternRef { ptr::write( extern_data_ptr, VMExternData { - ref_count: UnsafeCell::new(1), + ref_count: AtomicUsize::new(1), value_ptr, }, ); @@ -439,7 +449,7 @@ impl VMExternRef { /// semantics, and so only pointers are compared, and doesn't use any `Cmp` /// or `PartialCmp` implementation of the pointed-to values. #[inline] - pub fn cmp(a: &Self, b: &Self) -> Ordering { + pub fn cmp(a: &Self, b: &Self) -> cmp::Ordering { let a = a.0.as_ptr() as usize; let b = b.0.as_ptr() as usize; a.cmp(&b) @@ -488,20 +498,9 @@ type TableElem = UnsafeCell>; /// entries. Deduplication happens at GC time. #[repr(C)] pub struct VMExternRefActivationsTable { - /// Bump-allocation finger within the `chunk`. - /// - /// NB: this is an `UnsafeCell` because it is written to by compiled Wasm - /// code. - next: UnsafeCell>, - - /// Pointer to just after the `chunk`. - /// - /// This is *not* within the current chunk and therefore is not a valid - /// place to insert a reference! - end: NonNull, - - /// Bump allocation chunk that stores fast-path insertions. - chunk: Box<[TableElem]>, + /// Structures used to perform fast bump allocation of storage of externref + /// values. + alloc: VMExternRefTableAlloc, /// When unioned with `chunk`, this is an over-approximation of the GC roots /// on the stack, inside Wasm frames. @@ -510,7 +509,7 @@ pub struct VMExternRefActivationsTable { /// re-initialized to the just-discovered precise set of stack roots (which /// immediately becomes an over-approximation again as soon as Wasm runs and /// potentially drops references). - over_approximated_stack_roots: RefCell>, + over_approximated_stack_roots: HashSet, /// The precise set of on-stack, inside-Wasm GC roots that we discover via /// walking the stack and interpreting stack maps. @@ -518,7 +517,7 @@ pub struct VMExternRefActivationsTable { /// This is *only* used inside the `gc` function, and is empty otherwise. It /// is just part of this struct so that we can reuse the allocation, rather /// than create a new hash set every GC. - precise_stack_roots: RefCell>, + precise_stack_roots: HashSet, /// A pointer to the youngest host stack frame before we called /// into Wasm for the first time. When walking the stack in garbage @@ -526,7 +525,37 @@ pub struct VMExternRefActivationsTable { /// Wasm stack frame, which means we failed to find all on-stack, /// inside-a-Wasm-frame roots, and doing a GC could lead to freeing one of /// those missed roots, and use after free. - stack_canary: Cell>, + stack_canary: Option, +} + +#[repr(C)] +struct VMExternRefTableAlloc { + /// Bump-allocation finger within the `chunk`. + /// + /// NB: this is an `UnsafeCell` because it is written to by compiled Wasm + /// code. + next: UnsafeCell>, + + /// Pointer to just after the `chunk`. + /// + /// This is *not* within the current chunk and therefore is not a valid + /// place to insert a reference! + end: NonNull, + + /// Bump allocation chunk that stores fast-path insertions. + chunk: Box<[TableElem]>, +} + +// This gets around the usage of `UnsafeCell` throughout the internals of this +// allocator, but the storage should all be Send/Sync and synchronization isn't +// necessary since operations require `&mut self`. +unsafe impl Send for VMExternRefTableAlloc {} +unsafe impl Sync for VMExternRefTableAlloc {} + +fn _assert_send_sync() { + fn _assert() {} + _assert::(); + _assert::(); } impl VMExternRefActivationsTable { @@ -539,12 +568,14 @@ impl VMExternRefActivationsTable { let end = unsafe { next.add(chunk.len()) }; VMExternRefActivationsTable { - next: UnsafeCell::new(NonNull::new(next).unwrap()), - end: NonNull::new(end).unwrap(), - chunk, - over_approximated_stack_roots: RefCell::new(HashSet::with_capacity(Self::CHUNK_SIZE)), - precise_stack_roots: RefCell::new(HashSet::with_capacity(Self::CHUNK_SIZE)), - stack_canary: Cell::new(None), + alloc: VMExternRefTableAlloc { + next: UnsafeCell::new(NonNull::new(next).unwrap()), + end: NonNull::new(end).unwrap(), + chunk, + }, + over_approximated_stack_roots: HashSet::with_capacity(Self::CHUNK_SIZE), + precise_stack_roots: HashSet::with_capacity(Self::CHUNK_SIZE), + stack_canary: None, } } @@ -563,10 +594,10 @@ impl VMExternRefActivationsTable { /// `insert_slow_path` to infallibly insert the reference (potentially /// allocating additional space in the table to hold it). #[inline] - pub fn try_insert(&self, externref: VMExternRef) -> Result<(), VMExternRef> { + pub fn try_insert(&mut self, externref: VMExternRef) -> Result<(), VMExternRef> { unsafe { - let next = *self.next.get(); - if next == self.end { + let next = *self.alloc.next.get(); + if next == self.alloc.end { return Err(externref); } @@ -577,8 +608,8 @@ impl VMExternRefActivationsTable { ptr::write(next.as_ptr(), UnsafeCell::new(Some(externref))); let next = NonNull::new_unchecked(next.as_ptr().add(1)); - debug_assert!(next <= self.end); - *self.next.get() = next; + debug_assert!(next <= self.alloc.end); + *self.alloc.next.get() = next; Ok(()) } @@ -592,7 +623,7 @@ impl VMExternRefActivationsTable { /// The same as `gc`. #[inline] pub unsafe fn insert_with_gc( - &self, + &mut self, externref: VMExternRef, module_info_lookup: &dyn ModuleInfoLookup, ) { @@ -603,7 +634,7 @@ impl VMExternRefActivationsTable { #[inline(never)] unsafe fn gc_and_insert_slow( - &self, + &mut self, externref: VMExternRef, module_info_lookup: &dyn ModuleInfoLookup, ) { @@ -612,27 +643,26 @@ impl VMExternRefActivationsTable { // Might as well insert right into the hash set, rather than the bump // chunk, since we are already on a slow path and we get de-duplication // this way. - let mut roots = self.over_approximated_stack_roots.borrow_mut(); - roots.insert(VMExternRefWithTraits(externref)); + self.over_approximated_stack_roots + .insert(VMExternRefWithTraits(externref)); } fn num_filled_in_bump_chunk(&self) -> usize { - let next = unsafe { *self.next.get() }; - let bytes_unused = (self.end.as_ptr() as usize) - (next.as_ptr() as usize); + let next = unsafe { *self.alloc.next.get() }; + let bytes_unused = (self.alloc.end.as_ptr() as usize) - (next.as_ptr() as usize); let slots_unused = bytes_unused / mem::size_of::(); - self.chunk.len().saturating_sub(slots_unused) + self.alloc.chunk.len().saturating_sub(slots_unused) } fn elements(&self, mut f: impl FnMut(&VMExternRef)) { - let roots = self.over_approximated_stack_roots.borrow(); - for elem in roots.iter() { + for elem in self.over_approximated_stack_roots.iter() { f(&elem.0); } // The bump chunk is not all the way full, so we only iterate over its // filled-in slots. let num_filled = self.num_filled_in_bump_chunk(); - for slot in self.chunk.iter().take(num_filled) { + for slot in self.alloc.chunk.iter().take(num_filled) { if let Some(elem) = unsafe { &*slot.get() } { f(elem); } @@ -649,36 +679,20 @@ impl VMExternRefActivationsTable { /// Sweep the bump allocation table after we've discovered our precise stack /// roots. - fn sweep(&self, precise_stack_roots: &mut HashSet) { - // Swap out the over-approximated set so we can distinguish between the - // over-approximation before we started sweeping, and any new elements - // we might insert into the table because of re-entering Wasm via an - // `externref`'s destructor. The new elements must be kept alive for - // memory safety, but we keep this set around because we likely want to - // reuse its allocation/capacity for the new `precise_stack_roots` in - // the next GC cycle. - let mut old_over_approximated = mem::replace( - &mut *self.over_approximated_stack_roots.borrow_mut(), - Default::default(), - ); - + fn sweep(&mut self) { // Sweep our bump chunk. - // - // Just in case an `externref` destructor calls back into Wasm, passing - // more `externref`s into that Wasm, which requires the `externref`s to - // be inserted into this `VMExternRefActivationsTable`, make sure `next - // == end` so that they go into the over-approximation hash set. let num_filled = self.num_filled_in_bump_chunk(); unsafe { - *self.next.get() = self.end; + *self.alloc.next.get() = self.alloc.end; } - for slot in self.chunk.iter().take(num_filled) { + for slot in self.alloc.chunk.iter().take(num_filled) { unsafe { *slot.get() = None; } } debug_assert!( - self.chunk + self.alloc + .chunk .iter() .all(|slot| unsafe { (*slot.get()).as_ref().is_none() }), "after sweeping the bump chunk, all slots should be `None`" @@ -686,33 +700,25 @@ impl VMExternRefActivationsTable { // Reset our `next` finger to the start of the bump allocation chunk. unsafe { - let next = self.chunk.as_ptr() as *mut TableElem; + let next = self.alloc.chunk.as_ptr() as *mut TableElem; debug_assert!(!next.is_null()); - *self.next.get() = NonNull::new_unchecked(next); + *self.alloc.next.get() = NonNull::new_unchecked(next); } // The current `precise_stack_roots` becomes our new over-appoximated // set for the next GC cycle. - let mut over_approximated = self.over_approximated_stack_roots.borrow_mut(); - mem::swap(&mut *precise_stack_roots, &mut *over_approximated); + mem::swap( + &mut self.precise_stack_roots, + &mut self.over_approximated_stack_roots, + ); // And finally, the new `precise_stack_roots` should be cleared and // remain empty until the next GC cycle. // - // However, if an `externref` destructor called re-entered Wasm with - // more `externref`s, then the temp over-approximated set we were using - // during sweeping (now `precise_stack_roots`) is not empty, and we need - // to keep its references alive in our new over-approximated set. - over_approximated.extend(precise_stack_roots.drain()); - - // If we didn't re-enter Wasm during destructors (likely), - // `precise_stack_roots` has zero capacity, and the old - // over-approximated has a bunch of capacity. Reuse whichever set has - // most capacity. - if old_over_approximated.capacity() > precise_stack_roots.capacity() { - old_over_approximated.clear(); - *precise_stack_roots = old_over_approximated; - } + // Note that this may run arbitrary code as we run externref + // destructors. Because of our `&mut` borrow above on this table, + // though, we're guaranteed that nothing will touch this table. + self.precise_stack_roots.clear(); } /// Fetches the current value of this table's stack canary. @@ -724,7 +730,7 @@ impl VMExternRefActivationsTable { /// For more information on canaries see the gc functions below. #[inline] pub fn stack_canary(&self) -> Option { - self.stack_canary.get() + self.stack_canary } /// Sets the current value of the stack canary. @@ -736,14 +742,14 @@ impl VMExternRefActivationsTable { /// /// For more information on canaries see the gc functions below. #[inline] - pub fn set_stack_canary(&self, canary: Option) { - self.stack_canary.set(canary); + pub fn set_stack_canary(&mut self, canary: Option) { + self.stack_canary = canary; } } /// Used by the runtime to lookup information about a module given a /// program counter value. -pub trait ModuleInfoLookup: 'static { +pub trait ModuleInfoLookup { /// Lookup the module information from a program counter value. fn lookup(&self, pc: usize) -> Option>; } @@ -754,16 +760,6 @@ pub trait ModuleInfo { fn lookup_stack_map(&self, pc: usize) -> Option<&StackMap>; } -pub(crate) struct EmptyModuleInfoLookup; - -impl ModuleInfoLookup for EmptyModuleInfoLookup { - fn lookup(&self, _pc: usize) -> Option> { - None - } -} - -pub(crate) const EMPTY_MODULE_LOOKUP: EmptyModuleInfoLookup = EmptyModuleInfoLookup; - #[derive(Debug, Default)] struct DebugOnly { inner: T, @@ -810,22 +806,8 @@ impl std::ops::DerefMut for DebugOnly { /// that has frames on the stack with the given `stack_maps_registry`. pub unsafe fn gc( module_info_lookup: &dyn ModuleInfoLookup, - externref_activations_table: &VMExternRefActivationsTable, + externref_activations_table: &mut VMExternRefActivationsTable, ) { - // We borrow the precise stack roots `RefCell` for the whole duration of - // GC. Whether it is dynamically borrowed serves as a flag for detecting - // re-entrancy into GC. Re-entrancy can occur if we do a GC, drop an - // `externref`, and that `externref`'s destructor then triggers another - // GC. Whenever we detect re-entrancy, we return and give the first, - // outermost GC call priority. - let mut precise_stack_roots = match externref_activations_table - .precise_stack_roots - .try_borrow_mut() - { - Err(_) => return, - Ok(roots) => roots, - }; - log::debug!("start GC"); debug_assert!({ @@ -834,7 +816,7 @@ pub unsafe fn gc( // into the activations table's bump-allocated space at the // end. Therefore, it should always be empty upon entering this // function. - precise_stack_roots.is_empty() + externref_activations_table.precise_stack_roots.is_empty() }); // Whenever we call into Wasm from host code for the first time, we set a @@ -842,7 +824,7 @@ pub unsafe fn gc( // canary. If there is *not* a stack canary, then there must be zero Wasm // frames on the stack. Therefore, we can simply reset the table without // walking the stack. - let stack_canary = match externref_activations_table.stack_canary.get() { + let stack_canary = match externref_activations_table.stack_canary { None => { if cfg!(debug_assertions) { // Assert that there aren't any Wasm frames on the stack. @@ -851,7 +833,7 @@ pub unsafe fn gc( true }); } - externref_activations_table.sweep(&mut precise_stack_roots); + externref_activations_table.sweep(); log::debug!("end GC"); return; } @@ -911,7 +893,7 @@ pub unsafe fn gc( ); if let Some(r) = NonNull::new(r) { VMExternRefActivationsTable::insert_precise_stack_root( - &mut precise_stack_roots, + &mut externref_activations_table.precise_stack_roots, r, ); } @@ -941,10 +923,10 @@ pub unsafe fn gc( // would free those missing roots while they are still in use, leading to // use-after-free. if found_canary { - externref_activations_table.sweep(&mut precise_stack_roots); + externref_activations_table.sweep(); } else { log::warn!("did not find stack canary; skipping GC sweep"); - precise_stack_roots.clear(); + externref_activations_table.precise_stack_roots.clear(); } log::debug!("end GC"); @@ -972,12 +954,12 @@ mod tests { #[test] fn ref_count_is_at_correct_offset() { let s = "hi"; - let s: &dyn Any = &s as _; - let s: *const dyn Any = s as _; - let s: *mut dyn Any = s as _; + let s: &(dyn Any + Send + Sync) = &s as _; + let s: *const (dyn Any + Send + Sync) = s as _; + let s: *mut (dyn Any + Send + Sync) = s as _; let extern_data = VMExternData { - ref_count: UnsafeCell::new(0), + ref_count: AtomicUsize::new(0), value_ptr: NonNull::new(s).unwrap(), }; @@ -997,7 +979,7 @@ mod tests { let table = VMExternRefActivationsTable::new(); let table_ptr = &table as *const _; - let next_ptr = &table.next as *const _; + let next_ptr = &table.alloc.next as *const _; let actual_offset = (next_ptr as usize) - (table_ptr as usize); @@ -1024,7 +1006,7 @@ mod tests { let table = VMExternRefActivationsTable::new(); let table_ptr = &table as *const _; - let end_ptr = &table.end as *const _; + let end_ptr = &table.alloc.end as *const _; let actual_offset = (end_ptr as usize) - (table_ptr as usize); diff --git a/crates/runtime/src/helpers.c b/crates/runtime/src/helpers.c index 22e3d26211cf..da3db5824247 100644 --- a/crates/runtime/src/helpers.c +++ b/crates/runtime/src/helpers.c @@ -16,14 +16,15 @@ int RegisterSetjmp( void **buf_storage, - void (*body)(void*), - void *payload) { + void (*body)(void*, void*), + void *payload, + void *callee) { platform_jmp_buf buf; if (platform_setjmp(buf) != 0) { return 0; } *buf_storage = &buf; - body(payload); + body(payload, callee); return 1; } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index f2143681fa47..8c1ea559eadf 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -3,7 +3,7 @@ //! `InstanceHandle` is a reference-counting handle for an `Instance`. use crate::export::Export; -use crate::externref::{ModuleInfoLookup, VMExternRefActivationsTable}; +use crate::externref::VMExternRefActivationsTable; use crate::memory::{Memory, RuntimeMemoryCreator}; use crate::table::{Table, TableElement}; use crate::traphandlers::Trap; @@ -12,24 +12,21 @@ use crate::vmcontext::{ VMGlobalDefinition, VMGlobalImport, VMInterrupts, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, }; -use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable}; -use indexmap::IndexMap; +use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable, Store}; use memoffset::offset_of; use more_asserts::assert_lt; use std::alloc::Layout; use std::any::Any; -use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; use std::hash::Hash; use std::ptr::NonNull; -use std::rc::Rc; use std::sync::Arc; use std::{mem, ptr, slice}; use wasmtime_environ::entity::{packed_option::ReservedValue, EntityRef, EntitySet, PrimaryMap}; use wasmtime_environ::wasm::{ DataIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, ElemIndex, EntityIndex, - FuncIndex, GlobalIndex, MemoryIndex, TableElementType, TableIndex, + FuncIndex, GlobalIndex, MemoryIndex, TableElementType, TableIndex, WasmType, }; use wasmtime_environ::{ir, Module, VMOffsets}; @@ -41,7 +38,7 @@ pub use allocator::*; /// /// An instance can be created with a resource limiter so that hosts can take into account /// non-WebAssembly resource usage to determine if a linear memory or table should grow. -pub trait ResourceLimiter { +pub trait ResourceLimiter: Send + Sync + 'static { /// Notifies the resource limiter that an instance's linear memory has been requested to grow. /// /// * `current` is the current size of the linear memory in WebAssembly page units. @@ -53,7 +50,7 @@ pub trait ResourceLimiter { /// This function should return `true` to indicate that the growing operation is permitted or /// `false` if not permitted. Returning `true` when a maximum has been exceeded will have no /// effect as the linear memory will not grow. - fn memory_growing(&self, current: u32, desired: u32, maximum: Option) -> bool; + fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option) -> bool; /// Notifies the resource limiter that an instance's table has been requested to grow. /// @@ -65,7 +62,7 @@ pub trait ResourceLimiter { /// This function should return `true` to indicate that the growing operation is permitted or /// `false` if not permitted. Returning `true` when a maximum has been exceeded will have no /// effect as the table will not grow. - fn table_growing(&self, current: u32, desired: u32, maximum: Option) -> bool; + fn table_growing(&mut self, current: u32, desired: u32, maximum: Option) -> bool; /// The maximum number of instances that can be created for a `Store`. /// @@ -83,10 +80,6 @@ pub trait ResourceLimiter { fn memories(&self) -> usize; } -/// Runtime representation of an instance value, which erases all `Instance` -/// information since instances are just a collection of values. -pub type RuntimeInstance = Rc>; - /// A WebAssembly instance. /// /// This is repr(C) to ensure that the vmctx field is last. @@ -106,14 +99,14 @@ pub(crate) struct Instance { /// Stores the dropped passive element segments in this instantiation by index. /// If the index is present in the set, the segment has been dropped. - dropped_elements: RefCell>, + dropped_elements: EntitySet, /// Stores the dropped passive data segments in this instantiation by index. /// If the index is present in the set, the segment has been dropped. - dropped_data: RefCell>, + dropped_data: EntitySet, /// Hosts can store arbitrary per-instance information here. - host_state: Box, + host_state: Box, /// Additional context used by compiled wasm code. This field is last, and /// represents a dynamically-sized array that extends beyond the nominal @@ -242,16 +235,8 @@ impl Instance { } /// Return the indexed `VMGlobalDefinition`. - fn global(&self, index: DefinedGlobalIndex) -> VMGlobalDefinition { - unsafe { *self.global_ptr(index) } - } - - /// Set the indexed global to `VMGlobalDefinition`. - #[allow(dead_code)] - fn set_global(&self, index: DefinedGlobalIndex, global: VMGlobalDefinition) { - unsafe { - *self.global_ptr(index) = global; - } + fn global(&self, index: DefinedGlobalIndex) -> &VMGlobalDefinition { + unsafe { &*self.global_ptr(index) } } /// Return the indexed `VMGlobalDefinition`. @@ -295,17 +280,35 @@ impl Instance { unsafe { self.vmctx_plus_offset(self.offsets.vmctx_externref_activations_table()) } } - /// Return a pointer to the `ModuleInfoLookup`. - pub fn module_info_lookup(&self) -> *mut *const dyn ModuleInfoLookup { - unsafe { self.vmctx_plus_offset(self.offsets.vmctx_module_info_lookup()) } + /// Gets a pointer to this instance's `Store` which was originally + /// configured on creation. + /// + /// # Panics + /// + /// This will panic if the originally configured store was `None`. That can + /// happen for host functions so host functions can't be queried what their + /// original `Store` was since it's just retained as null (since host + /// functions are shared amongst threads and don't all share the same + /// store). + #[inline] + pub fn store(&self) -> *mut dyn Store { + let ptr = unsafe { *self.vmctx_plus_offset::<*mut dyn Store>(self.offsets.vmctx_store()) }; + assert!(!ptr.is_null()); + ptr + } + + pub unsafe fn set_store(&mut self, store: *mut dyn Store) { + *self.vmctx_plus_offset(self.offsets.vmctx_store()) = store; } /// Return a reference to the vmctx used by compiled wasm code. + #[inline] pub fn vmctx(&self) -> &VMContext { &self.vmctx } /// Return a raw pointer to the vmctx used by compiled wasm code. + #[inline] pub fn vmctx_ptr(&self) -> *mut VMContext { self.vmctx() as *const VMContext as *mut VMContext } @@ -423,13 +426,18 @@ impl Instance { /// /// Returns `None` if memory can't be grown by the specified amount /// of pages. - pub(crate) fn memory_grow(&self, memory_index: DefinedMemoryIndex, delta: u32) -> Option { + pub(crate) fn memory_grow( + &mut self, + memory_index: DefinedMemoryIndex, + delta: u32, + ) -> Option { + let limiter = unsafe { (*self.store()).limiter() }; let memory = self .memories - .get(memory_index) + .get_mut(memory_index) .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index())); - let result = unsafe { memory.grow(delta) }; + let result = unsafe { memory.grow(delta, limiter) }; // Keep current the VMContext pointers used by compiled wasm code. self.set_memory(memory_index, self.memories[memory_index].vmmemory()); @@ -446,12 +454,12 @@ impl Instance { /// This and `imported_memory_size` are currently unsafe because they /// dereference the memory import's pointers. pub(crate) unsafe fn imported_memory_grow( - &self, + &mut self, memory_index: MemoryIndex, delta: u32, ) -> Option { let import = self.imported_memory(memory_index); - let foreign_instance = (&*import.vmctx).instance(); + let foreign_instance = (*import.vmctx).instance_mut(); let foreign_memory = &*import.from; let foreign_index = foreign_instance.memory_index(foreign_memory); @@ -480,9 +488,8 @@ impl Instance { foreign_instance.memory_size(foreign_index) } - pub(crate) fn table_element_type(&self, table_index: TableIndex) -> TableElementType { - let table = self.get_table(table_index); - table.element_type() + pub(crate) fn table_element_type(&mut self, table_index: TableIndex) -> TableElementType { + unsafe { (*self.get_table(table_index)).element_type() } } /// Grow table by the specified amount of elements, filling them with @@ -491,7 +498,7 @@ impl Instance { /// Returns `None` if table can't be grown by the specified amount of /// elements, or if `init_value` is the wrong type of table element. pub(crate) fn table_grow( - &self, + &mut self, table_index: TableIndex, delta: u32, init_value: TableElement, @@ -502,17 +509,18 @@ impl Instance { } fn defined_table_grow( - &self, + &mut self, table_index: DefinedTableIndex, delta: u32, init_value: TableElement, ) -> Option { + let limiter = unsafe { (*self.store()).limiter() }; let table = self .tables - .get(table_index) + .get_mut(table_index) .unwrap_or_else(|| panic!("no table for index {}", table_index.index())); - let result = unsafe { table.grow(delta, init_value) }; + let result = unsafe { table.grow(delta, init_value, limiter) }; // Keep the `VMContext` pointers used by compiled Wasm code up to // date. @@ -521,36 +529,6 @@ impl Instance { result } - pub(crate) fn defined_table_fill( - &self, - table_index: DefinedTableIndex, - dst: u32, - val: TableElement, - len: u32, - ) -> Result<(), Trap> { - self.tables.get(table_index).unwrap().fill(dst, val, len) - } - - // Get table element by index. - fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option { - self.tables - .get(table_index) - .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) - .get(index) - } - - fn table_set( - &self, - table_index: DefinedTableIndex, - index: u32, - val: TableElement, - ) -> Result<(), ()> { - self.tables - .get(table_index) - .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) - .set(index, val) - } - fn alloc_layout(&self) -> Layout { let size = mem::size_of_val(self) .checked_add(usize::try_from(self.offsets.size_of_vmctx()).unwrap()) @@ -584,14 +562,14 @@ impl Instance { index: I, index_map: &HashMap, data: &'a Vec, - dropped: &RefCell>, + dropped: &EntitySet, ) -> &'a [T] where D: AsRef<[T]>, I: EntityRef + Hash, { match index_map.get(&index) { - Some(index) if !dropped.borrow().contains(I::new(*index)) => data[*index].as_ref(), + Some(index) if !dropped.contains(I::new(*index)) => data[*index].as_ref(), _ => &[], } } @@ -604,24 +582,28 @@ impl Instance { /// Returns a `Trap` error when the range within the table is out of bounds /// or the range within the passive element is out of bounds. pub(crate) fn table_init( - &self, + &mut self, table_index: TableIndex, elem_index: ElemIndex, dst: u32, src: u32, len: u32, ) -> Result<(), Trap> { + // TODO: this `clone()` shouldn't be necessary but is used for now to + // inform `rustc` that the lifetime of the elements here are + // disconnected from the lifetime of `self`. + let module = self.module.clone(); let elements = Self::find_passive_segment( elem_index, - &self.module.passive_elements_map, - &self.module.passive_elements, + &module.passive_elements_map, + &module.passive_elements, &self.dropped_elements, ); self.table_init_segment(table_index, elements, dst, src, len) } pub(crate) fn table_init_segment( - &self, + &mut self, table_index: TableIndex, elements: &[FuncIndex], dst: u32, @@ -630,7 +612,7 @@ impl Instance { ) -> Result<(), Trap> { // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init - let table = self.get_table(table_index); + let table = unsafe { &mut *self.get_table(table_index) }; let elements = match elements .get(usize::try_from(src).unwrap()..) @@ -665,19 +647,22 @@ impl Instance { } /// Drop an element. - pub(crate) fn elem_drop(&self, elem_index: ElemIndex) { + pub(crate) fn elem_drop(&mut self, elem_index: ElemIndex) { // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-elem-drop if let Some(index) = self.module.passive_elements_map.get(&elem_index) { - self.dropped_elements - .borrow_mut() - .insert(ElemIndex::new(*index)); + self.dropped_elements.insert(ElemIndex::new(*index)); } // Note that we don't check that we actually removed a segment because // dropping a non-passive segment is a no-op (not a trap). } + /// Get a locally-defined memory. + pub(crate) fn get_defined_memory(&mut self, index: DefinedMemoryIndex) -> *mut Memory { + ptr::addr_of_mut!(self.memories[index]) + } + /// Do a `memory.copy` /// /// # Errors @@ -685,7 +670,7 @@ impl Instance { /// Returns a `Trap` error when the source or destination ranges are out of /// bounds. pub(crate) fn memory_copy( - &self, + &mut self, dst_index: MemoryIndex, dst: u32, src_index: MemoryIndex, @@ -784,24 +769,28 @@ impl Instance { /// memory's bounds or if the source range is outside the data segment's /// bounds. pub(crate) fn memory_init( - &self, + &mut self, memory_index: MemoryIndex, data_index: DataIndex, dst: u32, src: u32, len: u32, ) -> Result<(), Trap> { + // TODO: this `clone()` shouldn't be necessary but is used for now to + // inform `rustc` that the lifetime of the elements here are + // disconnected from the lifetime of `self`. + let module = self.module.clone(); let data = Self::find_passive_segment( data_index, - &self.module.passive_data_map, - &self.module.passive_data, + &module.passive_data_map, + &module.passive_data, &self.dropped_data, ); self.memory_init_segment(memory_index, &data, dst, src, len) } pub(crate) fn memory_init_segment( - &self, + &mut self, memory_index: MemoryIndex, data: &[u8], dst: u32, @@ -834,11 +823,9 @@ impl Instance { } /// Drop the given data segment, truncating its length to zero. - pub(crate) fn data_drop(&self, data_index: DataIndex) { + pub(crate) fn data_drop(&mut self, data_index: DataIndex) { if let Some(index) = self.module.passive_data_map.get(&data_index) { - self.dropped_data - .borrow_mut() - .insert(DataIndex::new(*index)); + self.dropped_data.insert(DataIndex::new(*index)); } // Note that we don't check that we actually removed a segment because @@ -847,7 +834,7 @@ impl Instance { /// Get a table by index regardless of whether it is locally-defined or an /// imported, foreign table. - pub(crate) fn get_table(&self, table_index: TableIndex) -> &Table { + pub(crate) fn get_table(&mut self, table_index: TableIndex) -> *mut Table { if let Some(defined_table_index) = self.module.defined_table_index(table_index) { self.get_defined_table(defined_table_index) } else { @@ -856,33 +843,56 @@ impl Instance { } /// Get a locally-defined table. - pub(crate) fn get_defined_table(&self, index: DefinedTableIndex) -> &Table { - &self.tables[index] + pub(crate) fn get_defined_table(&mut self, index: DefinedTableIndex) -> *mut Table { + ptr::addr_of_mut!(self.tables[index]) } /// Get an imported, foreign table. - pub(crate) fn get_foreign_table(&self, index: TableIndex) -> &Table { + pub(crate) fn get_foreign_table(&mut self, index: TableIndex) -> *mut Table { let import = self.imported_table(index); - let foreign_instance = unsafe { (&mut *(import).vmctx).instance() }; - let foreign_table = unsafe { &mut *(import).from }; + let foreign_instance = unsafe { (*import.vmctx).instance_mut() }; + let foreign_table = unsafe { &*import.from }; let foreign_index = foreign_instance.table_index(foreign_table); - &foreign_instance.tables[foreign_index] + ptr::addr_of_mut!(foreign_instance.tables[foreign_index]) } pub(crate) fn get_defined_table_index_and_instance( - &self, + &mut self, index: TableIndex, - ) -> (DefinedTableIndex, &Instance) { + ) -> (DefinedTableIndex, &mut Instance) { if let Some(defined_table_index) = self.module.defined_table_index(index) { (defined_table_index, self) } else { let import = self.imported_table(index); - let foreign_instance = unsafe { (&mut *(import).vmctx).instance() }; - let foreign_table_def = unsafe { &mut *(import).from }; + let foreign_instance = unsafe { (*import.vmctx).instance_mut() }; + let foreign_table_def = unsafe { &*import.from }; let foreign_table_index = foreign_instance.table_index(foreign_table_def); (foreign_table_index, foreign_instance) } } + + fn drop_globals(&mut self) { + for (idx, global) in self.module.globals.iter() { + let idx = match self.module.defined_global_index(idx) { + Some(idx) => idx, + None => continue, + }; + match global.wasm_ty { + // For now only externref gloabls need to get destroyed + WasmType::ExternRef => {} + _ => continue, + } + unsafe { + drop((*self.global_ptr(idx)).as_externref_mut().take()); + } + } + } +} + +impl Drop for Instance { + fn drop(&mut self) { + self.drop_globals(); + } } /// A handle holding an `Instance` of a WebAssembly module. @@ -891,6 +901,16 @@ pub struct InstanceHandle { instance: *mut Instance, } +// These are only valid if the `Instance` type is send/sync, hence the +// assertion below. +unsafe impl Send for InstanceHandle {} +unsafe impl Sync for InstanceHandle {} + +fn _assert_send_sync() { + fn _assert() {} + _assert::(); +} + impl InstanceHandle { /// Create a new `InstanceHandle` pointing at the instance /// pointed to by the given `VMContext` pointer. @@ -898,6 +918,7 @@ impl InstanceHandle { /// # Safety /// This is unsafe because it doesn't work on just any `VMContext`, it must /// be a `VMContext` allocated as part of an `Instance`. + #[inline] pub unsafe fn from_vmctx(vmctx: *mut VMContext) -> Self { let instance = (&mut *vmctx).instance(); Self { @@ -911,6 +932,7 @@ impl InstanceHandle { } /// Return a raw pointer to the vmctx used by compiled wasm code. + #[inline] pub fn vmctx_ptr(&self) -> *mut VMContext { self.instance().vmctx_ptr() } @@ -944,12 +966,9 @@ impl InstanceHandle { self.instance().memory_index(memory) } - /// Grow memory in this instance by the specified amount of pages. - /// - /// Returns `None` if memory can't be grown by the specified amount - /// of pages. - pub fn memory_grow(&self, memory_index: DefinedMemoryIndex, delta: u32) -> Option { - self.instance().memory_grow(memory_index, delta) + /// Get a memory defined locally within this module. + pub fn get_defined_memory(&mut self, index: DefinedMemoryIndex) -> *mut Memory { + self.instance_mut().get_defined_memory(index) } /// Return the table index for the given `VMTableDefinition` in this instance. @@ -957,81 +976,33 @@ impl InstanceHandle { self.instance().table_index(table) } - /// Grow table in this instance by the specified amount of elements. - /// - /// When the table is successfully grown, returns the original size of the - /// table. - /// - /// Returns `None` if memory can't be grown by the specified amount of pages - /// or if the `init_value` is the incorrect table element type. - pub fn table_grow( - &self, - table_index: TableIndex, - delta: u32, - init_value: TableElement, - ) -> Option { - self.instance().table_grow(table_index, delta, init_value) + /// Get a table defined locally within this module. + pub fn get_defined_table(&mut self, index: DefinedTableIndex) -> *mut Table { + self.instance_mut().get_defined_table(index) } - /// Grow table in this instance by the specified amount of elements. - /// - /// When the table is successfully grown, returns the original size of the - /// table. - /// - /// Returns `None` if memory can't be grown by the specified amount of pages - /// or if the `init_value` is the incorrect table element type. - pub fn defined_table_grow( - &self, - table_index: DefinedTableIndex, - delta: u32, - init_value: TableElement, - ) -> Option { - self.instance() - .defined_table_grow(table_index, delta, init_value) + /// Return a reference to the contained `Instance`. + #[inline] + pub(crate) fn instance(&self) -> &Instance { + unsafe { &*(self.instance as *const Instance) } } - /// Get table element reference. - /// - /// Returns `None` if index is out of bounds. - pub fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option { - self.instance().table_get(table_index, index) + pub(crate) fn instance_mut(&mut self) -> &mut Instance { + unsafe { &mut *self.instance } } - /// Set table element reference. - /// - /// Returns an error if the index is out of bounds - pub fn table_set( - &self, - table_index: DefinedTableIndex, - index: u32, - val: TableElement, - ) -> Result<(), ()> { - self.instance().table_set(table_index, index, val) + /// Returns the `Store` pointer that was stored on creation + #[inline] + pub fn store(&self) -> *mut dyn Store { + self.instance().store() } - /// Fill a region of the table. + /// Configure the `*mut dyn Store` internal pointer after-the-fact. /// - /// Returns an error if the region is out of bounds or val is not of the - /// correct type. - pub fn defined_table_fill( - &self, - table_index: DefinedTableIndex, - dst: u32, - val: TableElement, - len: u32, - ) -> Result<(), Trap> { - self.instance() - .defined_table_fill(table_index, dst, val, len) - } - - /// Get a table defined locally within this module. - pub fn get_defined_table(&self, index: DefinedTableIndex) -> &Table { - self.instance().get_defined_table(index) - } - - /// Return a reference to the contained `Instance`. - pub(crate) fn instance(&self) -> &Instance { - unsafe { &*(self.instance as *const Instance) } + /// This is provided for the original `Store` itself to configure the first + /// self-pointer after the original `Box` has been initialized. + pub unsafe fn set_store(&mut self, store: *mut dyn Store) { + self.instance_mut().set_store(store); } /// Returns a clone of this instance. diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs index c91f89a74ff4..7c935288fbe0 100644 --- a/crates/runtime/src/instance/allocator.rs +++ b/crates/runtime/src/instance/allocator.rs @@ -1,4 +1,3 @@ -use crate::externref::{ModuleInfoLookup, VMExternRefActivationsTable, EMPTY_MODULE_LOOKUP}; use crate::imports::Imports; use crate::instance::{Instance, InstanceHandle, ResourceLimiter, RuntimeMemoryCreator}; use crate::memory::{DefaultMemoryCreator, Memory}; @@ -6,16 +5,15 @@ use crate::table::Table; use crate::traphandlers::Trap; use crate::vmcontext::{ VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, - VMGlobalDefinition, VMGlobalImport, VMInterrupts, VMMemoryImport, VMSharedSignatureIndex, - VMTableImport, + VMGlobalDefinition, VMGlobalImport, VMMemoryImport, VMSharedSignatureIndex, VMTableImport, }; +use crate::Store; use anyhow::Result; use std::alloc; use std::any::Any; -use std::cell::RefCell; use std::convert::TryFrom; +use std::marker; use std::ptr::{self, NonNull}; -use std::rc::Rc; use std::slice; use std::sync::Arc; use thiserror::Error; @@ -49,19 +47,23 @@ pub struct InstanceAllocationRequest<'a> { pub shared_signatures: SharedSignatures<'a>, /// The host state to associate with the instance. - pub host_state: Box, + pub host_state: Box, - /// The pointer to the VM interrupts structure to use for the instance. - pub interrupts: *const VMInterrupts, - - /// The pointer to the reference activations table to use for the instance. - pub externref_activations_table: *mut VMExternRefActivationsTable, - - /// The pointer to the module info lookup to use for the instance. - pub module_info_lookup: Option<*const dyn ModuleInfoLookup>, - - /// The resource limiter to use for the instance. - pub limiter: Option<&'a Rc>, + /// A pointer to the "store" for this instance to be allocated. The store + /// correlates with the `Store` in wasmtime itself, and lots of contextual + /// information about the execution of wasm can be learned through the store. + /// + /// Note that this is a raw pointer and has a static lifetime, both of which + /// are a bit of a lie. This is done purely so a store can learn about + /// itself when it gets called as a host function, and additionally so this + /// runtime can access internals as necessary (such as the + /// VMExternRefActivationsTable or the ResourceLimiter). + /// + /// Note that this ends up being a self-pointer to the instance when stored. + /// The reason is that the instance itself is then stored within the store. + /// We use a number of `PhantomPinned` declarations to indicate this to the + /// compiler. More info on this in `wasmtime/src/store.rs` + pub store: Option<*mut dyn Store>, } /// An link error while instantiating a module. @@ -141,7 +143,8 @@ pub unsafe trait InstanceAllocator: Send + Sync { /// This method is only safe to call immediately after an instance has been allocated. unsafe fn initialize( &self, - handle: &InstanceHandle, + handle: &mut InstanceHandle, + module: &Module, is_bulk_memory: bool, ) -> Result<(), InstantiationError>; @@ -232,9 +235,12 @@ fn get_table_init_start( } } -fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError> { - for init in &instance.module.table_initializers { - let table = instance.get_table(init.table_index); +fn check_table_init_bounds( + instance: &mut Instance, + module: &Module, +) -> Result<(), InstantiationError> { + for init in &module.table_initializers { + let table = unsafe { &*instance.get_table(init.table_index) }; let start = get_table_init_start(init, instance)?; let start = usize::try_from(start).unwrap(); let end = start.checked_add(init.elements.len()); @@ -254,8 +260,8 @@ fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError Ok(()) } -fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> { - for init in &instance.module.table_initializers { +fn initialize_tables(instance: &mut Instance, module: &Module) -> Result<(), InstantiationError> { + for init in &module.table_initializers { instance .table_init_segment( init.table_index, @@ -318,7 +324,7 @@ fn check_memory_init_bounds( } fn initialize_memories( - instance: &Instance, + instance: &mut Instance, initializers: &[MemoryInitializer], ) -> Result<(), InstantiationError> { for init in initializers { @@ -336,8 +342,8 @@ fn initialize_memories( Ok(()) } -fn check_init_bounds(instance: &Instance) -> Result<(), InstantiationError> { - check_table_init_bounds(instance)?; +fn check_init_bounds(instance: &mut Instance, module: &Module) -> Result<(), InstantiationError> { + check_table_init_bounds(instance, module)?; match &instance.module.memory_initialization { MemoryInitialization::Paged { out_of_bounds, .. } => { @@ -356,7 +362,8 @@ fn check_init_bounds(instance: &Instance) -> Result<(), InstantiationError> { } fn initialize_instance( - instance: &Instance, + instance: &mut Instance, + module: &Module, is_bulk_memory: bool, ) -> Result<(), InstantiationError> { // If bulk memory is not enabled, bounds check the data and element segments before @@ -364,14 +371,14 @@ fn initialize_instance( // in-order and side effects are observed up to the point of an out-of-bounds // initializer, so the early checking is not desired. if !is_bulk_memory { - check_init_bounds(instance)?; + check_init_bounds(instance, module)?; } // Initialize the tables - initialize_tables(instance)?; + initialize_tables(instance, module)?; // Initialize the memories - match &instance.module.memory_initialization { + match &module.memory_initialization { MemoryInitialization::Paged { map, out_of_bounds } => { for (index, pages) in map { let memory = instance.memory(index); @@ -404,12 +411,14 @@ fn initialize_instance( Ok(()) } -unsafe fn initialize_vmcontext(instance: &Instance, req: InstanceAllocationRequest) { - let module = &instance.module; +unsafe fn initialize_vmcontext(instance: &mut Instance, req: InstanceAllocationRequest) { + if let Some(store) = req.store { + *instance.interrupts() = (*store).vminterrupts(); + *instance.externref_activations_table() = (*store).externref_activations_table().0; + instance.set_store(store); + } - *instance.interrupts() = req.interrupts; - *instance.externref_activations_table() = req.externref_activations_table; - *instance.module_info_lookup() = req.module_info_lookup.unwrap_or(&EMPTY_MODULE_LOOKUP); + let module = &instance.module; // Initialize shared signatures let mut ptr = instance.signature_ids_ptr(); @@ -520,17 +529,24 @@ unsafe fn initialize_vmcontext_globals(instance: &Instance) { let from = if let Some(def_x) = module.defined_global_index(x) { instance.global(def_x) } else { - *instance.imported_global(x).from + &*instance.imported_global(x).from }; - *to = from; + // Globals of type `externref` need to manage the reference + // count as values move between globals, everything else is just + // copy-able bits. + match global.wasm_ty { + WasmType::ExternRef => *(*to).as_externref_mut() = from.as_externref().clone(), + _ => ptr::copy_nonoverlapping(from, to, 1), + } } GlobalInit::RefFunc(f) => { *(*to).as_anyfunc_mut() = instance.get_caller_checked_anyfunc(f).unwrap() as *const VMCallerCheckedAnyfunc; } GlobalInit::RefNullConst => match global.wasm_ty { - WasmType::FuncRef => *(*to).as_anyfunc_mut() = ptr::null(), - WasmType::ExternRef => *(*to).as_externref_mut() = None, + // `VMGlobalDefinition::new()` already zeroed out the bits + WasmType::FuncRef => {} + WasmType::ExternRef => {} ty => panic!("unsupported reference type for global: {:?}", ty), }, GlobalInit::Import => panic!("locally-defined global initialized as import"), @@ -545,6 +561,17 @@ pub struct OnDemandInstanceAllocator { stack_size: usize, } +// rustc is quite strict with the lifetimes when dealing with mutable borrows, +// so this is a little helper to get a shorter lifetime on `Option<&mut T>` +fn borrow_limiter<'a>( + limiter: &'a mut Option<&mut dyn ResourceLimiter>, +) -> Option<&'a mut dyn ResourceLimiter> { + match limiter { + Some(limiter) => Some(&mut **limiter), + None => None, + } +} + impl OnDemandInstanceAllocator { /// Creates a new on-demand instance allocator. pub fn new(mem_creator: Option>, stack_size: usize) -> Self { @@ -556,13 +583,16 @@ impl OnDemandInstanceAllocator { fn create_tables( module: &Module, - limiter: Option<&Rc>, + mut limiter: Option<&mut dyn ResourceLimiter>, ) -> Result, InstantiationError> { let num_imports = module.num_imported_tables; let mut tables: PrimaryMap = PrimaryMap::with_capacity(module.table_plans.len() - num_imports); for table in &module.table_plans.values().as_slice()[num_imports..] { - tables.push(Table::new_dynamic(table, limiter).map_err(InstantiationError::Resource)?); + tables.push( + Table::new_dynamic(table, borrow_limiter(&mut limiter)) + .map_err(InstantiationError::Resource)?, + ); } Ok(tables) } @@ -570,7 +600,7 @@ impl OnDemandInstanceAllocator { fn create_memories( &self, module: &Module, - limiter: Option<&Rc>, + mut limiter: Option<&mut dyn ResourceLimiter>, ) -> Result, InstantiationError> { let creator = self .mem_creator @@ -581,7 +611,7 @@ impl OnDemandInstanceAllocator { PrimaryMap::with_capacity(module.memory_plans.len() - num_imports); for plan in &module.memory_plans.values().as_slice()[num_imports..] { memories.push( - Memory::new_dynamic(plan, creator, limiter) + Memory::new_dynamic(plan, creator, borrow_limiter(&mut limiter)) .map_err(InstantiationError::Resource)?, ); } @@ -603,23 +633,24 @@ unsafe impl InstanceAllocator for OnDemandInstanceAllocator { &self, mut req: InstanceAllocationRequest, ) -> Result { - let memories = self.create_memories(&req.module, req.limiter)?; - let tables = Self::create_tables(&req.module, req.limiter)?; + let mut limiter = req.store.and_then(|s| (*s).limiter()); + let memories = self.create_memories(&req.module, borrow_limiter(&mut limiter))?; + let tables = Self::create_tables(&req.module, borrow_limiter(&mut limiter))?; let host_state = std::mem::replace(&mut req.host_state, Box::new(())); - let handle = { + let mut handle = { let instance = Instance { module: req.module.clone(), offsets: VMOffsets::new(std::mem::size_of::<*const u8>() as u8, &req.module), memories, tables, - dropped_elements: RefCell::new(EntitySet::with_capacity( - req.module.passive_elements.len(), - )), - dropped_data: RefCell::new(EntitySet::with_capacity(req.module.passive_data.len())), + dropped_elements: EntitySet::with_capacity(req.module.passive_elements.len()), + dropped_data: EntitySet::with_capacity(req.module.passive_data.len()), host_state, - vmctx: VMContext {}, + vmctx: VMContext { + _marker: marker::PhantomPinned, + }, }; let layout = instance.alloc_layout(); let instance_ptr = alloc::alloc(layout) as *mut Instance; @@ -632,17 +663,18 @@ unsafe impl InstanceAllocator for OnDemandInstanceAllocator { } }; - initialize_vmcontext(handle.instance(), req); + initialize_vmcontext(handle.instance_mut(), req); Ok(handle) } unsafe fn initialize( &self, - handle: &InstanceHandle, + handle: &mut InstanceHandle, + module: &Module, is_bulk_memory: bool, ) -> Result<(), InstantiationError> { - initialize_instance(handle.instance(), is_bulk_memory) + initialize_instance(handle.instance_mut(), module, is_bulk_memory) } unsafe fn deallocate(&self, handle: &InstanceHandle) { diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs index 7a17b2b143df..2395abfa55f7 100644 --- a/crates/runtime/src/instance/allocator/pooling.rs +++ b/crates/runtime/src/instance/allocator/pooling.rs @@ -7,6 +7,7 @@ //! Using the pooling instance allocator can speed up module instantiation //! when modules can be constrained based on configurable limits. +use super::borrow_limiter; use super::{ initialize_instance, initialize_vmcontext, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, InstantiationError, ResourceLimiter, @@ -14,11 +15,10 @@ use super::{ use crate::{instance::Instance, Memory, Mmap, Table, VMContext}; use anyhow::{anyhow, bail, Context, Result}; use rand::Rng; -use std::cell::RefCell; use std::cmp::min; use std::convert::TryFrom; +use std::marker; use std::mem; -use std::rc::Rc; use std::sync::{Arc, Mutex}; use wasmtime_environ::{ entity::{EntitySet, PrimaryMap}, @@ -296,6 +296,7 @@ struct InstancePool { free_list: Mutex>, memories: MemoryPool, tables: TablePool, + empty_module: Arc, } impl InstancePool { @@ -340,12 +341,12 @@ impl InstancePool { free_list: Mutex::new((0..max_instances).collect()), memories: MemoryPool::new(module_limits, instance_limits)?, tables: TablePool::new(module_limits, instance_limits)?, + empty_module: Arc::new(Module::default()), }; // Use a default module to initialize the instances to start - let module = Arc::new(Module::default()); for i in 0..instance_limits.count as usize { - pool.initialize(i, &module); + pool.initialize(i); } Ok(pool) @@ -356,7 +357,7 @@ impl InstancePool { &mut *(self.mapping.as_mut_ptr().add(index * self.instance_size) as *mut Instance) } - fn initialize(&self, index: usize, module: &Arc) { + fn initialize(&self, index: usize) { unsafe { let instance = self.instance(index); @@ -364,14 +365,16 @@ impl InstancePool { std::ptr::write( instance as _, Instance { - module: module.clone(), + module: self.empty_module.clone(), offsets: self.offsets, memories: PrimaryMap::with_capacity(self.offsets.num_defined_memories as usize), tables: PrimaryMap::with_capacity(self.offsets.num_defined_tables as usize), - dropped_elements: RefCell::new(EntitySet::new()), - dropped_data: RefCell::new(EntitySet::new()), + dropped_elements: EntitySet::new(), + dropped_data: EntitySet::new(), host_state: Box::new(()), - vmctx: VMContext {}, + vmctx: VMContext { + _marker: marker::PhantomPinned, + }, }, ); } @@ -391,18 +394,19 @@ impl InstancePool { ); instance.host_state = std::mem::replace(&mut req.host_state, Box::new(())); + let mut limiter = req.store.and_then(|s| (*s).limiter()); Self::set_instance_memories( instance, self.memories.get(index), self.memories.max_wasm_pages, - req.limiter, + borrow_limiter(&mut limiter), )?; Self::set_instance_tables( instance, - self.tables.get(index), + self.tables.get(index).map(|x| x as *mut usize), self.tables.max_elements, - req.limiter, + borrow_limiter(&mut limiter), )?; initialize_vmcontext(instance, req); @@ -452,7 +456,7 @@ impl InstancePool { // Decommit any linear memories that were used for (memory, base) in instance.memories.values_mut().zip(self.memories.get(index)) { - let memory = mem::take(memory); + let mut memory = mem::take(memory); debug_assert!(memory.is_static()); // Reset any faulted guard pages as the physical memory may be reused for another instance in the future @@ -460,14 +464,15 @@ impl InstancePool { memory .reset_guard_pages() .expect("failed to reset guard pages"); + drop(&mut memory); // require mutable on all platforms, not just uffd - let size = (memory.size() * WASM_PAGE_SIZE) as usize; + let size = (memory.size() as usize) * (WASM_PAGE_SIZE as usize); drop(memory); decommit_memory_pages(base, size).expect("failed to decommit linear memory pages"); } instance.memories.clear(); - instance.dropped_data.borrow_mut().clear(); + instance.dropped_data.clear(); // Decommit any tables that were used for (table, base) in instance.tables.values_mut().zip(self.tables.get(index)) { @@ -484,11 +489,21 @@ impl InstancePool { } instance.tables.clear(); - instance.dropped_elements.borrow_mut().clear(); + instance.dropped_elements.clear(); + + // Drop all `global` values which need a destructor, such as externref + // values which now need their reference count dropped. + instance.drop_globals(); // Drop any host state instance.host_state = Box::new(()); + // And finally reset the module/offsets back to their original. This + // should put everything back in a relatively pristine state for each + // fresh allocation later on. + instance.module = self.empty_module.clone(); + instance.offsets = self.offsets; + self.free_list.lock().unwrap().push(index); } @@ -496,7 +511,7 @@ impl InstancePool { instance: &mut Instance, mut memories: impl Iterator, max_pages: u32, - limiter: Option<&Rc>, + mut limiter: Option<&mut dyn ResourceLimiter>, ) -> Result<(), InstantiationError> { let module = instance.module.as_ref(); @@ -505,30 +520,34 @@ impl InstancePool { for plan in (&module.memory_plans.values().as_slice()[module.num_imported_memories..]).iter() { + let memory = unsafe { + std::slice::from_raw_parts_mut( + memories.next().unwrap(), + (max_pages as usize) * (WASM_PAGE_SIZE as usize), + ) + }; instance.memories.push( Memory::new_static( plan, - memories.next().unwrap(), - max_pages, + memory, commit_memory_pages, - limiter, + borrow_limiter(&mut limiter), ) .map_err(InstantiationError::Resource)?, ); } - let mut dropped_data = instance.dropped_data.borrow_mut(); - debug_assert!(dropped_data.is_empty()); - dropped_data.resize(module.passive_data.len()); + debug_assert!(instance.dropped_data.is_empty()); + instance.dropped_data.resize(module.passive_data.len()); Ok(()) } fn set_instance_tables( instance: &mut Instance, - mut tables: impl Iterator, + mut tables: impl Iterator, max_elements: u32, - limiter: Option<&Rc>, + mut limiter: Option<&mut dyn ResourceLimiter>, ) -> Result<(), InstantiationError> { let module = instance.module.as_ref(); @@ -537,18 +556,23 @@ impl InstancePool { for plan in (&module.table_plans.values().as_slice()[module.num_imported_tables..]).iter() { let base = tables.next().unwrap(); - commit_table_pages(base, max_elements as usize * mem::size_of::<*mut u8>()) - .map_err(InstantiationError::Resource)?; + commit_table_pages( + base as *mut u8, + max_elements as usize * mem::size_of::<*mut u8>(), + ) + .map_err(InstantiationError::Resource)?; + let table = unsafe { std::slice::from_raw_parts_mut(base, max_elements as usize) }; instance.tables.push( - Table::new_static(plan, base as _, max_elements, limiter) + Table::new_static(plan, table, borrow_limiter(&mut limiter)) .map_err(InstantiationError::Resource)?, ); } - let mut dropped_elements = instance.dropped_elements.borrow_mut(); - debug_assert!(dropped_elements.is_empty()); - dropped_elements.resize(module.passive_elements.len()); + debug_assert!(instance.dropped_elements.is_empty()); + instance + .dropped_elements + .resize(module.passive_elements.len()); Ok(()) } @@ -595,7 +619,7 @@ impl MemoryPool { } // The maximum module memory page count cannot exceed the memory reservation size - if (module_limits.memory_pages * WASM_PAGE_SIZE) as u64 + if u64::from(module_limits.memory_pages) * u64::from(WASM_PAGE_SIZE) > instance_limits.memory_reservation_size { bail!( @@ -957,21 +981,22 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { unsafe fn initialize( &self, - handle: &InstanceHandle, + handle: &mut InstanceHandle, + module: &Module, is_bulk_memory: bool, ) -> Result<(), InstantiationError> { - let instance = handle.instance(); + let instance = handle.instance_mut(); cfg_if::cfg_if! { if #[cfg(all(feature = "uffd", target_os = "linux"))] { - match &instance.module.memory_initialization { + match &module.memory_initialization { wasmtime_environ::MemoryInitialization::Paged{ out_of_bounds, .. } => { if !is_bulk_memory { - super::check_init_bounds(instance)?; + super::check_init_bounds(instance, module)?; } // Initialize the tables - super::initialize_tables(instance)?; + super::initialize_tables(instance, module)?; // Don't initialize the memory; the fault handler will back the pages when accessed @@ -984,10 +1009,10 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { Ok(()) }, - _ => initialize_instance(instance, is_bulk_memory) + _ => initialize_instance(instance, module, is_bulk_memory) } } else { - initialize_instance(instance, is_bulk_memory) + initialize_instance(instance, module, is_bulk_memory) } } } @@ -1395,10 +1420,7 @@ mod test { }, shared_signatures: VMSharedSignatureIndex::default().into(), host_state: Box::new(()), - interrupts: std::ptr::null(), - externref_activations_table: std::ptr::null_mut(), - module_info_lookup: None, - limiter: None, + store: None, }, ) .expect("allocation should succeed"), @@ -1420,10 +1442,7 @@ mod test { }, shared_signatures: VMSharedSignatureIndex::default().into(), host_state: Box::new(()), - interrupts: std::ptr::null(), - externref_activations_table: std::ptr::null_mut(), - module_info_lookup: None, - limiter: None, + store: None, }, ) { Err(InstantiationError::Limit(3)) => {} diff --git a/crates/runtime/src/instance/allocator/pooling/uffd.rs b/crates/runtime/src/instance/allocator/pooling/uffd.rs index 43ba9a654a9c..5357d0ffc3ff 100644 --- a/crates/runtime/src/instance/allocator/pooling/uffd.rs +++ b/crates/runtime/src/instance/allocator/pooling/uffd.rs @@ -130,7 +130,7 @@ fn reset_guard_page(addr: *mut u8, len: usize) -> Result<()> { } /// Represents a location of a page fault within monitored regions of memory. -enum FaultLocation<'a> { +enum FaultLocation { /// The address location is in a WebAssembly linear memory page. /// The fault handler will copy the pages from initialization data if necessary. MemoryPage { @@ -139,7 +139,7 @@ enum FaultLocation<'a> { /// The length of the page being accessed. len: usize, /// The instance related to the memory page that was accessed. - instance: &'a Instance, + instance: *mut Instance, /// The index of the memory that was accessed. memory_index: DefinedMemoryIndex, /// The Wasm page index to initialize if the access was not a guard page. @@ -194,9 +194,9 @@ impl FaultLocator { /// /// If the assumption holds true, accessing the instance data from the handler thread /// should, in theory, be safe. - unsafe fn get_instance(&self, index: usize) -> &Instance { + unsafe fn get_instance(&self, index: usize) -> *mut Instance { debug_assert!(index < self.max_instances); - &*((self.instances_start + (index * self.instance_size)) as *const Instance) + (self.instances_start + (index * self.instance_size)) as *mut Instance } unsafe fn locate(&self, addr: usize) -> Option { @@ -208,7 +208,7 @@ impl FaultLocator { let page_index = (addr - memory_start) / WASM_PAGE_SIZE; let instance = self.get_instance(index / self.max_memories); - let init_page_index = instance.memories.get(memory_index).and_then(|m| { + let init_page_index = (*instance).memories.get(memory_index).and_then(|m| { if page_index < m.size() as usize { Some(page_index) } else { @@ -310,13 +310,13 @@ unsafe fn handle_page_fault( match page_index { Some(page_index) => { - initialize_wasm_page(&uffd, instance, page_addr, memory_index, page_index)?; + initialize_wasm_page(&uffd, &*instance, page_addr, memory_index, page_index)?; } None => { log::trace!("out of bounds memory access at {:p}", addr); // Record the guard page fault so the page protection level can be reset later - instance.memories[memory_index].record_guard_page_fault( + (*instance).memories[memory_index].record_guard_page_fault( page_addr, len, reset_guard_page, diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 1c3ce53a3db4..6e5d4f6fb316 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -20,6 +20,8 @@ ) )] +use std::error::Error; + mod export; mod externref; mod imports; @@ -40,15 +42,15 @@ pub use crate::imports::Imports; pub use crate::instance::{ InstanceAllocationRequest, InstanceAllocator, InstanceHandle, InstanceLimits, InstantiationError, LinkError, ModuleLimits, OnDemandInstanceAllocator, - PoolingAllocationStrategy, PoolingInstanceAllocator, ResourceLimiter, RuntimeInstance, + PoolingAllocationStrategy, PoolingInstanceAllocator, ResourceLimiter, }; pub use crate::jit_int::GdbJitImageRegistration; pub use crate::memory::{Memory, RuntimeLinearMemory, RuntimeMemoryCreator}; pub use crate::mmap::Mmap; pub use crate::table::{Table, TableElement}; pub use crate::traphandlers::{ - catch_traps, init_traps, raise_lib_trap, raise_user_trap, resume_panic, with_last_info, - SignalHandler, TlsRestore, Trap, TrapInfo, + catch_traps, init_traps, raise_lib_trap, raise_user_trap, resume_panic, SignalHandler, + TlsRestore, Trap, }; pub use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, VMGlobalDefinition, @@ -80,3 +82,41 @@ pub fn pointer_type() -> wasmtime_environ::ir::Type { unreachable!() } } + +/// Dynamic runtime functionality needed by this crate throughout the execution +/// of a wasm instance. +/// +/// This trait is used to store a raw pointer trait object within each +/// `VMContext`. This raw pointer trait object points back to the +/// `wasmtime::Store` internally but is type-erased so this `wasmtime_runtime` +/// crate doesn't need the entire `wasmtime` crate to build. +/// +/// Note that this is an extra-unsafe trait because no heed is paid to the +/// lifetime of this store or the Send/Sync-ness of this store. All of that must +/// be respected by embedders (e.g. the `wasmtime::Store` structure). The theory +/// is that `wasmtime::Store` handles all this correctly. +pub unsafe trait Store { + /// Returns the raw pointer in memory where this store's shared + /// `VMInterrupts` structure is located. + /// + /// Used to configure `VMContext` initialization and store the right pointer + /// in the `VMContext`. + fn vminterrupts(&self) -> *mut VMInterrupts; + + /// Returns the externref management structures necessary for this store. + /// + /// The first element returned is the table in which externrefs are stored + /// throughout wasm execution, and the second element is how to look up + /// module information for gc requests. + fn externref_activations_table( + &mut self, + ) -> (&mut VMExternRefActivationsTable, &dyn ModuleInfoLookup); + + /// Returns a reference to the store's limiter for limiting resources, if any. + fn limiter(&mut self) -> Option<&mut dyn ResourceLimiter>; + + /// Callback invoked whenever fuel runs out by a wasm instance. If an error + /// is returned that's raised as a trap. Otherwise wasm execution will + /// continue as normal. + fn out_of_gas(&mut self) -> Result<(), Box>; +} diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs index 9c5800fd8ec9..25bf73fb1972 100644 --- a/crates/runtime/src/libcalls.rs +++ b/crates/runtime/src/libcalls.rs @@ -191,7 +191,7 @@ pub unsafe extern "C" fn wasmtime_memory32_grow( delta: u32, memory_index: u32, ) -> u32 { - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); let memory_index = DefinedMemoryIndex::from_u32(memory_index); instance @@ -205,7 +205,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_grow( delta: u32, memory_index: u32, ) -> u32 { - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); let memory_index = MemoryIndex::from_u32(memory_index); instance @@ -215,7 +215,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_grow( /// Implementation of memory.size for locally-defined 32-bit memories. pub unsafe extern "C" fn wasmtime_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 { - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance(); let memory_index = DefinedMemoryIndex::from_u32(memory_index); instance.memory_size(memory_index) @@ -226,7 +226,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_size( vmctx: *mut VMContext, memory_index: u32, ) -> u32 { - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance(); let memory_index = MemoryIndex::from_u32(memory_index); instance.imported_memory_size(memory_index) @@ -241,7 +241,7 @@ pub unsafe extern "C" fn wasmtime_table_grow( // or is a `VMExternRef` until we look at the table type. init_value: *mut u8, ) -> u32 { - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); let table_index = TableIndex::from_u32(table_index); match instance.table_element_type(table_index) { TableElementType::Func => { @@ -277,9 +277,9 @@ pub unsafe extern "C" fn wasmtime_table_fill( len: u32, ) { let result = { - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); let table_index = TableIndex::from_u32(table_index); - let table = instance.get_table(table_index); + let table = &mut *instance.get_table(table_index); match table.element_type() { TableElementType::Func => { let val = val as *mut VMCallerCheckedAnyfunc; @@ -313,7 +313,7 @@ pub unsafe extern "C" fn wasmtime_table_copy( let result = { let dst_table_index = TableIndex::from_u32(dst_table_index); let src_table_index = TableIndex::from_u32(src_table_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); let dst_table = instance.get_table(dst_table_index); let src_table = instance.get_table(src_table_index); Table::copy(dst_table, src_table, dst, src, len) @@ -335,7 +335,7 @@ pub unsafe extern "C" fn wasmtime_table_init( let result = { let table_index = TableIndex::from_u32(table_index); let elem_index = ElemIndex::from_u32(elem_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); instance.table_init(table_index, elem_index, dst, src, len) }; if let Err(trap) = result { @@ -346,7 +346,7 @@ pub unsafe extern "C" fn wasmtime_table_init( /// Implementation of `elem.drop`. pub unsafe extern "C" fn wasmtime_elem_drop(vmctx: *mut VMContext, elem_index: u32) { let elem_index = ElemIndex::from_u32(elem_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); instance.elem_drop(elem_index); } @@ -362,7 +362,7 @@ pub unsafe extern "C" fn wasmtime_memory_copy( let result = { let src_index = MemoryIndex::from_u32(src_index); let dst_index = MemoryIndex::from_u32(dst_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); instance.memory_copy(dst_index, dst, src_index, src, len) }; if let Err(trap) = result { @@ -380,7 +380,7 @@ pub unsafe extern "C" fn wasmtime_memory_fill( ) { let result = { let memory_index = DefinedMemoryIndex::from_u32(memory_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance(); instance.defined_memory_fill(memory_index, dst, val, len) }; if let Err(trap) = result { @@ -398,7 +398,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory_fill( ) { let result = { let memory_index = MemoryIndex::from_u32(memory_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); instance.imported_memory_fill(memory_index, dst, val, len) }; if let Err(trap) = result { @@ -418,7 +418,7 @@ pub unsafe extern "C" fn wasmtime_memory_init( let result = { let memory_index = MemoryIndex::from_u32(memory_index); let data_index = DataIndex::from_u32(data_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); instance.memory_init(memory_index, data_index, dst, src, len) }; if let Err(trap) = result { @@ -429,7 +429,7 @@ pub unsafe extern "C" fn wasmtime_memory_init( /// Implementation of `data.drop`. pub unsafe extern "C" fn wasmtime_data_drop(vmctx: *mut VMContext, data_index: u32) { let data_index = DataIndex::from_u32(data_index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance_mut(); instance.data_drop(data_index) } @@ -447,9 +447,8 @@ pub unsafe extern "C" fn wasmtime_activations_table_insert_with_gc( externref: *mut u8, ) { let externref = VMExternRef::clone_from_raw(externref); - let instance = (&mut *vmctx).instance(); - let activations_table = &**instance.externref_activations_table(); - let module_info_lookup = &**instance.module_info_lookup(); + let instance = (*vmctx).instance(); + let (activations_table, module_info_lookup) = (*instance.store()).externref_activations_table(); activations_table.insert_with_gc(externref, module_info_lookup); } @@ -459,14 +458,14 @@ pub unsafe extern "C" fn wasmtime_externref_global_get( index: u32, ) -> *mut u8 { let index = GlobalIndex::from_u32(index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance(); let global = instance.defined_or_imported_global_ptr(index); match (*global).as_externref().clone() { None => ptr::null_mut(), Some(externref) => { let raw = externref.as_raw(); - let activations_table = &**instance.externref_activations_table(); - let module_info_lookup = &**instance.module_info_lookup(); + let (activations_table, module_info_lookup) = + (*instance.store()).externref_activations_table(); activations_table.insert_with_gc(externref, module_info_lookup); raw } @@ -486,7 +485,7 @@ pub unsafe extern "C" fn wasmtime_externref_global_set( }; let index = GlobalIndex::from_u32(index); - let instance = (&mut *vmctx).instance(); + let instance = (*vmctx).instance(); let global = instance.defined_or_imported_global_ptr(index); // Swap the new `externref` value into the global before we drop the old @@ -583,6 +582,9 @@ pub unsafe extern "C" fn wasmtime_imported_memory_atomic_wait64( } /// Hook for when an instance runs out of fuel. -pub unsafe extern "C" fn wasmtime_out_of_gas(_vmctx: *mut VMContext) { - crate::traphandlers::out_of_gas() +pub unsafe extern "C" fn wasmtime_out_of_gas(vmctx: *mut VMContext) { + match (*(*vmctx).instance().store()).out_of_gas() { + Ok(()) => {} + Err(err) => crate::traphandlers::raise_user_trap(err), + } } diff --git a/crates/runtime/src/memory.rs b/crates/runtime/src/memory.rs index 973016224d52..59a941cb86c5 100644 --- a/crates/runtime/src/memory.rs +++ b/crates/runtime/src/memory.rs @@ -7,11 +7,7 @@ use crate::vmcontext::VMMemoryDefinition; use crate::ResourceLimiter; use anyhow::{bail, Result}; use more_asserts::{assert_ge, assert_le}; -use std::cell::{Cell, RefCell}; -use std::cmp::min; use std::convert::TryFrom; -use std::ptr; -use std::rc::Rc; use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE}; /// A memory allocator @@ -31,7 +27,7 @@ impl RuntimeMemoryCreator for DefaultMemoryCreator { } /// A linear memory -pub trait RuntimeLinearMemory { +pub trait RuntimeLinearMemory: Send + Sync { /// Returns the number of allocated wasm pages. fn size(&self) -> u32; @@ -43,7 +39,7 @@ pub trait RuntimeLinearMemory { /// /// Returns `None` if memory can't be grown by the specified amount /// of wasm pages. - fn grow(&self, delta: u32) -> Option; + fn grow(&mut self, delta: u32) -> Option; /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. fn vmmemory(&self) -> VMMemoryDefinition; @@ -53,7 +49,7 @@ pub trait RuntimeLinearMemory { #[derive(Debug)] pub struct MmapMemory { // The underlying allocation. - mmap: RefCell, + mmap: WasmMmap, // The optional maximum size in wasm pages of this linear memory. maximum: Option, @@ -108,7 +104,7 @@ impl MmapMemory { impl RuntimeLinearMemory for MmapMemory { /// Returns the number of allocated wasm pages. fn size(&self) -> u32 { - self.mmap.borrow().size + self.mmap.size } /// Returns the maximum number of pages the memory can grow to. @@ -121,19 +117,18 @@ impl RuntimeLinearMemory for MmapMemory { /// /// Returns `None` if memory can't be grown by the specified amount /// of wasm pages. - fn grow(&self, delta: u32) -> Option { + fn grow(&mut self, delta: u32) -> Option { // Optimization of memory.grow 0 calls. - let mut mmap = self.mmap.borrow_mut(); if delta == 0 { - return Some(mmap.size); + return Some(self.mmap.size); } - let new_pages = match mmap.size.checked_add(delta) { + let new_pages = match self.mmap.size.checked_add(delta) { Some(new_pages) => new_pages, // Linear memory size overflow. None => return None, }; - let prev_pages = mmap.size; + let prev_pages = self.mmap.size; if let Some(maximum) = self.maximum { if new_pages > maximum { @@ -145,7 +140,7 @@ impl RuntimeLinearMemory for MmapMemory { // Wasm linear memories are never allowed to grow beyond what is // indexable. If the memory has no maximum, enforce the greatest // limit here. - if new_pages >= WASM_MAX_PAGES { + if new_pages > WASM_MAX_PAGES { // Linear memory size would exceed the index range. return None; } @@ -154,7 +149,7 @@ impl RuntimeLinearMemory for MmapMemory { let prev_bytes = usize::try_from(prev_pages).unwrap() * WASM_PAGE_SIZE as usize; let new_bytes = usize::try_from(new_pages).unwrap() * WASM_PAGE_SIZE as usize; - if new_bytes > mmap.alloc.len() - self.offset_guard_size { + if new_bytes > self.mmap.alloc.len() - self.offset_guard_size { // If the new size is within the declared maximum, but needs more memory than we // have on hand, it's a dynamic heap and it can move. let guard_bytes = self.offset_guard_size; @@ -162,48 +157,59 @@ impl RuntimeLinearMemory for MmapMemory { let mut new_mmap = Mmap::accessible_reserved(new_bytes, request_bytes).ok()?; - let copy_len = mmap.alloc.len() - self.offset_guard_size; - new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&mmap.alloc.as_slice()[..copy_len]); + let copy_len = self.mmap.alloc.len() - self.offset_guard_size; + new_mmap.as_mut_slice()[..copy_len] + .copy_from_slice(&self.mmap.alloc.as_slice()[..copy_len]); - mmap.alloc = new_mmap; + self.mmap.alloc = new_mmap; } else if delta_bytes > 0 { // Make the newly allocated pages accessible. - mmap.alloc.make_accessible(prev_bytes, delta_bytes).ok()?; + self.mmap + .alloc + .make_accessible(prev_bytes, delta_bytes) + .ok()?; } - mmap.size = new_pages; + self.mmap.size = new_pages; Some(prev_pages) } /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. fn vmmemory(&self) -> VMMemoryDefinition { - let mmap = self.mmap.borrow(); VMMemoryDefinition { - base: mmap.alloc.as_mut_ptr(), - current_length: mmap.size as usize * WASM_PAGE_SIZE as usize, + base: self.mmap.alloc.as_mut_ptr(), + current_length: self.mmap.size as usize * WASM_PAGE_SIZE as usize, } } } -enum MemoryStorage { +/// Representation of a runtime wasm linear memory. +pub enum Memory { + /// A "static" memory where the lifetime of the backing memory is managed + /// elsewhere. Currently used with the pooling allocator. Static { - base: *mut u8, - size: Cell, - maximum: u32, + /// The memory in the host for this wasm memory. The length of this + /// slice is the maximum size of the memory that can be grown to. + base: &'static mut [u8], + + /// The current size, in wasm pages, of this memory. + size: u32, + + /// A callback which makes portions of `base` accessible for when memory + /// is grown. Otherwise it's expected that accesses to `base` will + /// fault. make_accessible: fn(*mut u8, usize) -> Result<()>, + /// Stores the pages in the linear memory that have faulted as guard pages when using the `uffd` feature. /// These pages need their protection level reset before the memory can grow. #[cfg(all(feature = "uffd", target_os = "linux"))] - guard_page_faults: RefCell Result<()>)>>, + guard_page_faults: Vec<(usize, usize, fn(*mut u8, usize) -> Result<()>)>, }, - Dynamic(Box), -} -/// Represents an instantiation of a WebAssembly memory. -pub struct Memory { - storage: MemoryStorage, - limiter: Option>, + /// A "dynamic" memory whose data is managed at runtime and lifetime is tied + /// to this instance. + Dynamic(Box), } impl Memory { @@ -211,40 +217,45 @@ impl Memory { pub fn new_dynamic( plan: &MemoryPlan, creator: &dyn RuntimeMemoryCreator, - limiter: Option<&Rc>, + limiter: Option<&mut dyn ResourceLimiter>, ) -> Result { - Self::new( - plan, - MemoryStorage::Dynamic(creator.new_memory(plan)?), - limiter, - ) + Self::limit_new(plan, limiter)?; + Ok(Memory::Dynamic(creator.new_memory(plan)?)) } /// Create a new static (immovable) memory instance for the specified plan. pub fn new_static( plan: &MemoryPlan, - base: *mut u8, - maximum: u32, + base: &'static mut [u8], make_accessible: fn(*mut u8, usize) -> Result<()>, - limiter: Option<&Rc>, + limiter: Option<&mut dyn ResourceLimiter>, ) -> Result { - let storage = MemoryStorage::Static { + Self::limit_new(plan, limiter)?; + + let base = match plan.memory.maximum { + Some(max) if (max as usize) < base.len() / (WASM_PAGE_SIZE as usize) => { + &mut base[..(max * WASM_PAGE_SIZE) as usize] + } + _ => base, + }; + + if plan.memory.minimum > 0 { + make_accessible( + base.as_mut_ptr(), + plan.memory.minimum as usize * WASM_PAGE_SIZE as usize, + )?; + } + + Ok(Memory::Static { base, - size: Cell::new(plan.memory.minimum), - maximum: min(plan.memory.maximum.unwrap_or(maximum), maximum), + size: plan.memory.minimum, make_accessible, #[cfg(all(feature = "uffd", target_os = "linux"))] - guard_page_faults: RefCell::new(Vec::new()), - }; - - Self::new(plan, storage, limiter) + guard_page_faults: Vec::new(), + }) } - fn new( - plan: &MemoryPlan, - storage: MemoryStorage, - limiter: Option<&Rc>, - ) -> Result { + fn limit_new(plan: &MemoryPlan, limiter: Option<&mut dyn ResourceLimiter>) -> Result<()> { if let Some(limiter) = limiter { if !limiter.memory_growing(0, plan.memory.minimum, plan.memory.maximum) { bail!( @@ -253,32 +264,14 @@ impl Memory { ); } } - - if let MemoryStorage::Static { - base, - make_accessible, - .. - } = &storage - { - if plan.memory.minimum > 0 { - make_accessible( - *base, - plan.memory.minimum as usize * WASM_PAGE_SIZE as usize, - )?; - } - } - - Ok(Self { - storage, - limiter: limiter.cloned(), - }) + Ok(()) } /// Returns the number of allocated wasm pages. pub fn size(&self) -> u32 { - match &self.storage { - MemoryStorage::Static { size, .. } => size.get(), - MemoryStorage::Dynamic(mem) => mem.size(), + match self { + Memory::Static { size, .. } => *size, + Memory::Dynamic(mem) => mem.size(), } } @@ -289,15 +282,15 @@ impl Memory { /// The runtime maximum may not be equal to the maximum from the linear memory's /// Wasm type when it is being constrained by an instance allocator. pub fn maximum(&self) -> Option { - match &self.storage { - MemoryStorage::Static { maximum, .. } => Some(*maximum), - MemoryStorage::Dynamic(mem) => mem.maximum(), + match self { + Memory::Static { base, .. } => Some((base.len() / (WASM_PAGE_SIZE as usize)) as u32), + Memory::Dynamic(mem) => mem.maximum(), } } /// Returns whether or not the underlying storage of the memory is "static". pub(crate) fn is_static(&self) -> bool { - if let MemoryStorage::Static { .. } = &self.storage { + if let Memory::Static { .. } = self { true } else { false @@ -317,57 +310,65 @@ impl Memory { /// /// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates /// this unsafety. - pub unsafe fn grow(&self, delta: u32) -> Option { + pub unsafe fn grow( + &mut self, + delta: u32, + limiter: Option<&mut dyn ResourceLimiter>, + ) -> Option { let old_size = self.size(); if delta == 0 { return Some(old_size); } let new_size = old_size.checked_add(delta)?; + let maximum = self.maximum(); - if let Some(limiter) = &self.limiter { - if !limiter.memory_growing(old_size, new_size, self.maximum()) { + if let Some(limiter) = limiter { + if !limiter.memory_growing(old_size, new_size, maximum) { return None; } } - match &self.storage { - MemoryStorage::Static { + #[cfg(all(feature = "uffd", target_os = "linux"))] + { + if self.is_static() { + // Reset any faulted guard pages before growing the memory. + self.reset_guard_pages().ok()?; + } + } + + match self { + Memory::Static { base, size, - maximum, make_accessible, .. } => { - // Reset any faulted guard pages before growing the memory. - #[cfg(all(feature = "uffd", target_os = "linux"))] - self.reset_guard_pages().ok()?; - - if new_size > *maximum || new_size >= WASM_MAX_PAGES { + if new_size > maximum.unwrap_or(WASM_MAX_PAGES) { return None; } let start = usize::try_from(old_size).unwrap() * WASM_PAGE_SIZE as usize; let len = usize::try_from(delta).unwrap() * WASM_PAGE_SIZE as usize; - make_accessible(base.add(start), len).ok()?; + make_accessible(base.as_mut_ptr().add(start), len).ok()?; - size.set(new_size); + *size = new_size; Some(old_size) } - MemoryStorage::Dynamic(mem) => mem.grow(delta), + Memory::Dynamic(mem) => mem.grow(delta), } } /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. pub fn vmmemory(&self) -> VMMemoryDefinition { - match &self.storage { - MemoryStorage::Static { base, size, .. } => VMMemoryDefinition { - base: *base, - current_length: size.get() as usize * WASM_PAGE_SIZE as usize, + match self { + Memory::Static { base, size, .. } => VMMemoryDefinition { + base: base.as_ptr() as *mut _, + current_length: *size as usize * WASM_PAGE_SIZE as usize, }, - MemoryStorage::Dynamic(mem) => mem.vmmemory(), + Memory::Dynamic(mem) => mem.vmmemory(), } } @@ -378,20 +379,18 @@ impl Memory { /// This function will panic if called on a dynamic memory. #[cfg(all(feature = "uffd", target_os = "linux"))] pub(crate) fn record_guard_page_fault( - &self, + &mut self, page_addr: *mut u8, size: usize, reset: fn(*mut u8, usize) -> Result<()>, ) { - match &self.storage { - MemoryStorage::Static { + match self { + Memory::Static { guard_page_faults, .. } => { - guard_page_faults - .borrow_mut() - .push((page_addr, size, reset)); + guard_page_faults.push((page_addr as usize, size, reset)); } - MemoryStorage::Dynamic(_) => { + Memory::Dynamic(_) => { unreachable!("dynamic memories should not have guard page faults") } } @@ -403,17 +402,16 @@ impl Memory { /// /// This function will panic if called on a dynamic memory. #[cfg(all(feature = "uffd", target_os = "linux"))] - pub(crate) fn reset_guard_pages(&self) -> Result<()> { - match &self.storage { - MemoryStorage::Static { + pub(crate) fn reset_guard_pages(&mut self) -> Result<()> { + match self { + Memory::Static { guard_page_faults, .. } => { - let mut faults = guard_page_faults.borrow_mut(); - for (addr, len, reset) in faults.drain(..) { - reset(addr, len)?; + for (addr, len, reset) in guard_page_faults.drain(..) { + reset(addr as *mut u8, len)?; } } - MemoryStorage::Dynamic(_) => { + Memory::Dynamic(_) => { unreachable!("dynamic memories should not have guard page faults") } } @@ -425,20 +423,12 @@ impl Memory { // The default memory representation is an empty memory that cannot grow. impl Default for Memory { fn default() -> Self { - fn make_accessible(_ptr: *mut u8, _len: usize) -> Result<()> { - unreachable!() - } - - Self { - storage: MemoryStorage::Static { - base: ptr::null_mut(), - size: Cell::new(0), - maximum: 0, - make_accessible, - #[cfg(all(feature = "uffd", target_os = "linux"))] - guard_page_faults: RefCell::new(Vec::new()), - }, - limiter: None, + Memory::Static { + base: &mut [], + size: 0, + make_accessible: |_, _| unreachable!(), + #[cfg(all(feature = "uffd", target_os = "linux"))] + guard_page_faults: Vec::new(), } } } diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs index 9e26721fb46e..6045af8301cf 100644 --- a/crates/runtime/src/table.rs +++ b/crates/runtime/src/table.rs @@ -5,12 +5,9 @@ use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; use crate::{ResourceLimiter, Trap, VMExternRef}; use anyhow::{bail, Result}; -use std::cell::{Cell, RefCell}; -use std::cmp::min; use std::convert::{TryFrom, TryInto}; use std::ops::Range; use std::ptr; -use std::rc::Rc; use wasmtime_environ::wasm::TableElementType; use wasmtime_environ::{ir, TablePlan}; @@ -25,6 +22,11 @@ pub enum TableElement { ExternRef(Option), } +// The usage of `*mut VMCallerCheckedAnyfunc` is safe w.r.t. thread safety, this +// just relies on thread-safety of `VMExternRef` itself. +unsafe impl Send for TableElement where VMExternRef: Send {} +unsafe impl Sync for TableElement where VMExternRef: Sync {} + impl TableElement { /// Consumes the given raw pointer into a table element. /// @@ -33,13 +35,13 @@ impl TableElement { /// This is unsafe as it will *not* clone any externref, leaving the reference count unchanged. /// /// This should only be used if the raw pointer is no longer in use. - unsafe fn from_raw(ty: TableElementType, ptr: *mut u8) -> Self { + unsafe fn from_raw(ty: TableElementType, ptr: usize) -> Self { match ty { TableElementType::Func => Self::FuncRef(ptr as _), - TableElementType::Val(_) => Self::ExternRef(if ptr.is_null() { + TableElementType::Val(_) => Self::ExternRef(if ptr == 0 { None } else { - Some(VMExternRef::from_raw(ptr)) + Some(VMExternRef::from_raw(ptr as *mut u8)) }), } } @@ -49,13 +51,13 @@ impl TableElement { /// # Safety /// /// This is unsafe as it will clone any externref, incrementing the reference count. - unsafe fn clone_from_raw(ty: TableElementType, ptr: *mut u8) -> Self { + unsafe fn clone_from_raw(ty: TableElementType, ptr: usize) -> Self { match ty { TableElementType::Func => Self::FuncRef(ptr as _), - TableElementType::Val(_) => Self::ExternRef(if ptr.is_null() { + TableElementType::Val(_) => Self::ExternRef(if ptr == 0 { None } else { - Some(VMExternRef::clone_from_raw(ptr)) + Some(VMExternRef::clone_from_raw(ptr as *mut u8)) }), } } @@ -68,10 +70,10 @@ impl TableElement { /// the reference count. /// /// Use `from_raw` to properly drop any table elements stored as raw pointers. - unsafe fn into_raw(self) -> *mut u8 { + unsafe fn into_raw(self) -> usize { match self { Self::FuncRef(e) => e as _, - Self::ExternRef(e) => e.map_or(ptr::null_mut(), |e| e.into_raw()), + Self::ExternRef(e) => e.map_or(0, |e| e.into_raw() as usize), } } } @@ -94,71 +96,68 @@ impl From for TableElement { } } -enum TableStorage { +/// Represents an instance's table. +pub enum Table { + /// A "static" table where storage space is managed externally, currently + /// used with the pooling allocator. Static { - data: *mut *mut u8, - size: Cell, + /// Where data for this table is stored. The length of this list is the + /// maximum size of the table. + data: &'static mut [usize], + /// The current size of the table. + size: u32, + /// The type of this table. ty: TableElementType, - maximum: u32, }, + /// A "dynamic" table where table storage space is dynamically allocated via + /// `malloc` (aka Rust's `Vec`). Dynamic { - elements: RefCell>, + /// Dynamically managed storage space for this table. The length of this + /// vector is the current size of the table. + elements: Vec, + /// The type of this table. ty: TableElementType, + /// Maximum size that `elements` can grow to. maximum: Option, }, } -/// Represents an instance's table. -pub struct Table { - storage: TableStorage, - limiter: Option>, -} - impl Table { /// Create a new dynamic (movable) table instance for the specified table plan. pub fn new_dynamic( plan: &TablePlan, - limiter: Option<&Rc>, + limiter: Option<&mut dyn ResourceLimiter>, ) -> Result { - let elements = RefCell::new(vec![ptr::null_mut(); plan.table.minimum as usize]); + Self::limit_new(plan, limiter)?; + let elements = vec![0; plan.table.minimum as usize]; let ty = plan.table.ty.clone(); let maximum = plan.table.maximum; - let storage = TableStorage::Dynamic { + Ok(Table::Dynamic { elements, ty, maximum, - }; - - Self::new(plan, storage, limiter) + }) } /// Create a new static (immovable) table instance for the specified table plan. pub fn new_static( plan: &TablePlan, - data: *mut *mut u8, - maximum: u32, - limiter: Option<&Rc>, + data: &'static mut [usize], + limiter: Option<&mut dyn ResourceLimiter>, ) -> Result { - let size = Cell::new(plan.table.minimum); + Self::limit_new(plan, limiter)?; + let size = plan.table.minimum; let ty = plan.table.ty.clone(); - let maximum = min(plan.table.maximum.unwrap_or(maximum), maximum); - - let storage = TableStorage::Static { - data, - size, - ty, - maximum, + let data = match plan.table.maximum { + Some(max) if (max as usize) < data.len() => &mut data[..max as usize], + _ => data, }; - Self::new(plan, storage, limiter) + Ok(Table::Static { data, size, ty }) } - fn new( - plan: &TablePlan, - storage: TableStorage, - limiter: Option<&Rc>, - ) -> Result { + fn limit_new(plan: &TablePlan, limiter: Option<&mut dyn ResourceLimiter>) -> Result<()> { if let Some(limiter) = limiter { if !limiter.table_growing(0, plan.table.minimum, plan.table.maximum) { bail!( @@ -167,24 +166,20 @@ impl Table { ); } } - - Ok(Self { - storage, - limiter: limiter.cloned(), - }) + Ok(()) } /// Returns the type of the elements in this table. pub fn element_type(&self) -> TableElementType { - match &self.storage { - TableStorage::Static { ty, .. } => *ty, - TableStorage::Dynamic { ty, .. } => *ty, + match self { + Table::Static { ty, .. } => *ty, + Table::Dynamic { ty, .. } => *ty, } } /// Returns whether or not the underlying storage of the table is "static". pub(crate) fn is_static(&self) -> bool { - if let TableStorage::Static { .. } = &self.storage { + if let Table::Static { .. } = self { true } else { false @@ -193,9 +188,9 @@ impl Table { /// Returns the number of allocated elements. pub fn size(&self) -> u32 { - match &self.storage { - TableStorage::Static { size, .. } => size.get(), - TableStorage::Dynamic { elements, .. } => elements.borrow().len().try_into().unwrap(), + match self { + Table::Static { size, .. } => *size, + Table::Dynamic { elements, .. } => elements.len().try_into().unwrap(), } } @@ -206,9 +201,9 @@ impl Table { /// The runtime maximum may not be equal to the maximum from the table's Wasm type /// when it is being constrained by an instance allocator. pub fn maximum(&self) -> Option { - match &self.storage { - TableStorage::Static { maximum, .. } => Some(*maximum), - TableStorage::Dynamic { maximum, .. } => maximum.clone(), + match self { + Table::Static { data, .. } => Some(data.len() as u32), + Table::Dynamic { maximum, .. } => maximum.clone(), } } @@ -216,32 +211,31 @@ impl Table { /// /// Returns a trap error on out-of-bounds accesses. pub fn init_funcs( - &self, + &mut self, dst: u32, items: impl ExactSizeIterator, ) -> Result<(), Trap> { assert!(self.element_type() == TableElementType::Func); - self.with_elements_mut(|elements| { - let elements = match elements - .get_mut(usize::try_from(dst).unwrap()..) - .and_then(|s| s.get_mut(..items.len())) - { - Some(elements) => elements, - None => return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds)), - }; - - for (item, slot) in items.zip(elements) { - *slot = item as *mut u8; - } - Ok(()) - }) + let elements = match self + .elements_mut() + .get_mut(usize::try_from(dst).unwrap()..) + .and_then(|s| s.get_mut(..items.len())) + { + Some(elements) => elements, + None => return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds)), + }; + + for (item, slot) in items.zip(elements) { + *slot = item as usize; + } + Ok(()) } /// Fill `table[dst..dst + len]` with `val`. /// /// Returns a trap error on out-of-bounds accesses. - pub fn fill(&self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> { + pub fn fill(&mut self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> { let start = dst as usize; let end = start .checked_add(len as usize) @@ -253,19 +247,16 @@ impl Table { debug_assert!(self.type_matches(&val)); - self.with_elements_mut(|elements| { - if let Some((last, elements)) = elements[start..end].split_last_mut() { - let ty = self.element_type(); - - for e in elements { - Self::set_raw(ty, e, val.clone()); - } - - Self::set_raw(self.element_type(), last, val); + let ty = self.element_type(); + if let Some((last, elements)) = self.elements_mut()[start..end].split_last_mut() { + for e in elements { + Self::set_raw(ty, e, val.clone()); } - Ok(()) - }) + Self::set_raw(ty, last, val); + } + + Ok(()) } /// Grow table by the specified amount of elements. @@ -284,11 +275,16 @@ impl Table { /// /// Generally, prefer using `InstanceHandle::table_grow`, which encapsulates /// this unsafety. - pub unsafe fn grow(&self, delta: u32, init_value: TableElement) -> Option { + pub unsafe fn grow( + &mut self, + delta: u32, + init_value: TableElement, + limiter: Option<&mut dyn ResourceLimiter>, + ) -> Option { let old_size = self.size(); let new_size = old_size.checked_add(delta)?; - if let Some(limiter) = &self.limiter { + if let Some(limiter) = limiter { if !limiter.table_growing(old_size, new_size, self.maximum()) { return None; } @@ -303,13 +299,15 @@ impl Table { debug_assert!(self.type_matches(&init_value)); // First resize the storage and then fill with the init value - match &self.storage { - TableStorage::Static { size, .. } => { - size.set(new_size); + match self { + Table::Static { size, data, .. } => { + debug_assert!(data[*size as usize..new_size as usize] + .iter() + .all(|x| *x == 0)); + *size = new_size; } - TableStorage::Dynamic { elements, .. } => { - let mut elements = elements.borrow_mut(); - elements.resize(new_size as usize, ptr::null_mut()); + Table::Dynamic { elements, .. } => { + elements.resize(new_size as usize, 0); } } @@ -323,11 +321,9 @@ impl Table { /// /// Returns `None` if the index is out of bounds. pub fn get(&self, index: u32) -> Option { - self.with_elements(|elements| { - elements - .get(index as usize) - .map(|p| unsafe { TableElement::clone_from_raw(self.element_type(), *p) }) - }) + self.elements() + .get(index as usize) + .map(|p| unsafe { TableElement::clone_from_raw(self.element_type(), *p) }) } /// Set reference to the specified element. @@ -336,16 +332,15 @@ impl Table { /// /// Returns an error if `index` is out of bounds or if this table type does /// not match the element type. - pub fn set(&self, index: u32, elem: TableElement) -> Result<(), ()> { + pub fn set(&mut self, index: u32, elem: TableElement) -> Result<(), ()> { if !self.type_matches(&elem) { return Err(()); } - self.with_elements_mut(|elements| { - let e = elements.get_mut(index as usize).ok_or(())?; - Self::set_raw(self.element_type(), e, elem); - Ok(()) - }) + let ty = self.element_type(); + let e = self.elements_mut().get_mut(index as usize).ok_or(())?; + Self::set_raw(ty, e, elem); + Ok(()) } /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. @@ -354,9 +349,9 @@ impl Table { /// /// Returns an error if the range is out of bounds of either the source or /// destination tables. - pub fn copy( - dst_table: &Self, - src_table: &Self, + pub unsafe fn copy( + dst_table: *mut Self, + src_table: *mut Self, dst_index: u32, src_index: u32, len: u32, @@ -365,16 +360,16 @@ impl Table { if src_index .checked_add(len) - .map_or(true, |n| n > src_table.size()) + .map_or(true, |n| n > (*src_table).size()) || dst_index .checked_add(len) - .map_or(true, |m| m > dst_table.size()) + .map_or(true, |m| m > (*dst_table).size()) { return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds)); } debug_assert!( - dst_table.element_type() == src_table.element_type(), + (*dst_table).element_type() == (*src_table).element_type(), "table element type mismatch" ); @@ -383,9 +378,9 @@ impl Table { // Check if the tables are the same as we cannot mutably borrow and also borrow the same `RefCell` if ptr::eq(dst_table, src_table) { - Self::copy_elements_within(dst_table, dst_range, src_range); + (*dst_table).copy_elements_within(dst_range, src_range); } else { - Self::copy_elements(dst_table, src_table, dst_range, src_range); + Self::copy_elements(&mut *dst_table, &*src_table, dst_range, src_range); } Ok(()) @@ -393,18 +388,15 @@ impl Table { /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. pub fn vmtable(&self) -> VMTableDefinition { - match &self.storage { - TableStorage::Static { data, size, .. } => VMTableDefinition { - base: *data as _, - current_elements: size.get(), + match self { + Table::Static { data, size, .. } => VMTableDefinition { + base: data.as_ptr() as *mut _, + current_elements: *size, + }, + Table::Dynamic { elements, .. } => VMTableDefinition { + base: elements.as_ptr() as _, + current_elements: elements.len().try_into().unwrap(), }, - TableStorage::Dynamic { elements, .. } => { - let elements = elements.borrow(); - VMTableDefinition { - base: elements.as_ptr() as _, - current_elements: elements.len().try_into().unwrap(), - } - } } } @@ -416,37 +408,21 @@ impl Table { } } - fn with_elements(&self, f: F) -> R - where - F: FnOnce(&[*mut u8]) -> R, - { - match &self.storage { - TableStorage::Static { data, size, .. } => unsafe { - f(std::slice::from_raw_parts(*data, size.get() as usize)) - }, - TableStorage::Dynamic { elements, .. } => { - let elements = elements.borrow(); - f(elements.as_slice()) - } + fn elements(&self) -> &[usize] { + match self { + Table::Static { data, size, .. } => &data[..*size as usize], + Table::Dynamic { elements, .. } => &elements[..], } } - fn with_elements_mut(&self, f: F) -> R - where - F: FnOnce(&mut [*mut u8]) -> R, - { - match &self.storage { - TableStorage::Static { data, size, .. } => unsafe { - f(std::slice::from_raw_parts_mut(*data, size.get() as usize)) - }, - TableStorage::Dynamic { elements, .. } => { - let mut elements = elements.borrow_mut(); - f(elements.as_mut_slice()) - } + fn elements_mut(&mut self) -> &mut [usize] { + match self { + Table::Static { data, size, .. } => &mut data[..*size as usize], + Table::Dynamic { elements, .. } => &mut elements[..], } } - fn set_raw(ty: TableElementType, elem: &mut *mut u8, val: TableElement) { + fn set_raw(ty: TableElementType, elem: &mut usize, val: TableElement) { unsafe { let old = *elem; *elem = val.into_raw(); @@ -457,7 +433,7 @@ impl Table { } fn copy_elements( - dst_table: &Self, + dst_table: &mut Self, src_table: &Self, dst_range: Range, src_range: Range, @@ -470,47 +446,43 @@ impl Table { match ty { TableElementType::Func => { // `funcref` are `Copy`, so just do a mempcy - dst_table.with_elements_mut(|dst| { - src_table.with_elements(|src| dst[dst_range].copy_from_slice(&src[src_range])) - }); + dst_table.elements_mut()[dst_range] + .copy_from_slice(&src_table.elements()[src_range]); } TableElementType::Val(_) => { // We need to clone each `externref` - dst_table.with_elements_mut(|dst| { - src_table.with_elements(|src| { - for (s, d) in src_range.zip(dst_range) { - let elem = unsafe { TableElement::clone_from_raw(ty, src[s]) }; - Self::set_raw(ty, &mut dst[d], elem); - } - }) - }); + let dst = dst_table.elements_mut(); + let src = src_table.elements(); + for (s, d) in src_range.zip(dst_range) { + let elem = unsafe { TableElement::clone_from_raw(ty, src[s]) }; + Self::set_raw(ty, &mut dst[d], elem); + } } } } - fn copy_elements_within(table: &Self, dst_range: Range, src_range: Range) { - let ty = table.element_type(); - + fn copy_elements_within(&mut self, dst_range: Range, src_range: Range) { + let ty = self.element_type(); + let dst = self.elements_mut(); match ty { TableElementType::Func => { // `funcref` are `Copy`, so just do a memmove - table.with_elements_mut(|dst| dst.copy_within(src_range, dst_range.start)); + dst.copy_within(src_range, dst_range.start); } TableElementType::Val(_) => { - // We need to clone each `externref` while handling overlapping ranges - table.with_elements_mut(|dst| { - if dst_range.start <= src_range.start { - for (s, d) in src_range.zip(dst_range) { - let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) }; - Self::set_raw(ty, &mut dst[d], elem); - } - } else { - for (s, d) in src_range.rev().zip(dst_range.rev()) { - let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) }; - Self::set_raw(ty, &mut dst[d], elem); - } + // We need to clone each `externref` while handling overlapping + // ranges + if dst_range.start <= src_range.start { + for (s, d) in src_range.zip(dst_range) { + let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) }; + Self::set_raw(ty, &mut dst[d], elem); } - }); + } else { + for (s, d) in src_range.rev().zip(dst_range.rev()) { + let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) }; + Self::set_raw(ty, &mut dst[d], elem); + } + } } } } @@ -526,25 +498,19 @@ impl Drop for Table { } // Properly drop any table elements stored in the table - self.with_elements(|elements| { - for element in elements.iter() { - let _ = unsafe { TableElement::from_raw(ty, *element) }; - } - }); + for element in self.elements() { + drop(unsafe { TableElement::from_raw(ty, *element) }); + } } } // The default table representation is an empty funcref table that cannot grow. impl Default for Table { fn default() -> Self { - Self { - storage: TableStorage::Static { - data: std::ptr::null_mut(), - size: Cell::new(0), - ty: TableElementType::Func, - maximum: 0, - }, - limiter: None, + Table::Static { + data: &mut [], + size: 0, + ty: TableElementType::Func, } } } diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs index 75f7a7d6d471..62dc531bdb2f 100644 --- a/crates/runtime/src/traphandlers.rs +++ b/crates/runtime/src/traphandlers.rs @@ -1,7 +1,7 @@ //! WebAssembly trap handling, which is built on top of the lower-level //! signalhandling mechanisms. -use crate::VMInterrupts; +use crate::{VMContext, VMInterrupts}; use backtrace::Backtrace; use std::any::Any; use std::cell::{Cell, UnsafeCell}; @@ -15,10 +15,12 @@ use wasmtime_environ::ir; pub use self::tls::TlsRestore; extern "C" { + #[allow(improper_ctypes)] fn RegisterSetjmp( jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8), + callback: extern "C" fn(*mut u8, *mut VMContext), payload: *mut u8, + callee: *mut VMContext, ) -> i32; fn Unwind(jmp_buf: *const u8) -> !; } @@ -165,76 +167,42 @@ impl Trap { /// returning them as a `Result`. /// /// Highly unsafe since `closure` won't have any dtors run. -pub unsafe fn catch_traps(trap_info: &impl TrapInfo, mut closure: F) -> Result<(), Trap> +pub unsafe fn catch_traps<'a, F>( + vminterrupts: *mut VMInterrupts, + signal_handler: Option<*const SignalHandler<'static>>, + callee: *mut VMContext, + mut closure: F, +) -> Result<(), Trap> where - F: FnMut(), + F: FnMut(*mut VMContext), { - return CallThreadState::new(trap_info).with(|cx| { + return CallThreadState::new(signal_handler).with(vminterrupts, |cx| { RegisterSetjmp( cx.jmp_buf.as_ptr(), call_closure::, &mut closure as *mut F as *mut u8, + callee, ) }); - extern "C" fn call_closure(payload: *mut u8) + extern "C" fn call_closure(payload: *mut u8, callee: *mut VMContext) where - F: FnMut(), + F: FnMut(*mut VMContext), { - unsafe { (*(payload as *mut F))() } + unsafe { (*(payload as *mut F))(callee) } } } -/// Runs `func` with the last `trap_info` object registered by `catch_traps`. -/// -/// Calls `func` with `None` if `catch_traps` wasn't previously called from this -/// stack frame. -pub fn with_last_info(func: impl FnOnce(Option<&dyn Any>) -> R) -> R { - tls::with(|state| func(state.map(|s| s.trap_info.as_any()))) -} - -/// Invokes the contextually-defined context's out-of-gas function. -/// -/// (basically delegates to `wasmtime::Store::out_of_gas`) -pub fn out_of_gas() { - tls::with(|state| state.unwrap().trap_info.out_of_gas()) -} - /// Temporary state stored on the stack which is registered in the `tls` module /// below for calls into wasm. -pub struct CallThreadState<'a> { +pub struct CallThreadState { unwind: UnsafeCell>, jmp_buf: Cell<*const u8>, handling_trap: Cell, - trap_info: &'a (dyn TrapInfo + 'a), + signal_handler: Option<*const SignalHandler<'static>>, prev: Cell, } -/// A package of functionality needed by `catch_traps` to figure out what to do -/// when handling a trap. -/// -/// Note that this is an `unsafe` trait at least because it's being run in the -/// context of a synchronous signal handler, so it needs to be careful to not -/// access too much state in answering these queries. -pub unsafe trait TrapInfo { - /// Converts this object into an `Any` to dynamically check its type. - fn as_any(&self) -> &dyn Any; - - /// Uses `call` to call a custom signal handler, if one is specified. - /// - /// Returns `true` if `call` returns true, otherwise returns `false`. - fn custom_signal_handler(&self, call: &dyn Fn(&SignalHandler) -> bool) -> bool; - - /// Callback invoked whenever WebAssembly has entirely consumed the fuel - /// that it was allotted. - /// - /// This function may return, and it may also `raise_lib_trap`. - fn out_of_gas(&self); - - /// Returns the VM interrupts to use for interrupting Wasm code. - fn interrupts(&self) -> &VMInterrupts; -} - enum UnwindReason { Panic(Box), UserTrap(Box), @@ -242,19 +210,23 @@ enum UnwindReason { JitTrap { backtrace: Backtrace, pc: usize }, } -impl<'a> CallThreadState<'a> { +impl CallThreadState { #[inline] - fn new(trap_info: &'a (dyn TrapInfo + 'a)) -> CallThreadState<'a> { + fn new(signal_handler: Option<*const SignalHandler<'static>>) -> CallThreadState { CallThreadState { unwind: UnsafeCell::new(MaybeUninit::uninit()), jmp_buf: Cell::new(ptr::null()), handling_trap: Cell::new(false), - trap_info, + signal_handler, prev: Cell::new(ptr::null()), } } - fn with(self, closure: impl FnOnce(&CallThreadState) -> i32) -> Result<(), Trap> { + fn with( + self, + interrupts: *mut VMInterrupts, + closure: impl FnOnce(&CallThreadState) -> i32, + ) -> Result<(), Trap> { let ret = tls::set(&self, || closure(&self))?; if ret != 0 { return Ok(()); @@ -263,9 +235,9 @@ impl<'a> CallThreadState<'a> { UnwindReason::UserTrap(data) => Err(Trap::User(data)), UnwindReason::LibTrap(trap) => Err(trap), UnwindReason::JitTrap { backtrace, pc } => { - let interrupts = self.trap_info.interrupts(); - let maybe_interrupted = - interrupts.stack_limit.load(SeqCst) == wasmtime_environ::INTERRUPTED; + let maybe_interrupted = unsafe { + (*interrupts).stack_limit.load(SeqCst) == wasmtime_environ::INTERRUPTED + }; Err(Trap::Jit { pc, backtrace, @@ -322,8 +294,10 @@ impl<'a> CallThreadState<'a> { // First up see if any instance registered has a custom trap handler, // in which case run them all. If anything handles the trap then we // return that the trap was handled. - if self.trap_info.custom_signal_handler(&call_handler) { - return 1 as *const _; + if let Some(handler) = self.signal_handler { + if unsafe { call_handler(&*handler) } { + return 1 as *const _; + } } // If this fault wasn't in wasm code, then it's not our problem @@ -366,7 +340,6 @@ impl Drop for ResetCell<'_, T> { mod tls { use super::CallThreadState; use crate::Trap; - use std::mem; use std::ptr; pub use raw::Ptr; @@ -388,7 +361,7 @@ mod tls { use std::cell::Cell; use std::ptr; - pub type Ptr = *const CallThreadState<'static>; + pub type Ptr = *const CallThreadState; // The first entry here is the `Ptr` which is what's used as part of the // public interface of this module. The second entry is a boolean which @@ -460,10 +433,11 @@ mod tls { /// Configures thread local state such that for the duration of the /// execution of `closure` any call to `with` will yield `ptr`, unless this /// is recursively called again. - pub fn set(state: &CallThreadState<'_>, closure: impl FnOnce() -> R) -> Result { - struct Reset<'a, 'b>(&'a CallThreadState<'b>); + #[inline] + pub fn set(state: &CallThreadState, closure: impl FnOnce() -> R) -> Result { + struct Reset<'a>(&'a CallThreadState); - impl Drop for Reset<'_, '_> { + impl Drop for Reset<'_> { #[inline] fn drop(&mut self) { raw::replace(self.0.prev.replace(ptr::null())) @@ -471,13 +445,7 @@ mod tls { } } - // Note that this extension of the lifetime to `'static` should be - // safe because we only ever access it below with an anonymous - // lifetime, meaning `'static` never leaks out of this module. - let ptr = unsafe { - mem::transmute::<*const CallThreadState<'_>, *const CallThreadState<'static>>(state) - }; - let prev = raw::replace(ptr)?; + let prev = raw::replace(state)?; state.prev.set(prev); let _reset = Reset(state); Ok(closure()) @@ -485,7 +453,7 @@ mod tls { /// Returns the last pointer configured with `set` above. Panics if `set` /// has not been previously called. - pub fn with(closure: impl FnOnce(Option<&CallThreadState<'_>>) -> R) -> R { + pub fn with(closure: impl FnOnce(Option<&CallThreadState>) -> R) -> R { let p = raw::get(); unsafe { closure(if p.is_null() { None } else { Some(&*p) }) } } diff --git a/crates/runtime/src/traphandlers/macos.rs b/crates/runtime/src/traphandlers/macos.rs index f48ae034a12f..4131da731481 100644 --- a/crates/runtime/src/traphandlers/macos.rs +++ b/crates/runtime/src/traphandlers/macos.rs @@ -149,7 +149,7 @@ use mach_addons::*; pub enum Void {} /// For now this is basically unused, we don't expose this any more for /// Wasmtime on macOS. -pub type SignalHandler<'a> = dyn Fn(Void) -> bool + 'a; +pub type SignalHandler<'a> = dyn Fn(Void) -> bool + Send + Sync + 'a; /// Process-global port that we use to route thread-level exceptions to. static mut WASMTIME_PORT: mach_port_name_t = MACH_PORT_NULL; diff --git a/crates/runtime/src/traphandlers/unix.rs b/crates/runtime/src/traphandlers/unix.rs index b4046f094429..a4a4573e0d02 100644 --- a/crates/runtime/src/traphandlers/unix.rs +++ b/crates/runtime/src/traphandlers/unix.rs @@ -7,7 +7,7 @@ use std::ptr::{self, null_mut}; /// Function which may handle custom signals while processing traps. pub type SignalHandler<'a> = - dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + 'a; + dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + Send + Sync + 'a; static mut PREV_SIGSEGV: MaybeUninit = MaybeUninit::uninit(); static mut PREV_SIGBUS: MaybeUninit = MaybeUninit::uninit(); diff --git a/crates/runtime/src/traphandlers/windows.rs b/crates/runtime/src/traphandlers/windows.rs index 3cfe3d00d950..db5ad8caba13 100644 --- a/crates/runtime/src/traphandlers/windows.rs +++ b/crates/runtime/src/traphandlers/windows.rs @@ -6,7 +6,8 @@ use winapi::um::winnt::*; use winapi::vc::excpt::*; /// Function which may handle custom signals while processing traps. -pub type SignalHandler<'a> = dyn Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + 'a; +pub type SignalHandler<'a> = + dyn Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + Send + Sync + 'a; pub unsafe fn platform_init() { // our trap handler needs to go first, so that we can recover from diff --git a/crates/runtime/src/vmcontext.rs b/crates/runtime/src/vmcontext.rs index fcb17466db8b..980c87e2a646 100644 --- a/crates/runtime/src/vmcontext.rs +++ b/crates/runtime/src/vmcontext.rs @@ -5,6 +5,7 @@ use crate::externref::VMExternRef; use crate::instance::Instance; use std::any::Any; use std::cell::UnsafeCell; +use std::marker; use std::ptr::NonNull; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::u32; @@ -21,6 +22,11 @@ pub struct VMFunctionImport { pub vmctx: *mut VMContext, } +// Declare that this type is send/sync, it's the responsibility of users of +// `VMFunctionImport` to uphold this guarantee. +unsafe impl Send for VMFunctionImport {} +unsafe impl Sync for VMFunctionImport {} + #[cfg(test)] mod test_vmfunction_import { use super::VMFunctionImport; @@ -77,6 +83,11 @@ pub struct VMTableImport { pub vmctx: *mut VMContext, } +// Declare that this type is send/sync, it's the responsibility of users of +// `VMTableImport` to uphold this guarantee. +unsafe impl Send for VMTableImport {} +unsafe impl Sync for VMTableImport {} + #[cfg(test)] mod test_vmtable_import { use super::VMTableImport; @@ -115,6 +126,11 @@ pub struct VMMemoryImport { pub vmctx: *mut VMContext, } +// Declare that this type is send/sync, it's the responsibility of users of +// `VMMemoryImport` to uphold this guarantee. +unsafe impl Send for VMMemoryImport {} +unsafe impl Sync for VMMemoryImport {} + #[cfg(test)] mod test_vmmemory_import { use super::VMMemoryImport; @@ -150,6 +166,11 @@ pub struct VMGlobalImport { pub from: *mut VMGlobalDefinition, } +// Declare that this type is send/sync, it's the responsibility of users of +// `VMGlobalImport` to uphold this guarantee. +unsafe impl Send for VMGlobalImport {} +unsafe impl Sync for VMGlobalImport {} + #[cfg(test)] mod test_vmglobal_import { use super::VMGlobalImport; @@ -259,7 +280,7 @@ mod test_vmtable_definition { /// /// TODO: Pack the globals more densely, rather than using the same size /// for every type. -#[derive(Debug, Copy, Clone)] +#[derive(Debug)] #[repr(C, align(16))] pub struct VMGlobalDefinition { storage: [u8; 16], @@ -524,6 +545,9 @@ pub struct VMCallerCheckedAnyfunc { // If more elements are added here, remember to add offset_of tests below! } +unsafe impl Send for VMCallerCheckedAnyfunc {} +unsafe impl Sync for VMCallerCheckedAnyfunc {} + #[cfg(test)] mod test_vmcaller_checked_anyfunc { use super::VMCallerCheckedAnyfunc; @@ -682,6 +706,16 @@ pub struct VMInterrupts { pub fuel_consumed: UnsafeCell, } +// The `VMInterrupts` type is a pod-type with no destructor, and we only access +// `stack_limit` from other threads, so add in these trait impls which are +// otherwise not available due to the `fuel_consumed` variable in +// `VMInterrupts`. +// +// Note that users of `fuel_consumed` understand that the unsafety encompasses +// ensuring that it's only mutated/accessed from one thread dynamically. +unsafe impl Send for VMInterrupts {} +unsafe impl Sync for VMInterrupts {} + impl VMInterrupts { /// Flag that an interrupt should occur pub fn interrupt(&self) { @@ -728,7 +762,17 @@ mod test_vminterrupts { /// TODO: We could move the globals into the `vmctx` allocation too. #[derive(Debug)] #[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside -pub struct VMContext {} +pub struct VMContext { + /// There's some more discussion about this within `wasmtime/src/lib.rs` but + /// the idea is that we want to tell the compiler that this contains + /// pointers which transitively refers to itself, to suppress some + /// optimizations that might otherwise assume this doesn't exist. + /// + /// The self-referential pointer we care about is the `*mut Store` pointer + /// early on in this context, which if you follow through enough levels of + /// nesting, eventually can refer back to this `VMContext` + pub _marker: marker::PhantomPinned, +} impl VMContext { /// Return a mutable reference to the associated `Instance`. @@ -742,6 +786,11 @@ impl VMContext { &*((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()) as *const Instance) } + #[inline] + pub(crate) unsafe fn instance_mut(&mut self) -> &mut Instance { + &mut *((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()) as *mut Instance) + } + /// Return a reference to the host state associated with this `Instance`. /// /// # Safety diff --git a/crates/wasi-common/cap-std-sync/src/dir.rs b/crates/wasi-common/cap-std-sync/src/dir.rs index 0b846ccf15a8..d4e9a6db6e24 100644 --- a/crates/wasi-common/cap-std-sync/src/dir.rs +++ b/crates/wasi-common/cap-std-sync/src/dir.rs @@ -106,7 +106,7 @@ impl Dir { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl WasiDir for Dir { fn as_any(&self) -> &dyn Any { self diff --git a/crates/wasi-common/cap-std-sync/src/file.rs b/crates/wasi-common/cap-std-sync/src/file.rs index 0105d615838e..4b1ef343f1fe 100644 --- a/crates/wasi-common/cap-std-sync/src/file.rs +++ b/crates/wasi-common/cap-std-sync/src/file.rs @@ -20,7 +20,7 @@ impl File { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl WasiFile for File { fn as_any(&self) -> &dyn Any { self @@ -119,10 +119,10 @@ impl WasiFile for File { async fn num_ready_bytes(&self) -> Result { Ok(self.0.num_ready_bytes()?) } - async fn readable(&mut self) -> Result<(), Error> { + async fn readable(&self) -> Result<(), Error> { Err(Error::badf()) } - async fn writable(&mut self) -> Result<(), Error> { + async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } } diff --git a/crates/wasi-common/cap-std-sync/src/lib.rs b/crates/wasi-common/cap-std-sync/src/lib.rs index ac8ee602e53d..c292ee5600f1 100644 --- a/crates/wasi-common/cap-std-sync/src/lib.rs +++ b/crates/wasi-common/cap-std-sync/src/lib.rs @@ -42,9 +42,7 @@ pub use clocks::clocks_ctx; pub use sched::sched_ctx; use cap_rand::RngCore; -use std::cell::RefCell; use std::path::Path; -use std::rc::Rc; use wasi_common::{table::Table, Error, WasiCtx, WasiFile}; pub struct WasiCtxBuilder(wasi_common::WasiCtxBuilder); @@ -55,7 +53,7 @@ impl WasiCtxBuilder { random_ctx(), clocks_ctx(), sched_ctx(), - Rc::new(RefCell::new(Table::new())), + Table::new(), )) } pub fn env(self, var: &str, value: &str) -> Result { @@ -124,6 +122,6 @@ impl WasiCtxBuilder { } } -pub fn random_ctx() -> RefCell> { - RefCell::new(Box::new(unsafe { cap_rand::rngs::OsRng::default() })) +pub fn random_ctx() -> Box { + Box::new(unsafe { cap_rand::rngs::OsRng::default() }) } diff --git a/crates/wasi-common/cap-std-sync/src/sched.rs b/crates/wasi-common/cap-std-sync/src/sched.rs index cbda52710990..df8622f07d8b 100644 --- a/crates/wasi-common/cap-std-sync/src/sched.rs +++ b/crates/wasi-common/cap-std-sync/src/sched.rs @@ -21,7 +21,7 @@ impl SyncSched { Self {} } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl WasiSched for SyncSched { async fn poll_oneoff<'a>(&self, poll: &mut Poll<'a>) -> Result<(), Error> { poll_oneoff(poll).await diff --git a/crates/wasi-common/cap-std-sync/src/stdio.rs b/crates/wasi-common/cap-std-sync/src/stdio.rs index c6afa8f9b4fa..6de8a98afa48 100644 --- a/crates/wasi-common/cap-std-sync/src/stdio.rs +++ b/crates/wasi-common/cap-std-sync/src/stdio.rs @@ -22,7 +22,7 @@ pub fn stdin() -> Stdin { Stdin(std::io::stdin()) } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl WasiFile for Stdin { fn as_any(&self) -> &dyn Any { self @@ -103,10 +103,10 @@ impl WasiFile for Stdin { async fn num_ready_bytes(&self) -> Result { Ok(self.0.num_ready_bytes()?) } - async fn readable(&mut self) -> Result<(), Error> { + async fn readable(&self) -> Result<(), Error> { Err(Error::badf()) } - async fn writable(&mut self) -> Result<(), Error> { + async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } } @@ -125,7 +125,7 @@ impl AsRawFd for Stdin { macro_rules! wasi_file_write_impl { ($ty:ty) => { - #[async_trait::async_trait(?Send)] + #[async_trait::async_trait] impl WasiFile for $ty { fn as_any(&self) -> &dyn Any { self @@ -209,10 +209,10 @@ macro_rules! wasi_file_write_impl { async fn num_ready_bytes(&self) -> Result { Ok(0) } - async fn readable(&mut self) -> Result<(), Error> { + async fn readable(&self) -> Result<(), Error> { Err(Error::badf()) } - async fn writable(&mut self) -> Result<(), Error> { + async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } } diff --git a/crates/wasi-common/src/ctx.rs b/crates/wasi-common/src/ctx.rs index cdf89fb89041..cf9b6618f5e8 100644 --- a/crates/wasi-common/src/ctx.rs +++ b/crates/wasi-common/src/ctx.rs @@ -6,25 +6,23 @@ use crate::string_array::{StringArray, StringArrayError}; use crate::table::Table; use crate::Error; use cap_rand::RngCore; -use std::cell::{RefCell, RefMut}; use std::path::{Path, PathBuf}; -use std::rc::Rc; pub struct WasiCtx { pub args: StringArray, pub env: StringArray, - pub random: RefCell>, + pub random: Box, pub clocks: WasiClocks, pub sched: Box, - pub table: Rc>, + pub table: Table, } impl WasiCtx { pub fn builder( - random: RefCell>, + random: Box, clocks: WasiClocks, sched: Box, - table: Rc>, + table: Table, ) -> WasiCtxBuilder { WasiCtxBuilder(WasiCtx { args: StringArray::new(), @@ -36,13 +34,13 @@ impl WasiCtx { }) } - pub fn insert_file(&self, fd: u32, file: Box, caps: FileCaps) { + pub fn insert_file(&mut self, fd: u32, file: Box, caps: FileCaps) { self.table() .insert_at(fd, Box::new(FileEntry::new(caps, file))); } pub fn insert_dir( - &self, + &mut self, fd: u32, dir: Box, caps: DirCaps, @@ -55,15 +53,15 @@ impl WasiCtx { ); } - pub fn table(&self) -> RefMut { - self.table.borrow_mut() + pub fn table(&mut self) -> &mut Table { + &mut self.table } } pub struct WasiCtxBuilder(WasiCtx); impl WasiCtxBuilder { - pub fn build(self) -> Result { + pub fn build(mut self) -> Result { use crate::file::TableFileExt; // Default to an empty readpipe for stdin: if self.0.table().get_file(0).is_err() { @@ -91,23 +89,23 @@ impl WasiCtxBuilder { Ok(self) } - pub fn stdin(self, f: Box) -> Self { + pub fn stdin(mut self, f: Box) -> Self { self.0.insert_file(0, f, FileCaps::all()); self } - pub fn stdout(self, f: Box) -> Self { + pub fn stdout(mut self, f: Box) -> Self { self.0.insert_file(1, f, FileCaps::all()); self } - pub fn stderr(self, f: Box) -> Self { + pub fn stderr(mut self, f: Box) -> Self { self.0.insert_file(2, f, FileCaps::all()); self } pub fn preopened_dir( - self, + mut self, dir: Box, path: impl AsRef, ) -> Result { diff --git a/crates/wasi-common/src/dir.rs b/crates/wasi-common/src/dir.rs index 9c49c6c8aa83..464f527325e4 100644 --- a/crates/wasi-common/src/dir.rs +++ b/crates/wasi-common/src/dir.rs @@ -2,8 +2,6 @@ use crate::file::{FdFlags, FileCaps, FileType, Filestat, OFlags, WasiFile}; use crate::{Error, ErrorExt, SystemTimeSpec}; use bitflags::bitflags; use std::any::Any; -use std::cell::Ref; -use std::ops::Deref; use std::path::PathBuf; #[wiggle::async_trait] @@ -113,14 +111,14 @@ impl DirEntry { } } -pub trait DirEntryExt<'a> { - fn get_cap(self, caps: DirCaps) -> Result, Error>; +pub trait DirEntryExt { + fn get_cap(&self, caps: DirCaps) -> Result<&dyn WasiDir, Error>; } -impl<'a> DirEntryExt<'a> for Ref<'a, DirEntry> { - fn get_cap(self, caps: DirCaps) -> Result, Error> { +impl DirEntryExt for DirEntry { + fn get_cap(&self, caps: DirCaps) -> Result<&dyn WasiDir, Error> { self.capable_of_dir(caps)?; - Ok(Ref::map(self, |r| r.dir.deref())) + Ok(&*self.dir) } } @@ -152,17 +150,17 @@ pub struct DirFdStat { } pub(crate) trait TableDirExt { - fn get_dir(&self, fd: u32) -> Result, Error>; + fn get_dir(&self, fd: u32) -> Result<&DirEntry, Error>; fn is_preopen(&self, fd: u32) -> bool; } impl TableDirExt for crate::table::Table { - fn get_dir(&self, fd: u32) -> Result, Error> { + fn get_dir(&self, fd: u32) -> Result<&DirEntry, Error> { self.get(fd) } fn is_preopen(&self, fd: u32) -> bool { if self.is::(fd) { - let dir_entry: std::cell::Ref = self.get(fd).unwrap(); + let dir_entry: &DirEntry = self.get(fd).unwrap(); dir_entry.preopen_path.is_some() } else { false diff --git a/crates/wasi-common/src/file.rs b/crates/wasi-common/src/file.rs index c718b7ad2551..a5516248cf26 100644 --- a/crates/wasi-common/src/file.rs +++ b/crates/wasi-common/src/file.rs @@ -1,11 +1,9 @@ use crate::{Error, ErrorExt, SystemTimeSpec}; use bitflags::bitflags; use std::any::Any; -use std::cell::{Ref, RefMut}; -use std::ops::{Deref, DerefMut}; #[wiggle::async_trait] -pub trait WasiFile: Send { +pub trait WasiFile: Send + Sync { fn as_any(&self) -> &dyn Any; async fn datasync(&self) -> Result<(), Error>; // write op async fn sync(&self) -> Result<(), Error>; // file op @@ -37,8 +35,8 @@ pub trait WasiFile: Send { async fn peek(&self, buf: &mut [u8]) -> Result; // read op async fn num_ready_bytes(&self) -> Result; // read op - async fn readable(&mut self) -> Result<(), Error>; - async fn writable(&mut self) -> Result<(), Error>; + async fn readable(&self) -> Result<(), Error>; + async fn writable(&self) -> Result<(), Error>; } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -86,14 +84,14 @@ pub struct Filestat { } pub(crate) trait TableFileExt { - fn get_file(&self, fd: u32) -> Result, Error>; - fn get_file_mut(&self, fd: u32) -> Result, Error>; + fn get_file(&self, fd: u32) -> Result<&FileEntry, Error>; + fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error>; } impl TableFileExt for crate::table::Table { - fn get_file(&self, fd: u32) -> Result, Error> { + fn get_file(&self, fd: u32) -> Result<&FileEntry, Error> { self.get(fd) } - fn get_file_mut(&self, fd: u32) -> Result, Error> { + fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error> { self.get_mut(fd) } } @@ -131,24 +129,20 @@ impl FileEntry { } } -pub trait FileEntryExt<'a> { - fn get_cap(self, caps: FileCaps) -> Result, Error>; +pub trait FileEntryExt { + fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error>; + fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error>; } -impl<'a> FileEntryExt<'a> for Ref<'a, FileEntry> { - fn get_cap(self, caps: FileCaps) -> Result, Error> { +impl FileEntryExt for FileEntry { + fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error> { self.capable_of(caps)?; - Ok(Ref::map(self, |r| r.file.deref())) + Ok(&*self.file) } -} -pub trait FileEntryMutExt<'a> { - fn get_cap(self, caps: FileCaps) -> Result, Error>; -} -impl<'a> FileEntryMutExt<'a> for RefMut<'a, FileEntry> { - fn get_cap(self, caps: FileCaps) -> Result, Error> { + fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error> { self.capable_of(caps)?; - Ok(RefMut::map(self, |r| r.file.deref_mut())) + Ok(&mut *self.file) } } diff --git a/crates/wasi-common/src/pipe.rs b/crates/wasi-common/src/pipe.rs index 2f39f5cb9cca..ca83e2d5cd2a 100644 --- a/crates/wasi-common/src/pipe.rs +++ b/crates/wasi-common/src/pipe.rs @@ -183,10 +183,10 @@ impl WasiFile for ReadPipe { async fn num_ready_bytes(&self) -> Result { Ok(0) } - async fn readable(&mut self) -> Result<(), Error> { + async fn readable(&self) -> Result<(), Error> { Err(Error::badf()) } - async fn writable(&mut self) -> Result<(), Error> { + async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } } @@ -342,10 +342,10 @@ impl WasiFile for WritePipe { async fn num_ready_bytes(&self) -> Result { Ok(0) } - async fn readable(&mut self) -> Result<(), Error> { + async fn readable(&self) -> Result<(), Error> { Err(Error::badf()) } - async fn writable(&mut self) -> Result<(), Error> { + async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } } diff --git a/crates/wasi-common/src/sched.rs b/crates/wasi-common/src/sched.rs index fecac2c5cac5..237c66d49af4 100644 --- a/crates/wasi-common/src/sched.rs +++ b/crates/wasi-common/src/sched.rs @@ -10,7 +10,7 @@ pub use subscription::{ }; #[wiggle::async_trait] -pub trait WasiSched { +pub trait WasiSched: Send + Sync { async fn poll_oneoff<'a>(&self, poll: &mut Poll<'a>) -> Result<(), Error>; async fn sched_yield(&self) -> Result<(), Error>; async fn sleep(&self, duration: Duration) -> Result<(), Error>; @@ -56,11 +56,11 @@ impl<'a> Poll<'a> { ud, )); } - pub fn subscribe_read(&mut self, file: &'a mut dyn WasiFile, ud: Userdata) { + pub fn subscribe_read(&mut self, file: &'a dyn WasiFile, ud: Userdata) { self.subs .push((Subscription::Read(RwSubscription::new(file)), ud)); } - pub fn subscribe_write(&mut self, file: &'a mut dyn WasiFile, ud: Userdata) { + pub fn subscribe_write(&mut self, file: &'a dyn WasiFile, ud: Userdata) { self.subs .push((Subscription::Write(RwSubscription::new(file)), ud)); } diff --git a/crates/wasi-common/src/sched/subscription.rs b/crates/wasi-common/src/sched/subscription.rs index cd861f6df063..060547365162 100644 --- a/crates/wasi-common/src/sched/subscription.rs +++ b/crates/wasi-common/src/sched/subscription.rs @@ -3,7 +3,6 @@ use crate::file::WasiFile; use crate::Error; use bitflags::bitflags; use cap_std::time::{Duration, Instant}; -use std::cell::Cell; bitflags! { pub struct RwEventFlags: u32 { @@ -12,24 +11,21 @@ bitflags! { } pub struct RwSubscription<'a> { - pub file: &'a mut dyn WasiFile, - status: Cell>>, + pub file: &'a dyn WasiFile, + status: Option>, } impl<'a> RwSubscription<'a> { - pub fn new(file: &'a mut dyn WasiFile) -> Self { - Self { - file, - status: Cell::new(None), - } + pub fn new(file: &'a dyn WasiFile) -> Self { + Self { file, status: None } } - pub fn complete(&self, size: u64, flags: RwEventFlags) { - self.status.set(Some(Ok((size, flags)))) + pub fn complete(&mut self, size: u64, flags: RwEventFlags) { + self.status = Some(Ok((size, flags))) } - pub fn error(&self, error: Error) { - self.status.set(Some(Err(error))) + pub fn error(&mut self, error: Error) { + self.status = Some(Err(error)) } - pub fn result(&self) -> Option> { + pub fn result(&mut self) -> Option> { self.status.take() } } @@ -72,8 +68,8 @@ pub enum SubscriptionResult { impl SubscriptionResult { pub fn from_subscription(s: Subscription) -> Option { match s { - Subscription::Read(s) => s.result().map(SubscriptionResult::Read), - Subscription::Write(s) => s.result().map(SubscriptionResult::Write), + Subscription::Read(mut s) => s.result().map(SubscriptionResult::Read), + Subscription::Write(mut s) => s.result().map(SubscriptionResult::Write), Subscription::MonotonicClock(s) => s.result().map(SubscriptionResult::MonotonicClock), } } diff --git a/crates/wasi-common/src/snapshots/preview_0.rs b/crates/wasi-common/src/snapshots/preview_0.rs index 6eff280a4778..ec059bf7fa46 100644 --- a/crates/wasi-common/src/snapshots/preview_0.rs +++ b/crates/wasi-common/src/snapshots/preview_0.rs @@ -1,4 +1,4 @@ -use crate::file::{FileCaps, FileEntryExt, FileEntryMutExt, TableFileExt, WasiFile}; +use crate::file::{FileCaps, FileEntryExt, TableFileExt}; use crate::sched::{ subscription::{RwEventFlags, SubscriptionResult}, Poll, Userdata, @@ -7,7 +7,6 @@ use crate::snapshots::preview_1::types as snapshot1_types; use crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1; use crate::{Error, ErrorExt, WasiCtx}; use cap_std::time::Duration; -use std::cell::RefMut; use std::collections::HashSet; use std::convert::{TryFrom, TryInto}; use std::io::{IoSlice, IoSliceMut}; @@ -28,7 +27,7 @@ impl wiggle::GuestErrorType for types::Errno { } impl types::UserErrorConversion for WasiCtx { - fn errno_from_error(&self, e: Error) -> Result { + fn errno_from_error(&mut self, e: Error) -> Result { debug!("Error: {:?}", e); e.try_into() .map_err(|e| wiggle::Trap::String(format!("{:?}", e))) @@ -338,35 +337,35 @@ convert_flags_bidirectional!( #[wiggle::async_trait] impl wasi_unstable::WasiUnstable for WasiCtx { async fn args_get<'a>( - &self, + &mut self, argv: &GuestPtr<'a, GuestPtr<'a, u8>>, argv_buf: &GuestPtr<'a, u8>, ) -> Result<(), Error> { Snapshot1::args_get(self, argv, argv_buf).await } - async fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> { + async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { Snapshot1::args_sizes_get(self).await } async fn environ_get<'a>( - &self, + &mut self, environ: &GuestPtr<'a, GuestPtr<'a, u8>>, environ_buf: &GuestPtr<'a, u8>, ) -> Result<(), Error> { Snapshot1::environ_get(self, environ, environ_buf).await } - async fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> { + async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { Snapshot1::environ_sizes_get(self).await } - async fn clock_res_get(&self, id: types::Clockid) -> Result { + async fn clock_res_get(&mut self, id: types::Clockid) -> Result { Snapshot1::clock_res_get(self, id.into()).await } async fn clock_time_get( - &self, + &mut self, id: types::Clockid, precision: types::Timestamp, ) -> Result { @@ -374,7 +373,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn fd_advise( - &self, + &mut self, fd: types::Fd, offset: types::Filesize, len: types::Filesize, @@ -384,7 +383,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn fd_allocate( - &self, + &mut self, fd: types::Fd, offset: types::Filesize, len: types::Filesize, @@ -392,24 +391,28 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Snapshot1::fd_allocate(self, fd.into(), offset, len).await } - async fn fd_close(&self, fd: types::Fd) -> Result<(), Error> { + async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> { Snapshot1::fd_close(self, fd.into()).await } - async fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> { + async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> { Snapshot1::fd_datasync(self, fd.into()).await } - async fn fd_fdstat_get(&self, fd: types::Fd) -> Result { + async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { Ok(Snapshot1::fd_fdstat_get(self, fd.into()).await?.into()) } - async fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> { + async fn fd_fdstat_set_flags( + &mut self, + fd: types::Fd, + flags: types::Fdflags, + ) -> Result<(), Error> { Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into()).await } async fn fd_fdstat_set_rights( - &self, + &mut self, fd: types::Fd, fs_rights_base: types::Rights, fs_rights_inheriting: types::Rights, @@ -423,12 +426,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx { .await } - async fn fd_filestat_get(&self, fd: types::Fd) -> Result { + async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { Ok(Snapshot1::fd_filestat_get(self, fd.into()).await?.into()) } async fn fd_filestat_set_size( - &self, + &mut self, fd: types::Fd, size: types::Filesize, ) -> Result<(), Error> { @@ -436,7 +439,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn fd_filestat_set_times( - &self, + &mut self, fd: types::Fd, atim: types::Timestamp, mtim: types::Timestamp, @@ -454,7 +457,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { // representation to a std::io::IoSlice(Mut) representation. async fn fd_read<'a>( - &self, + &mut self, fd: types::Fd, iovs: &types::IovecArray<'a>, ) -> Result { @@ -480,7 +483,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn fd_pread<'a>( - &self, + &mut self, fd: types::Fd, iovs: &types::IovecArray<'a>, offset: types::Filesize, @@ -509,7 +512,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn fd_write<'a>( - &self, + &mut self, fd: types::Fd, ciovs: &types::CiovecArray<'a>, ) -> Result { @@ -535,7 +538,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn fd_pwrite<'a>( - &self, + &mut self, fd: types::Fd, ciovs: &types::CiovecArray<'a>, offset: types::Filesize, @@ -563,12 +566,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Ok(types::Size::try_from(bytes_written)?) } - async fn fd_prestat_get(&self, fd: types::Fd) -> Result { + async fn fd_prestat_get(&mut self, fd: types::Fd) -> Result { Ok(Snapshot1::fd_prestat_get(self, fd.into()).await?.into()) } async fn fd_prestat_dir_name<'a>( - &self, + &mut self, fd: types::Fd, path: &GuestPtr<'a, u8>, path_max_len: types::Size, @@ -576,12 +579,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len).await } - async fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> { + async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> { Snapshot1::fd_renumber(self, from.into(), to.into()).await } async fn fd_seek( - &self, + &mut self, fd: types::Fd, offset: types::Filedelta, whence: types::Whence, @@ -589,16 +592,16 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Snapshot1::fd_seek(self, fd.into(), offset, whence.into()).await } - async fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> { + async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> { Snapshot1::fd_sync(self, fd.into()).await } - async fn fd_tell(&self, fd: types::Fd) -> Result { + async fn fd_tell(&mut self, fd: types::Fd) -> Result { Snapshot1::fd_tell(self, fd.into()).await } async fn fd_readdir<'a>( - &self, + &mut self, fd: types::Fd, buf: &GuestPtr<'a, u8>, buf_len: types::Size, @@ -608,7 +611,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_create_directory<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { @@ -616,7 +619,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_filestat_get<'a>( - &self, + &mut self, dirfd: types::Fd, flags: types::Lookupflags, path: &GuestPtr<'a, str>, @@ -629,7 +632,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_filestat_set_times<'a>( - &self, + &mut self, dirfd: types::Fd, flags: types::Lookupflags, path: &GuestPtr<'a, str>, @@ -650,7 +653,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_link<'a>( - &self, + &mut self, src_fd: types::Fd, src_flags: types::Lookupflags, src_path: &GuestPtr<'a, str>, @@ -669,7 +672,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_open<'a>( - &self, + &mut self, dirfd: types::Fd, dirflags: types::Lookupflags, path: &GuestPtr<'a, str>, @@ -693,7 +696,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_readlink<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, buf: &GuestPtr<'a, u8>, @@ -703,7 +706,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_remove_directory<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { @@ -711,7 +714,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_rename<'a>( - &self, + &mut self, src_fd: types::Fd, src_path: &GuestPtr<'a, str>, dest_fd: types::Fd, @@ -721,7 +724,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_symlink<'a>( - &self, + &mut self, src_path: &GuestPtr<'a, str>, dirfd: types::Fd, dest_path: &GuestPtr<'a, str>, @@ -730,7 +733,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn path_unlink_file<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { @@ -745,7 +748,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { // The bodies of these functions is mostly about converting the GuestPtr and types::-based // representation to use the Poll abstraction. async fn poll_oneoff<'a>( - &self, + &mut self, subs: &GuestPtr<'a, types::Subscription>, events: &GuestPtr<'a, types::Event>, nsubscriptions: types::Size, @@ -779,11 +782,11 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } } - let table = self.table(); + let table = &mut self.table; let mut sub_fds: HashSet = HashSet::new(); // We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside - let mut read_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new(); - let mut write_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new(); + let mut reads: Vec<(u32, Userdata)> = Vec::new(); + let mut writes: Vec<(u32, Userdata)> = Vec::new(); let mut poll = Poll::new(); let subs = subs.as_array(nsubscriptions); @@ -828,10 +831,10 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } else { sub_fds.insert(fd); } - let file_ref = table + table .get_file_mut(u32::from(fd))? - .get_cap(FileCaps::POLL_READWRITE)?; - read_refs.push((file_ref, sub.userdata.into())); + .get_cap_mut(FileCaps::POLL_READWRITE)?; + reads.push((u32::from(fd), sub.userdata.into())); } types::SubscriptionU::FdWrite(writesub) => { let fd = writesub.file_descriptor; @@ -841,10 +844,10 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } else { sub_fds.insert(fd); } - let file_ref = table + table .get_file_mut(u32::from(fd))? - .get_cap(FileCaps::POLL_READWRITE)?; - write_refs.push((file_ref, sub.userdata.into())); + .get_cap_mut(FileCaps::POLL_READWRITE)?; + writes.push((u32::from(fd), sub.userdata.into())); } } } @@ -924,20 +927,20 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Ok(num_results.try_into().expect("results fit into memory")) } - async fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap { + async fn proc_exit(&mut self, status: types::Exitcode) -> wiggle::Trap { Snapshot1::proc_exit(self, status).await } - async fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> { + async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> { Err(Error::trap("proc_raise unsupported")) } - async fn sched_yield(&self) -> Result<(), Error> { + async fn sched_yield(&mut self) -> Result<(), Error> { Snapshot1::sched_yield(self).await } async fn random_get<'a>( - &self, + &mut self, buf: &GuestPtr<'a, u8>, buf_len: types::Size, ) -> Result<(), Error> { @@ -945,7 +948,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn sock_recv<'a>( - &self, + &mut self, _fd: types::Fd, _ri_data: &types::IovecArray<'a>, _ri_flags: types::Riflags, @@ -954,7 +957,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn sock_send<'a>( - &self, + &mut self, _fd: types::Fd, _si_data: &types::CiovecArray<'a>, _si_flags: types::Siflags, @@ -962,7 +965,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Err(Error::trap("sock_send unsupported")) } - async fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { + async fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { Err(Error::trap("sock_shutdown unsupported")) } } diff --git a/crates/wasi-common/src/snapshots/preview_1.rs b/crates/wasi-common/src/snapshots/preview_1.rs index 553066628b7b..1d74c64dc6a5 100644 --- a/crates/wasi-common/src/snapshots/preview_1.rs +++ b/crates/wasi-common/src/snapshots/preview_1.rs @@ -1,8 +1,8 @@ use crate::{ dir::{DirCaps, DirEntry, DirEntryExt, DirFdStat, ReaddirCursor, ReaddirEntity, TableDirExt}, file::{ - Advice, FdFlags, FdStat, FileCaps, FileEntry, FileEntryExt, FileEntryMutExt, FileType, - Filestat, OFlags, TableFileExt, WasiFile, + Advice, FdFlags, FdStat, FileCaps, FileEntry, FileEntryExt, FileType, Filestat, OFlags, + TableFileExt, WasiFile, }, sched::{ subscription::{RwEventFlags, SubscriptionResult}, @@ -12,7 +12,6 @@ use crate::{ }; use anyhow::Context; use cap_std::time::{Duration, SystemClock}; -use std::cell::{Ref, RefMut}; use std::collections::HashSet; use std::convert::{TryFrom, TryInto}; use std::io::{IoSlice, IoSliceMut}; @@ -36,7 +35,7 @@ impl wiggle::GuestErrorType for types::Errno { } impl types::UserErrorConversion for WasiCtx { - fn errno_from_error(&self, e: Error) -> Result { + fn errno_from_error(&mut self, e: Error) -> Result { debug!("Error: {:?}", e); e.try_into() .map_err(|e| wiggle::Trap::String(format!("{:?}", e))) @@ -196,30 +195,30 @@ impl TryFrom for types::Errno { #[wiggle::async_trait] impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn args_get<'b>( - &self, + &mut self, argv: &GuestPtr<'b, GuestPtr<'b, u8>>, argv_buf: &GuestPtr<'b, u8>, ) -> Result<(), Error> { self.args.write_to_guest(argv_buf, argv) } - async fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> { + async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { Ok((self.args.number_elements(), self.args.cumulative_size())) } async fn environ_get<'b>( - &self, + &mut self, environ: &GuestPtr<'b, GuestPtr<'b, u8>>, environ_buf: &GuestPtr<'b, u8>, ) -> Result<(), Error> { self.env.write_to_guest(environ_buf, environ) } - async fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> { + async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { Ok((self.env.number_elements(), self.env.cumulative_size())) } - async fn clock_res_get(&self, id: types::Clockid) -> Result { + async fn clock_res_get(&mut self, id: types::Clockid) -> Result { let resolution = match id { types::Clockid::Realtime => Ok(self.clocks.system.resolution()), types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()), @@ -231,7 +230,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn clock_time_get( - &self, + &mut self, id: types::Clockid, precision: types::Timestamp, ) -> Result { @@ -256,7 +255,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_advise( - &self, + &mut self, fd: types::Fd, offset: types::Filesize, len: types::Filesize, @@ -271,7 +270,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_allocate( - &self, + &mut self, fd: types::Fd, offset: types::Filesize, len: types::Filesize, @@ -284,8 +283,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(()) } - async fn fd_close(&self, fd: types::Fd) -> Result<(), Error> { - let mut table = self.table(); + async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> { + let table = self.table(); let fd = u32::from(fd); // Fail fast: If not present in table, Badf @@ -297,7 +296,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let _ = table.delete(fd); } else if table.is::(fd) { // We cannot close preopened directories - let dir_entry: Ref = table.get(fd).unwrap(); + let dir_entry: &DirEntry = table.get(fd).unwrap(); if dir_entry.preopen_path().is_some() { return Err(Error::not_supported().context("cannot close propened directory")); } @@ -310,7 +309,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(()) } - async fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> { + async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> { self.table() .get_file(u32::from(fd))? .get_cap(FileCaps::DATASYNC)? @@ -319,15 +318,15 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(()) } - async fn fd_fdstat_get(&self, fd: types::Fd) -> Result { + async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { let table = self.table(); let fd = u32::from(fd); if table.is::(fd) { - let file_entry: Ref = table.get(fd)?; + let file_entry: &FileEntry = table.get(fd)?; let fdstat = file_entry.get_fdstat().await?; Ok(types::Fdstat::from(&fdstat)) } else if table.is::(fd) { - let dir_entry: Ref = table.get(fd)?; + let dir_entry: &DirEntry = table.get(fd)?; let dir_fdstat = dir_entry.get_dir_fdstat(); Ok(types::Fdstat::from(&dir_fdstat)) } else { @@ -335,16 +334,20 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - async fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> { + async fn fd_fdstat_set_flags( + &mut self, + fd: types::Fd, + flags: types::Fdflags, + ) -> Result<(), Error> { self.table() .get_file_mut(u32::from(fd))? - .get_cap(FileCaps::FDSTAT_SET_FLAGS)? + .get_cap_mut(FileCaps::FDSTAT_SET_FLAGS)? .set_fdflags(FdFlags::from(flags)) .await } async fn fd_fdstat_set_rights( - &self, + &mut self, fd: types::Fd, fs_rights_base: types::Rights, fs_rights_inheriting: types::Rights, @@ -352,11 +355,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let table = self.table(); let fd = u32::from(fd); if table.is::(fd) { - let mut file_entry: RefMut = table.get_mut(fd)?; + let file_entry: &mut FileEntry = table.get_mut(fd)?; let file_caps = FileCaps::from(&fs_rights_base); file_entry.drop_caps_to(file_caps) } else if table.is::(fd) { - let mut dir_entry: RefMut = table.get_mut(fd)?; + let dir_entry: &mut DirEntry = table.get_mut(fd)?; let dir_caps = DirCaps::from(&fs_rights_base); let file_caps = FileCaps::from(&fs_rights_inheriting); dir_entry.drop_caps_to(dir_caps, file_caps) @@ -365,7 +368,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - async fn fd_filestat_get(&self, fd: types::Fd) -> Result { + async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { let table = self.table(); let fd = u32::from(fd); if table.is::(fd) { @@ -388,7 +391,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_filestat_set_size( - &self, + &mut self, fd: types::Fd, size: types::Filesize, ) -> Result<(), Error> { @@ -401,7 +404,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_filestat_set_times( - &self, + &mut self, fd: types::Fd, atim: types::Timestamp, mtim: types::Timestamp, @@ -438,7 +441,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_read<'a>( - &self, + &mut self, fd: types::Fd, iovs: &types::IovecArray<'a>, ) -> Result { @@ -464,7 +467,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_pread<'a>( - &self, + &mut self, fd: types::Fd, iovs: &types::IovecArray<'a>, offset: types::Filesize, @@ -493,7 +496,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_write<'a>( - &self, + &mut self, fd: types::Fd, ciovs: &types::CiovecArray<'a>, ) -> Result { @@ -519,7 +522,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_pwrite<'a>( - &self, + &mut self, fd: types::Fd, ciovs: &types::CiovecArray<'a>, offset: types::Filesize, @@ -547,9 +550,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(types::Size::try_from(bytes_written)?) } - async fn fd_prestat_get(&self, fd: types::Fd) -> Result { + async fn fd_prestat_get(&mut self, fd: types::Fd) -> Result { let table = self.table(); - let dir_entry: Ref = table.get(u32::from(fd)).map_err(|_| Error::badf())?; + let dir_entry: &DirEntry = table.get(u32::from(fd)).map_err(|_| Error::badf())?; if let Some(ref preopen) = dir_entry.preopen_path() { let path_str = preopen.to_str().ok_or_else(|| Error::not_supported())?; let pr_name_len = u32::try_from(path_str.as_bytes().len())?; @@ -560,13 +563,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_prestat_dir_name<'a>( - &self, + &mut self, fd: types::Fd, path: &GuestPtr<'a, u8>, path_max_len: types::Size, ) -> Result<(), Error> { let table = self.table(); - let dir_entry: Ref = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?; + let dir_entry: &DirEntry = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?; if let Some(ref preopen) = dir_entry.preopen_path() { let path_bytes = preopen .to_str() @@ -583,8 +586,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Err(Error::not_supported()) } } - async fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> { - let mut table = self.table(); + async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> { + let table = self.table(); let from = u32::from(from); let to = u32::from(to); if !table.contains_key(from) { @@ -601,7 +604,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_seek( - &self, + &mut self, fd: types::Fd, offset: types::Filedelta, whence: types::Whence, @@ -628,7 +631,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(newoffset) } - async fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> { + async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> { self.table() .get_file(u32::from(fd))? .get_cap(FileCaps::SYNC)? @@ -637,7 +640,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(()) } - async fn fd_tell(&self, fd: types::Fd) -> Result { + async fn fd_tell(&mut self, fd: types::Fd) -> Result { // XXX should this be stream_position? let offset = self .table() @@ -649,7 +652,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn fd_readdir<'a>( - &self, + &mut self, fd: types::Fd, buf: &GuestPtr<'a, u8>, buf_len: types::Size, @@ -703,7 +706,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_create_directory<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { @@ -715,7 +718,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_filestat_get<'a>( - &self, + &mut self, dirfd: types::Fd, flags: types::Lookupflags, path: &GuestPtr<'a, str>, @@ -733,7 +736,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_filestat_set_times<'a>( - &self, + &mut self, dirfd: types::Fd, flags: types::Lookupflags, path: &GuestPtr<'a, str>, @@ -761,7 +764,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_link<'a>( - &self, + &mut self, src_fd: types::Fd, src_flags: types::Lookupflags, src_path: &GuestPtr<'a, str>, @@ -791,7 +794,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_open<'a>( - &self, + &mut self, dirfd: types::Fd, dirflags: types::Lookupflags, path: &GuestPtr<'a, str>, @@ -800,7 +803,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fs_rights_inheriting: types::Rights, fdflags: types::Fdflags, ) -> Result { - let mut table = self.table(); + let table = self.table(); let dirfd = u32::from(dirfd); if table.is::(dirfd) { return Err(Error::not_dir()); @@ -850,7 +853,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_readlink<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, buf: &GuestPtr<'a, u8>, @@ -876,7 +879,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_remove_directory<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { @@ -888,7 +891,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_rename<'a>( - &self, + &mut self, src_fd: types::Fd, src_path: &GuestPtr<'a, str>, dest_fd: types::Fd, @@ -911,7 +914,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_symlink<'a>( - &self, + &mut self, src_path: &GuestPtr<'a, str>, dirfd: types::Fd, dest_path: &GuestPtr<'a, str>, @@ -924,7 +927,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn path_unlink_file<'a>( - &self, + &mut self, dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { @@ -936,7 +939,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn poll_oneoff<'a>( - &self, + &mut self, subs: &GuestPtr<'a, types::Subscription>, events: &GuestPtr<'a, types::Event>, nsubscriptions: types::Size, @@ -970,11 +973,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - let table = self.table(); + let table = &mut self.table; let mut sub_fds: HashSet = HashSet::new(); // We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside - let mut read_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new(); - let mut write_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new(); + let mut read_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new(); + let mut write_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new(); let mut poll = Poll::new(); let subs = subs.as_array(nsubscriptions); @@ -1020,7 +1023,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { sub_fds.insert(fd); } let file_ref = table - .get_file_mut(u32::from(fd))? + .get_file(u32::from(fd))? .get_cap(FileCaps::POLL_READWRITE)?; read_refs.push((file_ref, sub.userdata.into())); } @@ -1033,7 +1036,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { sub_fds.insert(fd); } let file_ref = table - .get_file_mut(u32::from(fd))? + .get_file(u32::from(fd))? .get_cap(FileCaps::POLL_READWRITE)?; write_refs.push((file_ref, sub.userdata.into())); } @@ -1041,10 +1044,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } for (f, ud) in read_refs.iter_mut() { - poll.subscribe_read(f.deref_mut(), *ud); + poll.subscribe_read(*f, *ud); } for (f, ud) in write_refs.iter_mut() { - poll.subscribe_write(f.deref_mut(), *ud); + poll.subscribe_write(*f, *ud); } self.sched.poll_oneoff(&mut poll).await?; @@ -1122,7 +1125,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(num_results.try_into().expect("results fit into memory")) } - async fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap { + async fn proc_exit(&mut self, status: types::Exitcode) -> wiggle::Trap { // Check that the status is within WASI's range. if status < 126 { wiggle::Trap::I32Exit(status as i32) @@ -1131,26 +1134,26 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - async fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> { + async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> { Err(Error::trap("proc_raise unsupported")) } - async fn sched_yield(&self) -> Result<(), Error> { + async fn sched_yield(&mut self) -> Result<(), Error> { self.sched.sched_yield().await } async fn random_get<'a>( - &self, + &mut self, buf: &GuestPtr<'a, u8>, buf_len: types::Size, ) -> Result<(), Error> { let mut buf = buf.as_array(buf_len).as_slice_mut()?; - self.random.borrow_mut().try_fill_bytes(buf.deref_mut())?; + self.random.try_fill_bytes(buf.deref_mut())?; Ok(()) } async fn sock_recv<'a>( - &self, + &mut self, _fd: types::Fd, _ri_data: &types::IovecArray<'a>, _ri_flags: types::Riflags, @@ -1159,7 +1162,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } async fn sock_send<'a>( - &self, + &mut self, _fd: types::Fd, _si_data: &types::CiovecArray<'a>, _si_flags: types::Siflags, @@ -1167,7 +1170,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Err(Error::trap("sock_send unsupported")) } - async fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { + async fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { Err(Error::trap("sock_shutdown unsupported")) } } diff --git a/crates/wasi-common/src/table.rs b/crates/wasi-common/src/table.rs index bcbb641ca570..0549ecabbc22 100644 --- a/crates/wasi-common/src/table.rs +++ b/crates/wasi-common/src/table.rs @@ -1,6 +1,5 @@ use crate::{Error, ErrorExt}; use std::any::Any; -use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; /// The `Table` type is designed to map u32 handles to resources. The table is now part of the @@ -11,7 +10,7 @@ use std::collections::HashMap; /// The `Table` type is intended to model how the Interface Types concept of Resources is shaping /// up. Right now it is just an approximation. pub struct Table { - map: HashMap>>, + map: HashMap>, next_key: u32, } @@ -25,12 +24,12 @@ impl Table { } /// Insert a resource at a certain index. - pub fn insert_at(&mut self, key: u32, a: Box) { - self.map.insert(key, RefCell::new(a)); + pub fn insert_at(&mut self, key: u32, a: Box) { + self.map.insert(key, a); } /// Insert a resource at the next available index. - pub fn push(&mut self, a: Box) -> Result { + pub fn push(&mut self, a: Box) -> Result { // NOTE: The performance of this new key calculation could be very bad once keys wrap // around. if self.map.len() == u32::MAX as usize { @@ -42,7 +41,7 @@ impl Table { if self.map.contains_key(&key) { continue; } - self.map.insert(key, RefCell::new(a)); + self.map.insert(key, a); return Ok(key); } } @@ -55,12 +54,8 @@ impl Table { /// Check if the resource at a given index can be downcast to a given type. /// Note: this will always fail if the resource is already borrowed. pub fn is(&self, key: u32) -> bool { - if let Some(refcell) = self.map.get(&key) { - if let Ok(refmut) = refcell.try_borrow_mut() { - refmut.is::() - } else { - false - } + if let Some(r) = self.map.get(&key) { + r.is::() } else { false } @@ -69,17 +64,10 @@ impl Table { /// Get an immutable reference to a resource of a given type at a given index. Multiple /// immutable references can be borrowed at any given time. Borrow failure /// results in a trapping error. - pub fn get(&self, key: u32) -> Result, Error> { - if let Some(refcell) = self.map.get(&key) { - if let Ok(r) = refcell.try_borrow() { - if r.is::() { - Ok(Ref::map(r, |r| r.downcast_ref::().unwrap())) - } else { - Err(Error::badf().context("element is a different type")) - } - } else { - Err(Error::trap("table get of mutably borrowed element")) - } + pub fn get(&self, key: u32) -> Result<&T, Error> { + if let Some(r) = self.map.get(&key) { + r.downcast_ref::() + .ok_or_else(|| Error::badf().context("element is a different type")) } else { Err(Error::badf().context("key not in table")) } @@ -87,17 +75,10 @@ impl Table { /// Get a mutable reference to a resource of a given type at a given index. Only one mutable /// reference can be borrowed at any given time. Borrow failure results in a trapping error. - pub fn get_mut(&self, key: u32) -> Result, Error> { - if let Some(refcell) = self.map.get(&key) { - if let Ok(r) = refcell.try_borrow_mut() { - if r.is::() { - Ok(RefMut::map(r, |r| r.downcast_mut::().unwrap())) - } else { - Err(Error::badf().context("element is a different type")) - } - } else { - Err(Error::trap("table get_mut of borrowed element")) - } + pub fn get_mut(&mut self, key: u32) -> Result<&mut T, Error> { + if let Some(r) = self.map.get_mut(&key) { + r.downcast_mut::() + .ok_or_else(|| Error::badf().context("element is a different type")) } else { Err(Error::badf().context("key not in table")) } @@ -105,7 +86,7 @@ impl Table { /// Remove a resource at a given index from the table. Returns the resource /// if it was present. - pub fn delete(&mut self, key: u32) -> Option> { - self.map.remove(&key).map(|rc| RefCell::into_inner(rc)) + pub fn delete(&mut self, key: u32) -> Option> { + self.map.remove(&key) } } diff --git a/crates/wasi-common/tokio/src/file.rs b/crates/wasi-common/tokio/src/file.rs index 5907aa7f53ed..45e684e5596d 100644 --- a/crates/wasi-common/tokio/src/file.rs +++ b/crates/wasi-common/tokio/src/file.rs @@ -112,7 +112,7 @@ macro_rules! wasi_file_impl { } #[cfg(not(windows))] - async fn readable(&mut self) -> Result<(), Error> { + async fn readable(&self) -> Result<(), Error> { // The Inner impls OwnsRaw, which asserts exclusive use of the handle by the owned object. // AsyncFd needs to wrap an owned `impl std::os::unix::io::AsRawFd`. Rather than introduce // mutability to let it own the `Inner`, we are depending on the `&mut self` bound on this @@ -135,14 +135,14 @@ macro_rules! wasi_file_impl { } } #[cfg(windows)] - async fn readable(&mut self) -> Result<(), Error> { + async fn readable(&self) -> Result<(), Error> { // Windows uses a rawfd based scheduler :( use wasi_common::ErrorExt; Err(Error::badf()) } #[cfg(not(windows))] - async fn writable(&mut self) -> Result<(), Error> { + async fn writable(&self) -> Result<(), Error> { // The Inner impls OwnsRaw, which asserts exclusive use of the handle by the owned object. // AsyncFd needs to wrap an owned `impl std::os::unix::io::AsRawFd`. Rather than introduce // mutability to let it own the `Inner`, we are depending on the `&mut self` bound on this @@ -165,7 +165,7 @@ macro_rules! wasi_file_impl { } } #[cfg(windows)] - async fn writable(&mut self) -> Result<(), Error> { + async fn writable(&self) -> Result<(), Error> { // Windows uses a rawfd based scheduler :( use wasi_common::ErrorExt; Err(Error::badf()) diff --git a/crates/wasi-common/tokio/src/lib.rs b/crates/wasi-common/tokio/src/lib.rs index e7dc7f42e6ce..9a00b20a1dfd 100644 --- a/crates/wasi-common/tokio/src/lib.rs +++ b/crates/wasi-common/tokio/src/lib.rs @@ -3,10 +3,8 @@ mod file; pub mod sched; pub mod stdio; -use std::cell::RefCell; use std::future::Future; use std::path::Path; -use std::rc::Rc; pub use wasi_cap_std_sync::{clocks_ctx, random_ctx}; use wasi_common::{Error, Table, WasiCtx}; @@ -23,7 +21,7 @@ impl WasiCtxBuilder { random_ctx(), clocks_ctx(), sched_ctx(), - Rc::new(RefCell::new(Table::new())), + Table::new(), )) } pub fn env(self, var: &str, value: &str) -> Result { diff --git a/crates/wasi-common/tokio/src/sched/unix.rs b/crates/wasi-common/tokio/src/sched/unix.rs index 35e0234f7766..cd4a3f802715 100644 --- a/crates/wasi-common/tokio/src/sched/unix.rs +++ b/crates/wasi-common/tokio/src/sched/unix.rs @@ -9,13 +9,13 @@ use wasi_common::{ Context as _, Error, }; -struct FirstReady<'a, T>(Vec + 'a>>>); +struct FirstReady<'a, T>(Vec + Send + 'a>>>); impl<'a, T> FirstReady<'a, T> { fn new() -> Self { FirstReady(Vec::new()) } - fn push(&mut self, f: impl Future + 'a) { + fn push(&mut self, f: impl Future + Send + 'a) { self.0.push(Box::pin(f)); } } diff --git a/crates/wasi-crypto/src/lib.rs b/crates/wasi-crypto/src/lib.rs index 42a21f36d463..5a9455c2e99f 100644 --- a/crates/wasi-crypto/src/lib.rs +++ b/crates/wasi-crypto/src/lib.rs @@ -1,7 +1,20 @@ +use std::borrow::BorrowMut; + mod wiggle_interfaces; pub use wiggle_interfaces::WasiCryptoCtx; +pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> +where + T: BorrowMut, +{ + add_wasi_crypto_common_to_linker(linker)?; + add_wasi_crypto_asymmetric_common_to_linker(linker)?; + add_wasi_crypto_signatures_to_linker(linker)?; + add_wasi_crypto_symmetric_to_linker(linker)?; + Ok(()) +} + wasmtime_wiggle::wasmtime_integration!({ target: wiggle_interfaces::wasi_modules, witx: ["$CARGO_MANIFEST_DIR/spec/witx/wasi_ephemeral_crypto.witx"], @@ -9,22 +22,22 @@ wasmtime_wiggle::wasmtime_integration!({ modules: { wasi_ephemeral_crypto_common => { - name: WasiCryptoCommon, + name: wasi_crypto_common, docs: "wasi-crypto - Common module." }, wasi_ephemeral_crypto_asymmetric_common => { - name: WasiCryptoAsymmetricCommon, + name: wasi_crypto_asymmetric_common, docs: "wasi-crypto - Common module for asymmetric operations." }, wasi_ephemeral_crypto_signatures => { - name: WasiCryptoSignatures, + name: wasi_crypto_signatures, docs: "wasi-crypto - Signature module." }, wasi_ephemeral_crypto_symmetric => { - name: WasiCryptoSymmetric, + name: wasi_crypto_symmetric, docs: "wasi-crypto - Symmetric cryptography module." } } diff --git a/crates/wasi-crypto/src/wiggle_interfaces/asymmetric_common.rs b/crates/wasi-crypto/src/wiggle_interfaces/asymmetric_common.rs index 79cef9d68cb9..2d5c25a1f991 100644 --- a/crates/wasi-crypto/src/wiggle_interfaces/asymmetric_common.rs +++ b/crates/wasi-crypto/src/wiggle_interfaces/asymmetric_common.rs @@ -11,7 +11,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr // --- keypair_manager fn keypair_generate_managed( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, alg_type: guest_types::AlgorithmType, alg_str: &wiggle::GuestPtr<'_, str>, @@ -22,7 +22,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::None => None, }; - Ok(self + Ok((&*self) .keypair_generate_managed( secrets_manager_handle.into(), alg_type.into(), @@ -33,14 +33,14 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr } fn keypair_store_managed( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, kp_handle: guest_types::Keypair, kp_id_ptr: &wiggle::GuestPtr<'_, u8>, kp_id_max_len: guest_types::Size, ) -> Result<(), guest_types::CryptoErrno> { let key_id_buf = &mut *kp_id_ptr.as_array(kp_id_max_len).as_slice_mut()?; - Ok(self.keypair_store_managed( + Ok((&*self).keypair_store_managed( secrets_manager_handle.into(), kp_handle.into(), key_id_buf, @@ -48,12 +48,12 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr } fn keypair_replace_managed( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, kp_old_handle: guest_types::Keypair, kp_new_handle: guest_types::Keypair, ) -> Result { - Ok(self + Ok((&*self) .keypair_replace_managed( secrets_manager_handle.into(), kp_old_handle.into(), @@ -63,14 +63,14 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr } fn keypair_from_id( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, kp_id_ptr: &wiggle::GuestPtr<'_, u8>, kp_id_len: guest_types::Size, kp_version: guest_types::Version, ) -> Result { let kp_id = &*kp_id_ptr.as_array(kp_id_len).as_slice()?; - Ok(self + Ok((&*self) .keypair_from_id(secrets_manager_handle.into(), kp_id, Version(kp_version))? .into()) } @@ -78,7 +78,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr // --- keypair fn keypair_generate( - &self, + &mut self, alg_type: guest_types::AlgorithmType, alg_str: &wiggle::GuestPtr<'_, str>, options_handle: &guest_types::OptOptions, @@ -88,13 +88,13 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::None => None, }; - Ok(self + Ok((&*self) .keypair_generate(alg_type.into(), alg_str, options_handle.map(Into::into))? .into()) } fn keypair_import( - &self, + &mut self, alg_type: guest_types::AlgorithmType, alg_str: &wiggle::GuestPtr<'_, str>, encoded_ptr: &wiggle::GuestPtr<'_, u8>, @@ -103,52 +103,52 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr ) -> Result { let alg_str = &*alg_str.as_str()?; let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; - Ok(self + Ok((&*self) .keypair_import(alg_type.into(), alg_str, encoded, encoding.into())? .into()) } fn keypair_id( - &self, + &mut self, kp_handle: guest_types::Keypair, kp_id_ptr: &wiggle::GuestPtr<'_, u8>, kp_id_max_len: guest_types::Size, ) -> Result<(guest_types::Size, guest_types::Version), guest_types::CryptoErrno> { let kp_id_buf = &mut *kp_id_ptr.as_array(kp_id_max_len as _).as_slice_mut()?; - let (kp_id, version) = self.keypair_id(kp_handle.into())?; + let (kp_id, version) = (&*self).keypair_id(kp_handle.into())?; ensure!(kp_id.len() <= kp_id_buf.len(), CryptoError::Overflow.into()); kp_id_buf.copy_from_slice(&kp_id); Ok((kp_id.len().try_into()?, version.0)) } fn keypair_export( - &self, + &mut self, kp_handle: guest_types::Keypair, encoding: guest_types::KeypairEncoding, ) -> Result { - Ok(self + Ok((&*self) .keypair_export(kp_handle.into(), encoding.into())? .into()) } fn keypair_publickey( - &self, + &mut self, kp_handle: guest_types::Keypair, ) -> Result { - Ok(self.keypair_publickey(kp_handle.into())?.into()) + Ok((&*self).keypair_publickey(kp_handle.into())?.into()) } fn keypair_close( - &self, + &mut self, kp_handle: guest_types::Keypair, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.keypair_close(kp_handle.into())?) + Ok((&*self).keypair_close(kp_handle.into())?) } // --- publickey fn publickey_import( - &self, + &mut self, alg_type: guest_types::AlgorithmType, alg_str: &wiggle::GuestPtr<'_, str>, encoded_ptr: &wiggle::GuestPtr<'_, u8>, @@ -157,46 +157,46 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr ) -> Result { let alg_str = &*alg_str.as_str()?; let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; - Ok(self + Ok((&*self) .publickey_import(alg_type.into(), alg_str, encoded, encoding.into())? .into()) } fn publickey_export( - &self, + &mut self, pk_handle: guest_types::Publickey, encoding: guest_types::PublickeyEncoding, ) -> Result { - Ok(self + Ok((&*self) .publickey_export(pk_handle.into(), encoding.into())? .into()) } fn publickey_from_secretkey( - &self, + &mut self, sk_handle: guest_types::Secretkey, ) -> Result { - Ok(self.keypair_publickey(sk_handle.into())?.into()) + Ok((&*self).keypair_publickey(sk_handle.into())?.into()) } fn publickey_verify( - &self, + &mut self, pk_handle: guest_types::Publickey, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.publickey_verify(pk_handle.into())?) + Ok((&*self).publickey_verify(pk_handle.into())?) } fn publickey_close( - &self, + &mut self, pk_handle: guest_types::Publickey, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.publickey_close(pk_handle.into())?) + Ok((&*self).publickey_close(pk_handle.into())?) } // --- secretkey fn secretkey_import( - &self, + &mut self, alg_type: guest_types::AlgorithmType, alg_str: &wiggle::GuestPtr<'_, str>, encoded_ptr: &wiggle::GuestPtr<'_, u8>, @@ -205,43 +205,43 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr ) -> Result { let alg_str = &*alg_str.as_str()?; let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; - Ok(self + Ok((&*self) .secretkey_import(alg_type.into(), alg_str, encoded, encoding.into())? .into()) } fn secretkey_export( - &self, + &mut self, sk_handle: guest_types::Secretkey, encoding: guest_types::SecretkeyEncoding, ) -> Result { - Ok(self + Ok((&*self) .secretkey_export(sk_handle.into(), encoding.into())? .into()) } fn secretkey_close( - &self, + &mut self, sk_handle: guest_types::Secretkey, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.secretkey_close(sk_handle.into())?) + Ok((&*self).secretkey_close(sk_handle.into())?) } fn keypair_from_pk_and_sk( - &self, + &mut self, pk_handle: guest_types::Publickey, sk_handle: guest_types::Secretkey, ) -> Result { - Ok(self + Ok((&*self) .keypair_from_pk_and_sk(pk_handle.into(), sk_handle.into())? .into()) } fn keypair_secretkey( - &self, + &mut self, kp_handle: guest_types::Keypair, ) -> Result { - Ok(self.keypair_secretkey(kp_handle.into())?.into()) + Ok((&*self).keypair_secretkey(kp_handle.into())?.into()) } } diff --git a/crates/wasi-crypto/src/wiggle_interfaces/common.rs b/crates/wasi-crypto/src/wiggle_interfaces/common.rs index be7b49b30a54..8c5ed13cbde1 100644 --- a/crates/wasi-crypto/src/wiggle_interfaces/common.rs +++ b/crates/wasi-crypto/src/wiggle_interfaces/common.rs @@ -7,21 +7,21 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp // --- options fn options_open( - &self, + &mut self, options_type: guest_types::AlgorithmType, ) -> Result { - Ok(self.options_open(options_type.into())?.into()) + Ok((&*self).options_open(options_type.into())?.into()) } fn options_close( - &self, + &mut self, options_handle: guest_types::Options, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.options_close(options_handle.into())?) + Ok((&*self).options_close(options_handle.into())?) } fn options_set( - &self, + &mut self, options_handle: guest_types::Options, name_str: &wiggle::GuestPtr<'_, str>, value_ptr: &wiggle::GuestPtr<'_, u8>, @@ -29,11 +29,11 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp ) -> Result<(), guest_types::CryptoErrno> { let name_str: &str = &*name_str.as_str()?; let value: &[u8] = { &*value_ptr.as_array(value_len).as_slice()? }; - Ok(self.options_set(options_handle.into(), name_str, value)?) + Ok((&*self).options_set(options_handle.into(), name_str, value)?) } fn options_set_guest_buffer( - &self, + &mut self, options_handle: guest_types::Options, name_str: &wiggle::GuestPtr<'_, str>, buffer_ptr: &wiggle::GuestPtr<'_, u8>, @@ -42,38 +42,38 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp let name_str: &str = &*name_str.as_str()?; let buffer: &'static mut [u8] = unsafe { std::mem::transmute(&mut *buffer_ptr.as_array(buffer_len).as_slice_mut()?) }; - Ok(self.options_set_guest_buffer(options_handle.into(), name_str, buffer)?) + Ok((&*self).options_set_guest_buffer(options_handle.into(), name_str, buffer)?) } fn options_set_u64( - &self, + &mut self, options_handle: guest_types::Options, name_str: &wiggle::GuestPtr<'_, str>, value: u64, ) -> Result<(), guest_types::CryptoErrno> { let name_str: &str = &*name_str.as_str()?; - Ok(self.options_set_u64(options_handle.into(), name_str, value)?) + Ok((&*self).options_set_u64(options_handle.into(), name_str, value)?) } // --- array fn array_output_len( - &self, + &mut self, array_output_handle: guest_types::ArrayOutput, ) -> Result { - Ok(self + Ok((&*self) .array_output_len(array_output_handle.into())? .try_into()?) } fn array_output_pull( - &self, + &mut self, array_output_handle: guest_types::ArrayOutput, buf_ptr: &wiggle::GuestPtr<'_, u8>, buf_len: guest_types::Size, ) -> Result { let buf: &mut [u8] = { &mut *buf_ptr.as_array(buf_len).as_slice_mut()? }; - Ok(self + Ok((&*self) .array_output_pull(array_output_handle.into(), buf)? .try_into()?) } @@ -81,34 +81,34 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp // --- secrets_manager fn secrets_manager_open( - &self, + &mut self, options_handle: &guest_types::OptOptions, ) -> Result { let options_handle = match *options_handle { guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::None => None, }; - Ok(self + Ok((&*self) .secrets_manager_open(options_handle.map(Into::into))? .into()) } fn secrets_manager_close( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.secrets_manager_close(secrets_manager_handle.into())?) + Ok((&*self).secrets_manager_close(secrets_manager_handle.into())?) } fn secrets_manager_invalidate( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, key_id_ptr: &wiggle::GuestPtr<'_, u8>, key_id_len: guest_types::Size, key_version: guest_types::Version, ) -> Result<(), guest_types::CryptoErrno> { let key_id: &[u8] = { &*key_id_ptr.as_array(key_id_len).as_slice()? }; - Ok(self.secrets_manager_invalidate( + Ok((&*self).secrets_manager_invalidate( secrets_manager_handle.into(), key_id, Version(key_version), diff --git a/crates/wasi-crypto/src/wiggle_interfaces/key_exchange.rs b/crates/wasi-crypto/src/wiggle_interfaces/key_exchange.rs index 76bd6284744a..462d76864581 100644 --- a/crates/wasi-crypto/src/wiggle_interfaces/key_exchange.rs +++ b/crates/wasi-crypto/src/wiggle_interfaces/key_exchange.rs @@ -4,26 +4,27 @@ impl super::wasi_ephemeral_crypto_kx::WasiEphemeralCryptoKx for WasiCryptoCtx { // --- key exchange fn kx_dh( - &self, + &mut self, pk_handle: guest_types::Publickey, sk_handle: guest_types::Secretkey, ) -> Result { - Ok(self.kx_dh(pk_handle.into(), sk_handle.into())?.into()) + Ok((&*self).kx_dh(pk_handle.into(), sk_handle.into())?.into()) } // --- Key encapsulation fn kx_encapsulate( - &self, + &mut self, pk_handle: guest_types::Publickey, ) -> Result<(guest_types::ArrayOutput, guest_types::ArrayOutput), guest_types::CryptoErrno> { - let (secret_handle, encapsulated_secret_handle) = self.kx_encapsulate(pk_handle.into())?; + let (secret_handle, encapsulated_secret_handle) = + (&*self).kx_encapsulate(pk_handle.into())?; Ok((secret_handle.into(), encapsulated_secret_handle.into())) } fn kx_decapsulate( - &self, + &mut self, sk_handle: guest_types::Secretkey, encapsulated_secret_ptr: &wiggle::GuestPtr<'_, u8>, encapsulated_secret_len: guest_types::Size, @@ -31,7 +32,7 @@ impl super::wasi_ephemeral_crypto_kx::WasiEphemeralCryptoKx for WasiCryptoCtx { let encapsulated_secret = &*encapsulated_secret_ptr .as_array(encapsulated_secret_len) .as_slice()?; - Ok(self + Ok((&*self) .kx_decapsulate(sk_handle.into(), encapsulated_secret)? .into()) } diff --git a/crates/wasi-crypto/src/wiggle_interfaces/signatures.rs b/crates/wasi-crypto/src/wiggle_interfaces/signatures.rs index 3a88b40937d4..ff4db33e0a14 100644 --- a/crates/wasi-crypto/src/wiggle_interfaces/signatures.rs +++ b/crates/wasi-crypto/src/wiggle_interfaces/signatures.rs @@ -6,17 +6,17 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for // --- signature fn signature_export( - &self, + &mut self, signature_handle: guest_types::Signature, encoding: guest_types::SignatureEncoding, ) -> Result { - Ok(self + Ok((&*self) .signature_export(signature_handle.into(), encoding.into())? .into()) } fn signature_import( - &self, + &mut self, alg_str: &wiggle::GuestPtr<'_, str>, encoded_ptr: &wiggle::GuestPtr<'_, u8>, encoded_len: guest_types::Size, @@ -24,86 +24,89 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for ) -> Result { let alg_str = &*alg_str.as_str()?; let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; - Ok(self + Ok((&*self) .signature_import(alg_str, encoded, encoding.into())? .into()) } fn signature_state_open( - &self, + &mut self, kp_handle: guest_types::Keypair, ) -> Result { - Ok(self.signature_state_open(kp_handle.into())?.into()) + Ok((&*self).signature_state_open(kp_handle.into())?.into()) } fn signature_state_update( - &self, + &mut self, state_handle: guest_types::SignatureState, input_ptr: &wiggle::GuestPtr<'_, u8>, input_len: guest_types::Size, ) -> Result<(), guest_types::CryptoErrno> { let input = &*input_ptr.as_array(input_len).as_slice()?; - Ok(self.signature_state_update(state_handle.into(), input)?) + Ok((&*self).signature_state_update(state_handle.into(), input)?) } fn signature_state_sign( - &self, + &mut self, signature_state_handle: guest_types::SignatureState, ) -> Result { - Ok(self + Ok((&*self) .signature_state_sign(signature_state_handle.into())? .into()) } fn signature_state_close( - &self, + &mut self, signature_state_handle: guest_types::SignatureState, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.signature_state_close(signature_state_handle.into())?) + Ok((&*self).signature_state_close(signature_state_handle.into())?) } fn signature_verification_state_open( - &self, + &mut self, pk_handle: guest_types::Publickey, ) -> Result { - Ok(self + Ok((&*self) .signature_verification_state_open(pk_handle.into())? .into()) } fn signature_verification_state_update( - &self, + &mut self, verification_state_handle: guest_types::SignatureVerificationState, input_ptr: &wiggle::GuestPtr<'_, u8>, input_len: guest_types::Size, ) -> Result<(), guest_types::CryptoErrno> { let input: &[u8] = &*input_ptr.as_array(input_len).as_slice()?; - Ok(self.signature_verification_state_update(verification_state_handle.into(), input)?) + Ok( + (&*self) + .signature_verification_state_update(verification_state_handle.into(), input)?, + ) } fn signature_verification_state_verify( - &self, + &mut self, verification_state_handle: guest_types::SignatureVerificationState, signature_handle: guest_types::Signature, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.signature_verification_state_verify( + Ok((&*self).signature_verification_state_verify( verification_state_handle.into(), signature_handle.into(), )?) } fn signature_verification_state_close( - &self, + &mut self, verification_state_handle: guest_types::SignatureVerificationState, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.signature_verification_state_close(verification_state_handle.into())?) + Ok((&*self).signature_verification_state_close(verification_state_handle.into())?) } fn signature_close( - &self, + &mut self, signature_handle: guest_types::Signature, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.signature_close(signature_handle.into())?) + Ok((&*self).signature_close(signature_handle.into())?) } } diff --git a/crates/wasi-crypto/src/wiggle_interfaces/symmetric.rs b/crates/wasi-crypto/src/wiggle_interfaces/symmetric.rs index 89d33ce8ec67..881c3f5d65fc 100644 --- a/crates/wasi-crypto/src/wiggle_interfaces/symmetric.rs +++ b/crates/wasi-crypto/src/wiggle_interfaces/symmetric.rs @@ -7,7 +7,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa // --- secrets_manager fn symmetric_key_generate_managed( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, alg_str: &wiggle::GuestPtr<'_, str>, options_handle: &guest_types::OptOptions, @@ -17,7 +17,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::None => None, }; - Ok(self + Ok((&*self) .symmetric_key_generate_managed( secrets_manager_handle.into(), alg_str, @@ -27,7 +27,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa } fn symmetric_key_store_managed( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, symmetric_key_handle: guest_types::SymmetricKey, symmetric_key_id_ptr: &wiggle::GuestPtr<'_, u8>, @@ -36,7 +36,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa let key_id_buf = &mut *symmetric_key_id_ptr .as_array(symmetric_key_id_max_len) .as_slice_mut()?; - Ok(self.symmetric_key_store_managed( + Ok((&*self).symmetric_key_store_managed( secrets_manager_handle.into(), symmetric_key_handle.into(), key_id_buf, @@ -44,12 +44,12 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa } fn symmetric_key_replace_managed( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, symmetric_key_old_handle: guest_types::SymmetricKey, symmetric_key_new_handle: guest_types::SymmetricKey, ) -> Result { - Ok(self + Ok((&*self) .symmetric_key_replace_managed( secrets_manager_handle.into(), symmetric_key_old_handle.into(), @@ -59,7 +59,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa } fn symmetric_key_from_id( - &self, + &mut self, secrets_manager_handle: guest_types::SecretsManager, symmetric_key_id_ptr: &wiggle::GuestPtr<'_, u8>, symmetric_key_id_len: guest_types::Size, @@ -68,7 +68,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa let symmetric_key_id = &*symmetric_key_id_ptr .as_array(symmetric_key_id_len) .as_slice()?; - Ok(self + Ok((&*self) .symmetric_key_from_id( secrets_manager_handle.into(), symmetric_key_id, @@ -80,7 +80,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa // --- key fn symmetric_key_generate( - &self, + &mut self, alg_str: &wiggle::GuestPtr<'_, str>, options_handle: &guest_types::OptOptions, ) -> Result { @@ -89,33 +89,33 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::None => None, }; - Ok(self + Ok((&*self) .symmetric_key_generate(alg_str, options_handle.map(Into::into))? .into()) } fn symmetric_key_import( - &self, + &mut self, alg_str: &wiggle::GuestPtr<'_, str>, raw_ptr: &wiggle::GuestPtr<'_, u8>, raw_len: guest_types::Size, ) -> Result { let alg_str = &*alg_str.as_str()?; let raw = &*raw_ptr.as_array(raw_len).as_slice()?; - Ok(self.symmetric_key_import(alg_str, raw)?.into()) + Ok((&*self).symmetric_key_import(alg_str, raw)?.into()) } fn symmetric_key_export( - &self, + &mut self, symmetric_key_handle: guest_types::SymmetricKey, ) -> Result { - Ok(self + Ok((&*self) .symmetric_key_export(symmetric_key_handle.into())? .into()) } fn symmetric_key_id( - &self, + &mut self, symmetric_key_handle: guest_types::SymmetricKey, symmetric_key_id_ptr: &wiggle::GuestPtr<'_, u8>, symmetric_key_id_max_len: guest_types::Size, @@ -123,7 +123,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa let key_id_buf = &mut *symmetric_key_id_ptr .as_array(symmetric_key_id_max_len) .as_slice_mut()?; - let (key_id, version) = self.symmetric_key_id(symmetric_key_handle.into())?; + let (key_id, version) = (&*self).symmetric_key_id(symmetric_key_handle.into())?; ensure!( key_id.len() <= key_id_buf.len(), CryptoError::Overflow.into() @@ -133,16 +133,16 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa } fn symmetric_key_close( - &self, + &mut self, key_handle: guest_types::SymmetricKey, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.symmetric_key_close(key_handle.into())?) + Ok((&*self).symmetric_key_close(key_handle.into())?) } // --- state fn symmetric_state_open( - &self, + &mut self, alg_str: &wiggle::GuestPtr<'_, str>, key_handle: &guest_types::OptSymmetricKey, options_handle: &guest_types::OptOptions, @@ -156,7 +156,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::None => None, }; - Ok(self + Ok((&*self) .symmetric_state_open( alg_str, key_handle.map(Into::into), @@ -166,7 +166,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa } fn symmetric_state_options_get( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, name_str: &wiggle::GuestPtr<'_, str>, value_ptr: &wiggle::GuestPtr<'_, u8>, @@ -174,78 +174,78 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa ) -> Result { let name_str: &str = &*name_str.as_str()?; let value = &mut *value_ptr.as_array(value_max_len).as_slice_mut()?; - Ok(self + Ok((&*self) .options_get(symmetric_state_handle.into(), name_str, value)? .try_into()?) } fn symmetric_state_options_get_u64( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, name_str: &wiggle::GuestPtr<'_, str>, ) -> Result { let name_str: &str = &*name_str.as_str()?; - Ok(self.options_get_u64(symmetric_state_handle.into(), name_str)?) + Ok((&*self).options_get_u64(symmetric_state_handle.into(), name_str)?) } fn symmetric_state_close( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.symmetric_state_close(symmetric_state_handle.into())?) + Ok((&*self).symmetric_state_close(symmetric_state_handle.into())?) } fn symmetric_state_absorb( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, data_ptr: &wiggle::GuestPtr<'_, u8>, data_len: guest_types::Size, ) -> Result<(), guest_types::CryptoErrno> { let data = &*data_ptr.as_array(data_len).as_slice()?; - Ok(self.symmetric_state_absorb(symmetric_state_handle.into(), data)?) + Ok((&*self).symmetric_state_absorb(symmetric_state_handle.into(), data)?) } fn symmetric_state_squeeze( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, out_ptr: &wiggle::GuestPtr<'_, u8>, out_len: guest_types::Size, ) -> Result<(), guest_types::CryptoErrno> { let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; - Ok(self.symmetric_state_squeeze(symmetric_state_handle.into(), out)?) + Ok((&*self).symmetric_state_squeeze(symmetric_state_handle.into(), out)?) } fn symmetric_state_squeeze_tag( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, ) -> Result { - Ok(self + Ok((&*self) .symmetric_state_squeeze_tag(symmetric_state_handle.into())? .into()) } fn symmetric_state_squeeze_key( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, alg_str: &wiggle::GuestPtr<'_, str>, ) -> Result { let alg_str = &*alg_str.as_str()?; - Ok(self + Ok((&*self) .symmetric_state_squeeze_key(symmetric_state_handle.into(), alg_str)? .into()) } fn symmetric_state_max_tag_len( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, ) -> Result { - Ok(self + Ok((&*self) .symmetric_state_max_tag_len(symmetric_state_handle.into())? .try_into()?) } fn symmetric_state_encrypt( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, out_ptr: &wiggle::GuestPtr<'_, u8>, out_len: guest_types::Size, @@ -254,13 +254,13 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa ) -> Result { let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let data = &*data_ptr.as_array(data_len).as_slice()?; - Ok(self + Ok((&*self) .symmetric_state_encrypt(symmetric_state_handle.into(), out, data)? .try_into()?) } fn symmetric_state_encrypt_detached( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, out_ptr: &wiggle::GuestPtr<'_, u8>, out_len: guest_types::Size, @@ -269,13 +269,13 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa ) -> Result { let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let data = &*data_ptr.as_array(data_len).as_slice()?; - Ok(self + Ok((&*self) .symmetric_state_encrypt_detached(symmetric_state_handle.into(), out, data)? .into()) } fn symmetric_state_decrypt( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, out_ptr: &wiggle::GuestPtr<'_, u8>, out_len: guest_types::Size, @@ -284,13 +284,13 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa ) -> Result { let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let data = &*data_ptr.as_array(data_len).as_slice()?; - Ok(self + Ok((&*self) .symmetric_state_decrypt(symmetric_state_handle.into(), out, data)? .try_into()?) } fn symmetric_state_decrypt_detached( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, out_ptr: &wiggle::GuestPtr<'_, u8>, out_len: guest_types::Size, @@ -302,55 +302,55 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let data = &*data_ptr.as_array(data_len).as_slice()?; let raw_tag: &[u8] = &*raw_tag_ptr.as_array(raw_tag_len).as_slice()?; - Ok(self + Ok((&*self) .symmetric_state_decrypt_detached(symmetric_state_handle.into(), out, data, raw_tag)? .try_into()?) } fn symmetric_state_ratchet( - &self, + &mut self, symmetric_state_handle: guest_types::SymmetricState, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.symmetric_state_ratchet(symmetric_state_handle.into())?) + Ok((&*self).symmetric_state_ratchet(symmetric_state_handle.into())?) } // --- tag fn symmetric_tag_len( - &self, + &mut self, symmetric_tag_handle: guest_types::SymmetricTag, ) -> Result { - Ok(self + Ok((&*self) .symmetric_tag_len(symmetric_tag_handle.into())? .try_into()?) } fn symmetric_tag_pull( - &self, + &mut self, symmetric_tag_handle: guest_types::SymmetricTag, buf_ptr: &wiggle::GuestPtr<'_, u8>, buf_len: guest_types::Size, ) -> Result { let buf = &mut *buf_ptr.as_array(buf_len).as_slice_mut()?; - Ok(self + Ok((&*self) .symmetric_tag_pull(symmetric_tag_handle.into(), buf)? .try_into()?) } fn symmetric_tag_verify( - &self, + &mut self, symmetric_tag_handle: guest_types::SymmetricTag, expected_raw_ptr: &wiggle::GuestPtr<'_, u8>, expected_raw_len: guest_types::Size, ) -> Result<(), guest_types::CryptoErrno> { let expected_raw = &*expected_raw_ptr.as_array(expected_raw_len).as_slice()?; - Ok(self.symmetric_tag_verify(symmetric_tag_handle.into(), expected_raw)?) + Ok((&*self).symmetric_tag_verify(symmetric_tag_handle.into(), expected_raw)?) } fn symmetric_tag_close( - &self, + &mut self, symmetric_tag_handle: guest_types::SymmetricTag, ) -> Result<(), guest_types::CryptoErrno> { - Ok(self.symmetric_tag_close(symmetric_tag_handle.into())?) + Ok((&*self).symmetric_tag_close(symmetric_tag_handle.into())?) } } diff --git a/crates/wasi-nn/src/impl.rs b/crates/wasi-nn/src/impl.rs index 47fd323fdb41..8152de77b992 100644 --- a/crates/wasi-nn/src/impl.rs +++ b/crates/wasi-nn/src/impl.rs @@ -28,7 +28,7 @@ pub enum UsageError { impl<'a> WasiEphemeralNn for WasiNnCtx { fn load<'b>( - &self, + &mut self, builders: &GraphBuilderArray<'_>, encoding: GraphEncoding, target: ExecutionTarget, @@ -76,7 +76,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx { Ok(id) } - fn init_execution_context(&self, graph: Graph) -> Result { + fn init_execution_context(&mut self, graph: Graph) -> Result { let request = if let Some((_, executable_graph)) = self.ctx.borrow_mut().graphs.get_mut(graph) { executable_graph.create_infer_request()? @@ -90,7 +90,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx { } fn set_input<'b>( - &self, + &mut self, context: GraphExecutionContext, index: u32, tensor: &Tensor<'b>, @@ -135,7 +135,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx { Ok(()) } - fn compute(&self, context: GraphExecutionContext) -> Result<()> { + fn compute(&mut self, context: GraphExecutionContext) -> Result<()> { if let Some(execution) = self.ctx.borrow_mut().executions.get_mut(context) { Ok(execution.request.infer()?) } else { @@ -144,7 +144,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx { } fn get_output<'b>( - &self, + &mut self, context: GraphExecutionContext, index: u32, out_buffer: &GuestPtr<'_, u8>, diff --git a/crates/wasi-nn/src/lib.rs b/crates/wasi-nn/src/lib.rs index 68999d9310d3..783188652023 100644 --- a/crates/wasi-nn/src/lib.rs +++ b/crates/wasi-nn/src/lib.rs @@ -16,7 +16,7 @@ wasmtime_wiggle::wasmtime_integration!({ // This macro will emit a struct to represent the instance, with this name and docs: modules: { wasi_ephemeral_nn => { - name: WasiNn, + name: wasi_nn, docs: "An instantiated instance of the wasi-nn exports.", } }, diff --git a/crates/wasi-nn/src/witx.rs b/crates/wasi-nn/src/witx.rs index 8244f9964443..d17da5fbbb4b 100644 --- a/crates/wasi-nn/src/witx.rs +++ b/crates/wasi-nn/src/witx.rs @@ -11,7 +11,7 @@ wiggle::from_witx!({ use types::NnErrno; impl<'a> types::UserErrorConversion for WasiNnCtx { - fn nn_errno_from_wasi_nn_error(&self, e: WasiNnError) -> Result { + fn nn_errno_from_wasi_nn_error(&mut self, e: WasiNnError) -> Result { eprintln!("Host error: {:?}", e); match e { WasiNnError::OpenvinoSetupError(_) => unimplemented!(), diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 3edaa3450589..5c6886eec958 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -28,7 +28,7 @@ pub use sync::*; #[cfg(feature = "tokio")] pub mod tokio { pub use wasi_tokio::*; - super::define_wasi!(async); + super::define_wasi!(async + Send); } // The only difference between these definitions for sync vs async is whether @@ -38,44 +38,18 @@ pub mod tokio { #[doc(hidden)] #[macro_export] macro_rules! define_wasi { - ($async_mode: tt) => { + ($async_mode: tt $($bounds:tt)*) => { -use std::cell::RefCell; -use std::rc::Rc; -use wasmtime::{Config, Linker, Store}; -use wasi_common::WasiCtx; +use std::borrow::BorrowMut; +use wasmtime::Linker; -/// An instantiated instance of all available wasi exports. Presently includes -/// both the "preview1" snapshot and the "unstable" (preview0) snapshot. -pub struct Wasi { - preview_1: snapshots::preview_1::Wasi, - preview_0: snapshots::preview_0::Wasi, -} - -impl Wasi { - pub fn new(store: &Store, context: WasiCtx) -> Self { - let context = Rc::new(RefCell::new(context)); - let preview_1 = snapshots::preview_1::Wasi::new(store, context.clone()); - let preview_0 = snapshots::preview_0::Wasi::new(store, context); - Self { - preview_1, - preview_0, - } - } - pub fn add_to_linker(&self, linker: &mut Linker) -> Result<(), anyhow::Error> { - self.preview_1.add_to_linker(linker)?; - self.preview_0.add_to_linker(linker)?; - Ok(()) - } - pub fn add_to_config(config: &mut Config) { - snapshots::preview_1::Wasi::add_to_config(config); - snapshots::preview_0::Wasi::add_to_config(config); - } - pub fn set_context(store: &Store, context: WasiCtx) -> Result<(), WasiCtx> { - // It doesn't matter which underlying `Wasi` type this gets called on as the - // implementations are identical - snapshots::preview_1::Wasi::set_context(store, context) - } +pub fn add_to_linker(linker: &mut Linker) -> anyhow::Result<()> +where + T: BorrowMut $($bounds)*, +{ + snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker)?; + snapshots::preview_0::add_wasi_unstable_to_linker(linker)?; + Ok(()) } pub mod snapshots { @@ -94,7 +68,7 @@ pub mod snapshots { // This macro will emit a struct to represent the instance, // with this name and docs: modules: { wasi_snapshot_preview1 => - { name: Wasi, + { name: wasi_snapshot_preview1, docs: "An instantiated instance of the wasi exports. This represents a wasi module which can be used to instantiate other wasm @@ -122,7 +96,7 @@ resolution.", // This macro will emit a struct to represent the instance, // with this name and docs: modules: { wasi_unstable => - { name: Wasi, + { name: wasi_unstable, docs: "An instantiated instance of the wasi exports. This represents a wasi module which can be used to instantiate other wasm diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index f133fa787b77..5cd16be24c36 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1,16 +1,12 @@ use crate::memory::MemoryCreator; use crate::trampoline::MemoryCreatorProxy; -use crate::{func::HostFunc, Caller, FuncType, IntoFunc, Trap, Val, WasmRet, WasmTy}; use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; use std::cmp; -use std::collections::HashMap; use std::convert::TryFrom; use std::fmt; -use std::future::Future; #[cfg(feature = "cache")] use std::path::Path; -use std::pin::Pin; use std::sync::Arc; use wasmparser::WasmFeatures; #[cfg(feature = "cache")] @@ -273,97 +269,6 @@ impl Default for InstanceAllocationStrategy { } } -/// This type is used for storing host functions in a `Config`. -/// -/// The module and function names are interned for more compact storage. -#[derive(Clone)] -struct HostFuncMap { - index_map: HashMap, usize>, - strings: Vec>, - funcs: HashMap<(usize, usize), (Arc, bool)>, -} - -impl HostFuncMap { - fn new() -> Self { - Self { - index_map: HashMap::new(), - strings: Vec::new(), - funcs: HashMap::new(), - } - } - - fn insert(&mut self, module: &str, name: &str, async_required: bool, func: HostFunc) { - let key = (self.intern_str(module), self.intern_str(name)); - self.funcs.insert(key, (Arc::new(func), async_required)); - } - - fn get(&self, module: &str, name: &str) -> Option<&HostFunc> { - let key = ( - self.index_map.get(module).cloned()?, - self.index_map.get(name).cloned()?, - ); - self.funcs.get(&key).map(|f| f.0.as_ref()) - } - - fn intern_str(&mut self, string: &str) -> usize { - if let Some(idx) = self.index_map.get(string) { - return *idx; - } - let string: Arc = string.into(); - let idx = self.strings.len(); - self.strings.push(string.clone()); - self.index_map.insert(string, idx); - idx - } - - fn async_required(&self) -> bool { - self.funcs.values().any(|f| f.1) - } - - fn iter(&self) -> impl Iterator { - self.funcs.values().map(|v| &*v.0) - } -} - -macro_rules! generate_wrap_async_host_func { - ($num:tt $($args:ident)*) => (paste::paste!{ - /// Same as [`Config::wrap_host_func`], except the closure asynchronously produces - /// its result. For more information see the [`Func`](crate::Func) documentation. - /// - /// Note: creating an engine will fail if an async host function is defined and - /// [async support](Config::async_support) is not enabled. - #[allow(non_snake_case)] - #[cfg(feature = "async")] - #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - pub fn []<$($args,)* R>( - &mut self, - module: &str, - name: &str, - func: impl for <'a> Fn(Caller<'a>, $($args),*) -> Box + 'a> + Send + Sync + 'static, - ) - where - $($args: WasmTy,)* - R: WasmRet, - { - // Defer the check for async support until engine creation time to not introduce an order dependency - self.host_funcs.insert( - module, - name, - true, - HostFunc::wrap(move |caller: Caller<'_>, $($args: $args),*| { - let store = caller.store().clone(); - debug_assert!(store.async_support()); - let mut future = Pin::from(func(caller, $($args),*)); - match store.block_on(future.as_mut()) { - Ok(ret) => ret.into_fallible(), - Err(e) => R::fallible_from_trap(e), - } - }) - ); - } - }) -} - /// Global configuration options used to create an [`Engine`](crate::Engine) /// and customize its behavior. /// @@ -385,7 +290,6 @@ pub struct Config { pub(crate) wasm_backtrace_details_env_used: bool, #[cfg(feature = "async")] pub(crate) async_stack_size: usize, - host_funcs: HostFuncMap, pub(crate) async_support: bool, } @@ -421,7 +325,6 @@ impl Config { features: WasmFeatures::default(), #[cfg(feature = "async")] async_stack_size: 2 << 20, - host_funcs: HostFuncMap::new(), async_support: false, }; ret.cranelift_debug_verifier(false); @@ -1190,107 +1093,6 @@ impl Config { self } - /// Defines a host function for the [`Config`] for the given callback. - /// - /// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function. - /// - /// Note that the implementation of `func` must adhere to the `ty` - /// signature given, error or traps may occur if it does not respect the - /// `ty` signature. - /// - /// Additionally note that this is quite a dynamic function since signatures - /// are not statically known. For performance reasons, it's recommended - /// to use [`Config::wrap_host_func`] if you can because with statically known - /// signatures the engine can optimize the implementation much more. - /// - /// The callback must be `Send` and `Sync` as it is shared between all engines created - /// from the `Config`. For more relaxed bounds, use [`Func::new`](crate::Func::new) to define the function. - pub fn define_host_func( - &mut self, - module: &str, - name: &str, - ty: FuncType, - func: impl Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static, - ) { - self.host_funcs - .insert(module, name, false, HostFunc::new(self, ty, func)); - } - - /// Defines an async host function for the [`Config`] for the given callback. - /// - /// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function. - /// - /// This function is the asynchronous analogue of [`Config::define_host_func`] and much of - /// that documentation applies to this as well. - /// - /// Additionally note that this is quite a dynamic function since signatures - /// are not statically known. For performance reasons, it's recommended - /// to use `Config::wrap$N_host_func_async` if you can because with statically known - /// signatures the engine can optimize the implementation much more. - /// - /// The callback must be `Send` and `Sync` as it is shared between all engines created - /// from the `Config`. For more relaxed bounds, use [`Func::new_async`](crate::Func::new_async) to define the function. - /// - /// Note: creating an engine will fail if an async host function is defined and [async support](Config::async_support) - /// is not enabled. - #[cfg(feature = "async")] - #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - pub fn define_host_func_async(&mut self, module: &str, name: &str, ty: FuncType, func: F) - where - F: for<'a> Fn( - Caller<'a>, - &'a [Val], - &'a mut [Val], - ) -> Box> + 'a> - + Send - + Sync - + 'static, - { - // Defer the check for async support until engine creation time to not introduce an order dependency - self.host_funcs.insert( - module, - name, - true, - HostFunc::new(self, ty, move |caller, params, results| { - let store = caller.store().clone(); - debug_assert!(store.async_support()); - let mut future = Pin::from(func(caller, params, results)); - match store.block_on(future.as_mut()) { - Ok(Ok(())) => Ok(()), - Ok(Err(trap)) | Err(trap) => Err(trap), - } - }), - ); - } - - /// Defines a host function for the [`Config`] from the given Rust closure. - /// - /// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function. - /// - /// See [`Func::wrap`](crate::Func::wrap) for information about accepted parameter and result types for the closure. - /// - /// The closure must be `Send` and `Sync` as it is shared between all engines created - /// from the `Config`. For more relaxed bounds, use [`Func::wrap`](crate::Func::wrap) to wrap the closure. - pub fn wrap_host_func( - &mut self, - module: &str, - name: &str, - func: impl IntoFunc + Send + Sync, - ) { - self.host_funcs - .insert(module, name, false, HostFunc::wrap(func)); - } - - for_each_function_signature!(generate_wrap_async_host_func); - - pub(crate) fn host_funcs(&self) -> impl Iterator { - self.host_funcs.iter() - } - - pub(crate) fn get_host_func(&self, module: &str, name: &str) -> Option<&HostFunc> { - self.host_funcs.get(module, name) - } - pub(crate) fn target_isa(&self) -> Box { self.isa_flags .clone() @@ -1303,18 +1105,6 @@ impl Config { self.isa_flags.clone().finish(settings::Flags::new(flags)) } - pub(crate) fn validate(&self) -> Result<()> { - // This is used to validate that the config is internally consistent prior to - // creating an engine using this config. - - // Check that there isn't a host function defined that requires async support enabled - if self.host_funcs.async_required() && !self.async_support { - bail!("an async host function cannot be defined without async support enabled in the config"); - } - - Ok(()) - } - pub(crate) fn build_compiler(&self, allocator: &dyn InstanceAllocator) -> Compiler { let isa = self.target_isa(); let mut tunables = self.tunables.clone(); diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 8876eb042dfb..6634e1e6500e 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -1,41 +1,11 @@ -use crate::signatures::{SignatureCollection, SignatureRegistry}; +use crate::signatures::SignatureRegistry; use crate::Config; use anyhow::Result; -use std::collections::HashMap; use std::sync::Arc; #[cfg(feature = "cache")] use wasmtime_cache::CacheConfig; use wasmtime_jit::Compiler; -use wasmtime_runtime::{debug_builtins, InstanceAllocator, InstanceHandle, VMCallerCheckedAnyfunc}; - -/// This is used as a Send+Sync wrapper around two data structures relating to -/// host functions defined on `Config`: -/// -/// * `anyfuncs` - this stores a mapping between the host function instance and -/// a `VMCallerCheckedAnyfunc` that can be used as the function's value in Wasmtime's ABI. -/// The address of the anyfunc needs to be stable, thus the boxed value. -/// -/// * `signatures` - this stores the collection of shared signatures registered for every -/// usable host functions with this engine. -struct EngineHostFuncs { - anyfuncs: HashMap>, - signatures: SignatureCollection, -} - -impl EngineHostFuncs { - fn new(registry: &SignatureRegistry) -> Self { - Self { - anyfuncs: HashMap::new(), - signatures: SignatureCollection::new(registry), - } - } -} - -// This is safe for send and sync as it is read-only once the -// engine is constructed and the host functions live with the config, -// which the engine keeps a strong reference to. -unsafe impl Send for EngineHostFuncs {} -unsafe impl Sync for EngineHostFuncs {} +use wasmtime_runtime::{debug_builtins, InstanceAllocator}; /// An `Engine` which is a global context for compilation and management of wasm /// modules. @@ -69,31 +39,19 @@ struct EngineInner { compiler: Compiler, allocator: Box, signatures: SignatureRegistry, - host_funcs: EngineHostFuncs, } impl Engine { /// Creates a new [`Engine`] with the specified compilation and /// configuration settings. pub fn new(config: &Config) -> Result { + // Ensure that wasmtime_runtime's signal handlers are configured. This + // is the per-program initialization required for handling traps, such + // as configuring signals, vectored exception handlers, etc. + wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_pc); debug_builtins::ensure_exported(); - config.validate()?; let allocator = config.build_allocator()?; let registry = SignatureRegistry::new(); - let mut host_funcs = EngineHostFuncs::new(®istry); - - // Register all the host function signatures with the collection - for func in config.host_funcs() { - let sig = host_funcs - .signatures - .register(func.ty.as_wasm_func_type(), func.trampoline); - - // Cloning the instance handle is safe as host functions outlive the engine - host_funcs.anyfuncs.insert( - unsafe { func.instance.clone() }, - Box::new(func.anyfunc(sig)), - ); - } Ok(Engine { inner: Arc::new(EngineInner { @@ -101,7 +59,6 @@ impl Engine { compiler: config.build_compiler(allocator.as_ref()), allocator, signatures: registry, - host_funcs, }), }) } @@ -134,21 +91,6 @@ impl Engine { &self.inner.signatures } - pub(crate) fn host_func_signatures(&self) -> &SignatureCollection { - &self.inner.host_funcs.signatures - } - - pub(crate) fn host_func_anyfunc( - &self, - instance: &InstanceHandle, - ) -> Option<&VMCallerCheckedAnyfunc> { - self.inner - .host_funcs - .anyfuncs - .get(instance) - .map(AsRef::as_ref) - } - /// Ahead-of-time (AOT) compiles a WebAssembly module. /// /// The `bytes` provided must be in one of two formats: diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index 665acf9f02cb..99dc44073fea 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -1,14 +1,13 @@ -use crate::memory::Memory; -use crate::trampoline::{generate_global_export, generate_table_export, StoreInstanceHandle}; -use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val}; +use crate::store::{StoreData, StoreOpaque, Stored}; +use crate::trampoline::{generate_global_export, generate_table_export}; +use crate::values::{from_checked_anyfunc, into_checked_anyfunc}; use crate::{ - ExternRef, ExternType, Func, GlobalType, Instance, Module, Mutability, Store, TableType, Trap, - ValType, + AsContext, AsContextMut, ExternRef, ExternType, Func, GlobalType, Instance, Memory, Module, + Mutability, TableType, Trap, Val, ValType, }; use anyhow::{anyhow, bail, Result}; use std::mem; use std::ptr; -use wasmtime_environ::wasm; use wasmtime_runtime::{self as runtime, InstanceHandle}; // Externals @@ -99,20 +98,21 @@ impl Extern { } /// Returns the type associated with this `Extern`. - pub fn ty(&self) -> ExternType { + pub fn ty(&self, store: impl AsContext) -> ExternType { + let store = store.as_context(); match self { - Extern::Func(ft) => ExternType::Func(ft.ty()), - Extern::Memory(ft) => ExternType::Memory(ft.ty()), - Extern::Table(tt) => ExternType::Table(tt.ty()), - Extern::Global(gt) => ExternType::Global(gt.ty()), - Extern::Instance(i) => ExternType::Instance(i.ty()), + Extern::Func(ft) => ExternType::Func(ft.ty(store)), + Extern::Memory(ft) => ExternType::Memory(ft.ty(store)), + Extern::Table(tt) => ExternType::Table(tt.ty(store)), + Extern::Global(gt) => ExternType::Global(gt.ty(store)), + Extern::Instance(i) => ExternType::Instance(i.ty(store)), Extern::Module(m) => ExternType::Module(m.ty()), } } pub(crate) unsafe fn from_wasmtime_export( - wasmtime_export: &wasmtime_runtime::Export, - store: &Store, + wasmtime_export: wasmtime_runtime::Export, + store: &mut StoreOpaque<'_>, ) -> Extern { match wasmtime_export { wasmtime_runtime::Export::Function(f) => { @@ -127,27 +127,20 @@ impl Extern { wasmtime_runtime::Export::Table(t) => { Extern::Table(Table::from_wasmtime_table(t, store)) } - wasmtime_runtime::Export::Instance(i) => { - Extern::Instance(Instance::from_wasmtime(i, store)) - } - wasmtime_runtime::Export::Module(m) => { - Extern::Module(m.downcast_ref::().unwrap().clone()) - } } } - pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool { - let my_store = match self { - Extern::Func(f) => f.store(), - Extern::Global(g) => &g.instance.store, - Extern::Memory(m) => &m.instance.store, - Extern::Table(t) => &t.instance.store, - Extern::Instance(i) => i.store(), + pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque<'_>) -> bool { + match self { + Extern::Func(f) => f.comes_from_same_store(store), + Extern::Global(g) => store.store_data().contains(g.0), + Extern::Memory(m) => m.comes_from_same_store(store), + Extern::Table(t) => store.store_data().contains(t.0), + Extern::Instance(i) => i.comes_from_same_store(store), // Modules don't live in stores right now, so they're compatible // with all stores. - Extern::Module(_) => return true, - }; - Store::same(my_store, store) + Extern::Module(_) => true, + } } pub(crate) fn desc(&self) -> &'static str { @@ -160,17 +153,6 @@ impl Extern { Extern::Module(_) => "module", } } - - pub(crate) fn wasmtime_export(&self) -> wasmtime_runtime::Export { - match self { - Extern::Func(f) => f.wasmtime_export().clone().into(), - Extern::Global(f) => f.wasmtime_export().clone().into(), - Extern::Table(f) => f.wasmtime_export().clone().into(), - Extern::Memory(f) => f.wasmtime_export().clone().into(), - Extern::Instance(f) => wasmtime_runtime::Export::Instance(f.wasmtime_export().clone()), - Extern::Module(f) => wasmtime_runtime::Export::Module(Box::new(f.clone())), - } - } } impl From for Extern { @@ -225,10 +207,7 @@ impl From for Extern { /// cloning process only performs a shallow clone, so two cloned `Global` /// instances are equivalent in their functionality. #[derive(Clone)] -pub struct Global { - instance: StoreInstanceHandle, - wasmtime_export: wasmtime_runtime::ExportGlobal, -} +pub struct Global(Stored); impl Global { /// Creates a new WebAssembly `global` value with the provide type `ty` and @@ -242,46 +221,36 @@ impl Global { /// /// Returns an error if the `ty` provided does not match the type of the /// value `val`. - pub fn new(store: &Store, ty: GlobalType, val: Val) -> Result { + pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result { + Global::_new(&mut store.as_context_mut().opaque(), ty, val) + } + + fn _new(store: &mut StoreOpaque<'_>, ty: GlobalType, val: Val) -> Result { if !val.comes_from_same_store(store) { bail!("cross-`Store` globals are not supported"); } if val.ty() != *ty.content() { bail!("value provided does not match the type of this global"); } - let (instance, wasmtime_export) = generate_global_export(store, &ty, val)?; - Ok(Global { - instance, - wasmtime_export, - }) + unsafe { + let wasmtime_export = generate_global_export(store, &ty, val)?; + Ok(Global::from_wasmtime_global(wasmtime_export, store)) + } } /// Returns the underlying type of this `global`. - pub fn ty(&self) -> GlobalType { - // The original export is coming from wasmtime_runtime itself we should - // support all the types coming out of it, so assert such here. - GlobalType::from_wasmtime_global(&self.wasmtime_export.global) - } - - /// Returns the value type of this `global`. - pub fn val_type(&self) -> ValType { - ValType::from_wasm_type(&self.wasmtime_export.global.wasm_ty) - } - - /// Returns the underlying mutability of this `global`. - pub fn mutability(&self) -> Mutability { - if self.wasmtime_export.global.mutability { - Mutability::Var - } else { - Mutability::Const - } + pub fn ty(&self, store: impl AsContext) -> GlobalType { + let store = store.as_context(); + let ty = &store[self.0].global; + GlobalType::from_wasmtime_global(&ty) } /// Returns the current [`Val`] of this global. - pub fn get(&self) -> Val { + pub fn get(&self, mut store: impl AsContextMut) -> Val { unsafe { - let definition = &mut *self.wasmtime_export.definition; - match self.val_type() { + let store = store.as_context_mut(); + let definition = &*store[self.0].definition; + match self.ty(&store).content() { ValType::I32 => Val::from(*definition.as_i32()), ValType::I64 => Val::from(*definition.as_i64()), ValType::F32 => Val::F32(*definition.as_u32()), @@ -293,7 +262,7 @@ impl Global { .map(|inner| ExternRef { inner }), ), ValType::FuncRef => { - from_checked_anyfunc(definition.as_anyfunc() as *mut _, &self.instance.store) + from_checked_anyfunc(definition.as_anyfunc() as *mut _, &mut store.opaque()) } ty => unimplemented!("Global::get for {:?}", ty), } @@ -306,19 +275,22 @@ impl Global { /// /// Returns an error if this global has a different type than `Val`, or if /// it's not a mutable global. - pub fn set(&self, val: Val) -> Result<()> { - if self.mutability() != Mutability::Var { + pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> { + let store = store.as_context_mut(); + let ty = self.ty(&store); + if ty.mutability() != Mutability::Var { bail!("immutable global cannot be set"); } - let ty = self.val_type(); - if val.ty() != ty { + let ty = ty.content(); + if val.ty() != *ty { bail!("global of type {:?} cannot be set to {:?}", ty, val.ty()); } - if !val.comes_from_same_store(&self.instance.store) { + let mut store = store.opaque(); + if !val.comes_from_same_store(&store) { bail!("cross-`Store` values are not supported"); } unsafe { - let definition = &mut *self.wasmtime_export.definition; + let definition = &mut *store[self.0].definition; match val { Val::I32(i) => *definition.as_i32_mut() = i, Val::I64(i) => *definition.as_i64_mut() = i, @@ -326,14 +298,10 @@ impl Global { Val::F64(f) => *definition.as_u64_mut() = f, Val::FuncRef(f) => { *definition.as_anyfunc_mut() = f.map_or(ptr::null(), |f| { - f.caller_checked_anyfunc().as_ptr() as *const _ + f.caller_checked_anyfunc(&mut store).as_ptr() as *const _ }); } Val::ExternRef(x) => { - // In case the old value's `Drop` implementation is - // re-entrant and tries to touch this global again, do a - // replace, and then drop. This way no one can observe a - // halfway-deinitialized value. let old = mem::replace(definition.as_externref_mut(), x.map(|x| x.inner)); drop(old); } @@ -344,28 +312,24 @@ impl Global { } pub(crate) unsafe fn from_wasmtime_global( - wasmtime_export: &wasmtime_runtime::ExportGlobal, - store: &Store, + wasmtime_export: wasmtime_runtime::ExportGlobal, + store: &mut StoreOpaque<'_>, ) -> Global { - Global { - instance: store.existing_vmctx(wasmtime_export.vmctx), - wasmtime_export: wasmtime_export.clone(), - } + Global(store.store_data_mut().insert(wasmtime_export)) } - pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Global { - &self.wasmtime_export.global + pub(crate) fn wasmtime_ty<'a>( + &self, + data: &'a StoreData, + ) -> &'a wasmtime_environ::wasm::Global { + &data[self.0].global } - pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMGlobalImport { + pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMGlobalImport { wasmtime_runtime::VMGlobalImport { - from: self.wasmtime_export.definition, + from: store[self.0].definition, } } - - pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportGlobal { - &self.wasmtime_export - } } /// A WebAssembly `table`, or an array of values. @@ -384,21 +348,7 @@ impl Global { /// cloning process only performs a shallow clone, so two cloned `Table` /// instances are equivalent in their functionality. #[derive(Clone)] -pub struct Table { - instance: StoreInstanceHandle, - wasmtime_export: wasmtime_runtime::ExportTable, -} - -fn set_table_item( - instance: &InstanceHandle, - table_index: wasm::DefinedTableIndex, - item_index: u32, - item: runtime::TableElement, -) -> Result<()> { - instance - .table_set(table_index, item_index, item) - .map_err(|()| anyhow!("table element index out of bounds")) -} +pub struct Table(Stored); impl Table { /// Creates a new `Table` with the given parameters. @@ -412,8 +362,19 @@ impl Table { /// # Errors /// /// Returns an error if `init` does not match the element type of the table. - pub fn new(store: &Store, ty: TableType, init: Val) -> Result
{ - let (instance, wasmtime_export) = generate_table_export(store, &ty)?; + pub fn new(mut store: impl AsContextMut, ty: TableType, init: Val) -> Result
{ + Table::_new(&mut store.as_context_mut().opaque(), ty, init) + } + + fn _new(store: &mut StoreOpaque, ty: TableType, init: Val) -> Result
{ + if init.ty() != *ty.element() { + bail!( + "table initialization value type {:?} does not have expected type {:?}", + init.ty(), + ty.element(), + ); + } + let wasmtime_export = generate_table_export(store, &ty)?; let init: runtime::TableElement = match ty.element() { ValType::FuncRef => into_checked_anyfunc(init, store)?.into(), @@ -428,41 +389,46 @@ impl Table { }; // Initialize entries with the init value. - let definition = unsafe { &*wasmtime_export.definition }; - let index = instance.table_index(definition); - for i in 0..definition.current_elements { - set_table_item(&instance, index, i, init.clone())?; - } + unsafe { + let table = Table::from_wasmtime_table(wasmtime_export, store); + (*table.wasmtime_table(store)) + .fill(0, init, ty.limits().min()) + .map_err(Trap::from_runtime)?; - Ok(Table { - instance, - wasmtime_export, - }) + Ok(table) + } } /// Returns the underlying type of this table, including its element type as /// well as the maximum/minimum lower bounds. - pub fn ty(&self) -> TableType { - TableType::from_wasmtime_table(&self.wasmtime_export.table.table) + pub fn ty(&self, store: impl AsContext) -> TableType { + let store = store.as_context(); + let ty = &store[self.0].table.table; + TableType::from_wasmtime_table(ty) } - fn wasmtime_table_index(&self) -> wasm::DefinedTableIndex { - unsafe { self.instance.table_index(&*self.wasmtime_export.definition) } + fn wasmtime_table(&self, store: &mut StoreOpaque<'_>) -> *mut runtime::Table { + unsafe { + let export = &store[self.0]; + let mut handle = InstanceHandle::from_vmctx(export.vmctx); + let idx = handle.table_index(&*export.definition); + handle.get_defined_table(idx) + } } /// Returns the table element value at `index`. /// /// Returns `None` if `index` is out of bounds. - pub fn get(&self, index: u32) -> Option { - let table_index = self.wasmtime_table_index(); - let item = self.instance.table_get(table_index, index)?; - match item { - runtime::TableElement::FuncRef(f) => { - Some(unsafe { from_checked_anyfunc(f, &self.instance.store) }) - } - runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)), - runtime::TableElement::ExternRef(Some(x)) => { - Some(Val::ExternRef(Some(ExternRef { inner: x }))) + pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Option { + let mut store = store.as_context_mut().opaque(); + let table = self.wasmtime_table(&mut store); + unsafe { + match (*table).get(index)? { + runtime::TableElement::FuncRef(f) => Some(from_checked_anyfunc(f, &mut store)), + runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)), + runtime::TableElement::ExternRef(Some(x)) => { + Some(Val::ExternRef(Some(ExternRef { inner: x }))) + } } } } @@ -473,22 +439,22 @@ impl Table { /// /// Returns an error if `index` is out of bounds or if `val` does not have /// the right type to be stored in this table. - pub fn set(&self, index: u32, val: Val) -> Result<()> { - if !val.comes_from_same_store(&self.instance.store) { - bail!("cross-`Store` values are not supported in tables"); + pub fn set(&self, mut store: impl AsContextMut, index: u32, val: Val) -> Result<()> { + let ty = self.ty(&store).element().clone(); + let mut store = store.as_context_mut().opaque(); + let val = val.into_table_element(&mut store, ty)?; + let table = self.wasmtime_table(&mut store); + unsafe { + (*table) + .set(index, val) + .map_err(|()| anyhow!("table element index out of bounds")) } - let table_index = self.wasmtime_table_index(); - set_table_item( - &self.instance, - table_index, - index, - val.into_table_element()?, - ) } /// Returns the current size of this table. - pub fn size(&self) -> u32 { - unsafe { (*self.wasmtime_export.definition).current_elements } + pub fn size(&self, store: impl AsContext) -> u32 { + let store = store.as_context(); + unsafe { (*store[self.0].definition).current_elements } } /// Grows the size of this table by `delta` more elements, initialization @@ -501,31 +467,20 @@ impl Table { /// Returns an error if the table cannot be grown by `delta`, for example /// if it would cause the table to exceed its maximum size. Also returns an /// error if `init` is not of the right type. - pub fn grow(&self, delta: u32, init: Val) -> Result { - let index = self.wasmtime_table_index(); - let orig_size = match self.ty().element() { - ValType::FuncRef => { - let init = into_checked_anyfunc(init, &self.instance.store)?; - self.instance.defined_table_grow(index, delta, init.into()) - } - ValType::ExternRef => { - let init = match init { - Val::ExternRef(Some(x)) => Some(x.inner), - Val::ExternRef(None) => None, - _ => bail!("incorrect init value for growing table"), - }; - self.instance.defined_table_grow( - index, - delta, - runtime::TableElement::ExternRef(init), - ) + pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Val) -> Result { + let ty = self.ty(&store).element().clone(); + let mut store = store.as_context_mut().opaque(); + let init = init.into_table_element(&mut store, ty)?; + let table = self.wasmtime_table(&mut store); + unsafe { + match (*table).grow(delta, init, store.limiter()) { + Some(size) => { + let vm = (*table).vmtable(); + *store[self.0].definition = vm; + Ok(size) + } + None => bail!("failed to grow table by `{}`", delta), } - _ => unreachable!("only `funcref` and `externref` tables are supported"), - }; - if let Some(size) = orig_size { - Ok(size) - } else { - bail!("failed to grow table by `{}`", delta) } } @@ -537,31 +492,25 @@ impl Table { /// Returns an error if the range is out of bounds of either the source or /// destination tables. pub fn copy( + mut store: impl AsContextMut, dst_table: &Table, dst_index: u32, src_table: &Table, src_index: u32, len: u32, ) -> Result<()> { - if !Store::same(&dst_table.instance.store, &src_table.instance.store) { - bail!("cross-`Store` table copies are not supported"); - } - - if dst_table.ty() != src_table.ty() { + if dst_table.ty(&store).element() != src_table.ty(&store).element() { bail!("tables do not have the same element type"); } - // NB: We must use the `dst_table`'s `wasmtime_handle` for the - // `dst_table_index` and vice versa for `src_table` since each table can - // come from different modules. - let dst_table_index = dst_table.wasmtime_table_index(); - let dst_table_index = dst_table.instance.get_defined_table(dst_table_index); + let mut store = store.as_context_mut().opaque(); - let src_table_index = src_table.wasmtime_table_index(); - let src_table_index = src_table.instance.get_defined_table(src_table_index); - - runtime::Table::copy(dst_table_index, src_table_index, dst_index, src_index, len) - .map_err(|e| Trap::from_runtime(&dst_table.instance.store, e))?; + let dst = dst_table.wasmtime_table(&mut store); + let src = src_table.wasmtime_table(&mut store); + unsafe { + runtime::Table::copy(dst, src, dst_index, src_index, len) + .map_err(Trap::from_runtime)?; + } Ok(()) } @@ -577,49 +526,37 @@ impl Table { /// * the region to be filled is out of bounds, or /// /// * `val` comes from a different `Store` from this table. - pub fn fill(&self, dst: u32, val: Val, len: u32) -> Result<()> { - if !val.comes_from_same_store(&self.instance.store) { - bail!("cross-`Store` table fills are not supported"); - } + pub fn fill(&self, mut store: impl AsContextMut, dst: u32, val: Val, len: u32) -> Result<()> { + let ty = self.ty(&store).element().clone(); + let mut store = store.as_context_mut().opaque(); + let val = val.into_table_element(&mut store, ty)?; - // Ensure the fill value is the correct type - if self.ty().element() != &val.ty() { - bail!("mismatched element fill type"); + let table = self.wasmtime_table(&mut store); + unsafe { + (*table).fill(dst, val, len).map_err(Trap::from_runtime)?; } - let table_index = self.wasmtime_table_index(); - self.instance - .handle - .defined_table_fill(table_index, dst, val.into_table_element()?, len) - .map_err(|e| Trap::from_runtime(&self.instance.store, e))?; - Ok(()) } pub(crate) unsafe fn from_wasmtime_table( - wasmtime_export: &wasmtime_runtime::ExportTable, - store: &Store, + wasmtime_export: wasmtime_runtime::ExportTable, + store: &mut StoreOpaque<'_>, ) -> Table { - Table { - instance: store.existing_vmctx(wasmtime_export.vmctx), - wasmtime_export: wasmtime_export.clone(), - } + Table(store.store_data_mut().insert(wasmtime_export)) } - pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Table { - &self.wasmtime_export.table.table + pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::wasm::Table { + &data[self.0].table.table } - pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMTableImport { + pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMTableImport { + let export = &store[self.0]; wasmtime_runtime::VMTableImport { - from: self.wasmtime_export.definition, - vmctx: self.wasmtime_export.vmctx, + from: export.definition, + vmctx: export.vmctx, } } - - pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportTable { - &self.wasmtime_export - } } // Exports @@ -651,8 +588,8 @@ impl<'instance> Export<'instance> { } /// Return the `ExternType` of this export. - pub fn ty(&self) -> ExternType { - self.definition.ty() + pub fn ty(&self, store: impl AsContext) -> ExternType { + self.definition.ty(store) } /// Consume this `Export` and return the contained `Extern`. diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 0319ee000229..25f6298ab993 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -1,15 +1,20 @@ -use crate::trampoline::StoreInstanceHandle; -use crate::{Config, Extern, FuncType, Store, Trap, Val, ValType}; +use crate::store::{StoreData, StoreOpaque, StoreOpaqueSend, Stored}; +use crate::{ + AsContext, AsContextMut, Engine, Extern, FuncType, StoreContext, StoreContextMut, Trap, Val, + ValType, +}; use anyhow::{bail, Context as _, Result}; use smallvec::{smallvec, SmallVec}; use std::cmp::max; +use std::error::Error; use std::fmt; use std::future::Future; use std::mem; use std::panic::{self, AssertUnwindSafe}; use std::pin::Pin; -use std::ptr::{self, NonNull}; +use std::ptr::NonNull; use std::sync::atomic::Ordering::Relaxed; +use std::sync::Arc; use wasmtime_environ::wasm::{EntityIndex, FuncIndex}; use wasmtime_runtime::{ raise_user_trap, ExportFunction, InstanceAllocator, InstanceHandle, OnDemandInstanceAllocator, @@ -17,127 +22,6 @@ use wasmtime_runtime::{ VMTrampoline, }; -/// Represents a host function. -/// -/// This differs from `Func` in that it is not associated with a `Store`. -/// Host functions are associated with a `Config`. -pub(crate) struct HostFunc { - pub ty: FuncType, - pub instance: InstanceHandle, - pub trampoline: VMTrampoline, -} - -impl HostFunc { - /// Creates a new host function from a callback. - /// - /// This is analogous to [`Func::new`]. - pub fn new( - config: &Config, - ty: FuncType, - func: impl Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static, - ) -> Self { - let ty_clone = ty.clone(); - - // Create a trampoline that converts raw u128 values to `Val` - let func = Box::new(move |caller_vmctx, values_vec: *mut u128| { - // Lookup the last registered store as host functions have no associated store - let store = wasmtime_runtime::with_last_info(|last| { - last.and_then(|s| s.downcast_ref::()) - .cloned() - .expect("Host function called without thread state") - }); - - Func::invoke(&store, &ty_clone, caller_vmctx, values_vec, &func) - }); - - let (instance, trampoline) = crate::trampoline::create_function(&ty, func, config, None) - .expect("failed to create host function"); - - Self { - ty, - instance, - trampoline, - } - } - - /// Creates a new host function from wrapping a closure. - /// - /// This is analogous to [`Func::wrap`]. - pub fn wrap(func: impl IntoFunc + Send + Sync) -> Self { - let (ty, instance, trampoline) = func.into_func(None); - - Self { - ty, - instance, - trampoline, - } - } - - /// Gets a caller-checked anyfunc for this host function given a shared signature index. - /// - /// The shared signature index must have been registered for the signature of - /// this host function. - pub fn anyfunc(&self, sig: VMSharedSignatureIndex) -> VMCallerCheckedAnyfunc { - let mut anyfunc = match self - .instance - .lookup_by_declaration(&EntityIndex::Function(FuncIndex::from_u32(0))) - { - wasmtime_runtime::Export::Function(f) => unsafe { f.anyfunc.as_ref() }.clone(), - _ => unreachable!(), - }; - - anyfunc.type_index = sig; - anyfunc - } - - /// Converts a `HostFunc` to a `Func`. - /// - /// # Safety - /// - /// This is unsafe as the caller must ensure that the store's config defines this `HostFunc`. - pub unsafe fn to_func(&self, store: &Store) -> Func { - let instance = StoreInstanceHandle { - store: store.clone(), - // This clone of the instance handle should be safe because it should not be deallocated - // until all configs that reference it are dropped. - // A config will not drop until all stores referencing the config are dropped. - handle: self.instance.clone(), - }; - - let export = ExportFunction { - anyfunc: store - .engine() - .host_func_anyfunc(&self.instance) - .unwrap() - .into(), - }; - - Func { - instance, - trampoline: self.trampoline, - export, - } - } -} - -impl Drop for HostFunc { - fn drop(&mut self) { - // Host functions are always allocated with the default (on-demand) allocator - unsafe { OnDemandInstanceAllocator::default().deallocate(&self.instance) } - } -} - -// A note about thread safety of host function instance handles: -// Host functions must be `Send+Sync` because `Module` must be `Send+Sync`. -// However, the underlying runtime `Instance` is not `Send` or `Sync`. -// For this to be safe, we must ensure that the runtime instance's state is not mutated for host functions. -// Additionally, we add the `Send+Sync` bounds to `define_host_func` and `wrap_host_func` so -// that the underlying closure stored in the instance's host state is safe to call from any thread. -// Therefore, these impls should be safe because the underlying instance is not mutated and -// the closures backing the host functions are required to be `Send+Sync`. -unsafe impl Send for HostFunc {} -unsafe impl Sync for HostFunc {} - /// A WebAssembly function which can be called. /// /// This type can represent a number of callable items, such as: @@ -316,11 +200,16 @@ unsafe impl Sync for HostFunc {} /// # Ok(()) /// # } /// ``` -#[derive(Clone)] -pub struct Func { - instance: StoreInstanceHandle, - trampoline: VMTrampoline, - export: ExportFunction, +#[derive(Debug, Copy, Clone)] +pub struct Func(Stored); + +pub(crate) enum FuncData { + StoreOwned { + trampoline: VMTrampoline, + export: ExportFunction, + }, + SharedHost(Arc), + Host(HostFunc), } macro_rules! for_each_function_signature { @@ -360,20 +249,18 @@ macro_rules! generate_wrap_async_func { #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] pub fn []( - store: &Store, - state: T, - func: impl for<'a> Fn(Caller<'a>, &'a T, $($args),*) -> Box + 'a> + 'static, + store: impl AsContextMut, + func: impl for<'a> Fn(Caller<'a, T>, $($args),*) -> Box + Send + 'a> + Send + Sync + 'static, ) -> Func where - T: 'static, $($args: WasmTy,)* R: WasmRet, { - assert!(store.async_support(), concat!("cannot use `wrap", $num, "_async` without enabling async support on the config")); - Func::wrap(store, move |caller: Caller<'_>, $($args: $args),*| { - let store = caller.store().clone(); - let mut future = Pin::from(func(caller, &state, $($args),*)); - match store.block_on(future.as_mut()) { + assert!(store.as_context().async_support(), concat!("cannot use `wrap", $num, "_async` without enabling async support on the config")); + Func::wrap(store, move |mut caller: Caller<'_, T>, $($args: $args),*| { + let async_cx = caller.store.as_context_mut().opaque().async_cx(); + let mut future = Pin::from(func(caller, $($args),*)); + match unsafe { async_cx.block_on(future.as_mut()) } { Ok(ret) => ret.into_fallible(), Err(e) => R::fallible_from_trap(e), } @@ -406,41 +293,18 @@ impl Func { /// are not statically known. For a more performant `Func` it's recommended /// to use [`Func::wrap`] if you can because with statically known /// signatures the engine can optimize the implementation much more. - pub fn new( - store: &Store, + pub fn new( + mut store: impl AsContextMut, ty: FuncType, - func: impl Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap> + 'static, + func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static, ) -> Self { - let ty_clone = ty.clone(); + let mut store = store.as_context_mut().opaque(); - // Create a trampoline that converts raw u128 values to `Val` - let func = Box::new(move |caller_vmctx, values_vec: *mut u128| { - // Lookup the last registered store as host functions have no associated store - let store = wasmtime_runtime::with_last_info(|last| { - last.and_then(|s| s.downcast_ref::()) - .cloned() - .expect("function called without thread state") - }); - - Func::invoke(&store, &ty_clone, caller_vmctx, values_vec, &func) - }); - - let (instance, trampoline) = - crate::trampoline::create_function(&ty, func, store.engine().config(), Some(store)) - .expect("failed to create function"); - - let idx = EntityIndex::Function(FuncIndex::from_u32(0)); - let (instance, export) = match instance.lookup_by_declaration(&idx) { - wasmtime_runtime::Export::Function(f) => { - (unsafe { store.add_instance(instance, true) }, f) - } - _ => unreachable!(), - }; - - Func { - instance, - trampoline, - export, + // part of this unsafety is about matching the `T` to a `Store`, + // which is done through the `AsContextMut` bound above. + unsafe { + let host = HostFunc::new(store.engine(), ty, func); + host.into_func(&mut store) } } @@ -516,25 +380,25 @@ impl Func { /// ``` #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - pub fn new_async(store: &Store, ty: FuncType, state: T, func: F) -> Func + pub fn new_async(store: impl AsContextMut, ty: FuncType, func: F) -> Func where - T: 'static, F: for<'a> Fn( - Caller<'a>, - &'a T, + Caller<'a, T>, &'a [Val], &'a mut [Val], - ) -> Box> + 'a> + ) -> Box> + Send + 'a> + + Send + + Sync + 'static, { assert!( - store.async_support(), + store.as_context().async_support(), "cannot use `new_async` without enabling async support in the config" ); - Func::new(store, ty, move |caller, params, results| { - let store = caller.store().clone(); - let mut future = Pin::from(func(caller, &state, params, results)); - match store.block_on(future.as_mut()) { + Func::new(store, ty, move |mut caller, params, results| { + let async_cx = caller.store.as_context_mut().opaque().async_cx(); + let mut future = Pin::from(func(caller, params, results)); + match unsafe { async_cx.block_on(future.as_mut()) } { Ok(Ok(())) => Ok(()), Ok(Err(trap)) | Err(trap) => Err(trap), } @@ -542,14 +406,13 @@ impl Func { } pub(crate) unsafe fn from_caller_checked_anyfunc( - store: &Store, + store: &mut StoreOpaque, anyfunc: *mut VMCallerCheckedAnyfunc, ) -> Option { let anyfunc = NonNull::new(anyfunc)?; debug_assert!(anyfunc.as_ref().type_index != VMSharedSignatureIndex::default()); let export = ExportFunction { anyfunc }; - let f = Func::from_wasmtime_function(&export, store); - Some(f) + Some(Func::from_wasmtime_function(export, store)) } /// Creates a new `Func` from the given Rust closure. @@ -746,52 +609,38 @@ impl Func { /// # Ok(()) /// # } /// ``` - pub fn wrap(store: &Store, func: impl IntoFunc) -> Func { - let (_, instance, trampoline) = func.into_func(Some(store)); - - let (instance, export) = unsafe { - let idx = EntityIndex::Function(FuncIndex::from_u32(0)); - match instance.lookup_by_declaration(&idx) { - wasmtime_runtime::Export::Function(f) => (store.add_instance(instance, true), f), - _ => unreachable!(), - } - }; - - Func { - instance, - export, - trampoline, + pub fn wrap( + mut store: impl AsContextMut, + func: impl IntoFunc, + ) -> Func { + let mut store = store.as_context_mut().opaque(); + // part of this unsafety is about matching the `T` to a `Store`, + // which is done through the `AsContextMut` bound above. + unsafe { + let host = HostFunc::wrap(store.engine(), func); + host.into_func(&mut store) } } for_each_function_signature!(generate_wrap_async_func); - pub(crate) fn sig_index(&self) -> VMSharedSignatureIndex { - unsafe { self.export.anyfunc.as_ref().type_index } - } - /// Returns the underlying wasm type that this `Func` has. - pub fn ty(&self) -> FuncType { + pub fn ty(&self, store: impl AsContext) -> FuncType { // Signatures should always be registered in the engine's registry of // shared signatures, so we should be able to unwrap safely here. + let store = store.as_context(); + let sig_index = unsafe { store[self.0].export().anyfunc.as_ref().type_index }; FuncType::from_wasm_func_type( - self.instance - .store + store .engine() .signatures() - .lookup_type(self.sig_index()) + .lookup_type(sig_index) .expect("signature should be registered"), ) } - /// Returns the number of parameters that this function takes. - pub fn param_arity(&self) -> usize { - self.ty().params().len() - } - - /// Returns the number of results this function produces. - pub fn result_arity(&self) -> usize { - self.ty().results().len() + pub(crate) fn sig_index(&self, data: &StoreData) -> VMSharedSignatureIndex { + unsafe { data[self.0].export().anyfunc.as_ref().type_index } } /// Invokes this function with the `params` given, returning the results and @@ -806,12 +655,13 @@ impl Func { /// This function will panic if called on a function belonging to an async /// store. Asynchronous stores must always use `call_async`. /// initiates a panic. - pub fn call(&self, params: &[Val]) -> Result> { + pub fn call(&self, mut store: impl AsContextMut, params: &[Val]) -> Result> { assert!( - !cfg!(feature = "async") || !self.store().async_support(), + !cfg!(feature = "async") || !store.as_context().async_support(), "must use `call_async` when async support is enabled on the config", ); - self._call(params) + let my_ty = self.ty(&store); + self.call_impl(&mut store.as_context_mut().opaque(), my_ty, params) } /// Invokes this function with the `params` given, returning the results @@ -837,22 +687,50 @@ impl Func { /// only works with functions defined within an asynchronous store. #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - pub async fn call_async(&self, params: &[Val]) -> Result> { + pub async fn call_async( + &self, + mut store: impl AsContextMut, + params: &[Val], + ) -> Result> + where + T: Send, + { + let my_ty = self.ty(&store); + self._call_async(store.as_context_mut().opaque_send(), my_ty, params) + .await + } + + #[cfg(feature = "async")] + async fn _call_async( + &self, + mut store: StoreOpaqueSend<'_>, + my_ty: FuncType, + params: &[Val], + ) -> Result> { assert!( - self.store().async_support(), + store.async_support(), "cannot use `call_async` without enabling async support in the config", ); - let result = self.store().on_fiber(|| self._call(params)).await??; + let result = store + .on_fiber(|store| self.call_impl(store, my_ty, params)) + .await??; Ok(result) } - fn _call(&self, params: &[Val]) -> Result> { + fn call_impl( + &self, + store: &mut StoreOpaque<'_>, + my_ty: FuncType, + params: &[Val], + ) -> Result> { + let data = &store[self.0]; + let trampoline = data.trampoline(); + let anyfunc = data.export().anyfunc; // We need to perform a dynamic check that the arguments given to us // match the signature of this function and are appropriate to pass to // this function. This involves checking to make sure we have the right // number and types of arguments as well as making sure everything is // from the same `Store`. - let my_ty = self.ty(); if my_ty.params().len() != params.len() { bail!( "expected {} arguments, got {}", @@ -873,21 +751,21 @@ impl Func { ty ); } - if !arg.comes_from_same_store(&self.instance.store) { + if !arg.comes_from_same_store(store) { bail!("cross-`Store` values are not currently supported"); } unsafe { - arg.write_value_to(&self.instance.store, slot); + arg.write_value_to(store, slot); } } // Call the trampoline. unsafe { - let anyfunc = self.export.anyfunc.as_ref(); - invoke_wasm_and_catch_traps(&self.instance.store, || { - (self.trampoline)( + let anyfunc = anyfunc.as_ref(); + invoke_wasm_and_catch_traps(store, |callee| { + trampoline( anyfunc.vmctx, - ptr::null_mut(), + callee, anyfunc.func_ptr.as_ptr(), values_vec.as_mut_ptr(), ) @@ -899,36 +777,34 @@ impl Func { for (index, ty) in my_ty.results().enumerate() { unsafe { let ptr = values_vec.as_ptr().add(index); - results.push(Val::read_value_from(&self.instance.store, ptr, ty)); + results.push(Val::read_value_from(store, ptr, ty)); } } Ok(results.into()) } - pub(crate) fn caller_checked_anyfunc(&self) -> NonNull { - self.export.anyfunc + #[inline] + pub(crate) fn caller_checked_anyfunc( + &self, + store: &StoreOpaque, + ) -> NonNull { + store[self.0].export().anyfunc } - pub(crate) unsafe fn from_wasmtime_function(export: &ExportFunction, store: &Store) -> Self { + pub(crate) unsafe fn from_wasmtime_function( + export: ExportFunction, + store: &mut StoreOpaque, + ) -> Self { let anyfunc = export.anyfunc.as_ref(); - - Func { - instance: store.existing_vmctx(anyfunc.vmctx), - export: export.clone(), - trampoline: store.lookup_trampoline(&*anyfunc), - } - } - - /// Get a reference to this function's store. - #[inline] - pub fn store(&self) -> &Store { - &self.instance.store + let trampoline = store.lookup_trampoline(&*anyfunc); + let data = FuncData::StoreOwned { trampoline, export }; + Func(store.store_data_mut().insert(data)) } - pub(crate) fn vmimport(&self) -> VMFunctionImport { + pub(crate) fn vmimport(&self, store: &mut StoreOpaque<'_>) -> VMFunctionImport { unsafe { - let f = self.caller_checked_anyfunc(); + let f = self.caller_checked_anyfunc(store); VMFunctionImport { body: f.as_ref().func_ptr, vmctx: f.as_ref().vmctx, @@ -936,16 +812,15 @@ impl Func { } } - pub(crate) fn wasmtime_export(&self) -> &ExportFunction { - &self.export + pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { + store.store_data().contains(self.0) } - fn invoke( - store: &Store, + fn invoke( + mut caller: Caller<'_, T>, ty: &FuncType, - caller_vmctx: *mut VMContext, values_vec: *mut u128, - func: &dyn Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap>, + func: &dyn Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap>, ) -> Result<(), Trap> { // We have a dynamic guarantee that `values_vec` has the right // number of arguments and the right types of arguments. As a result @@ -953,9 +828,10 @@ impl Func { const STACK_ARGS: usize = 4; const STACK_RETURNS: usize = 2; let mut args: SmallVec<[Val; STACK_ARGS]> = SmallVec::with_capacity(ty.params().len()); + let mut store = caller.store.as_context_mut().opaque(); for (i, ty) in ty.params().enumerate() { unsafe { - let val = Val::read_value_from(store, values_vec.add(i), ty); + let val = Val::read_value_from(&mut store, values_vec.add(i), ty); args.push(val); } } @@ -963,32 +839,26 @@ impl Func { let mut returns: SmallVec<[Val; STACK_RETURNS]> = smallvec![Val::null(); ty.results().len()]; - func( - Caller { - store, - caller_vmctx, - }, - &args, - &mut returns, - )?; + func(caller.sub_caller(), &args, &mut returns)?; // Unlike our arguments we need to dynamically check that the return // values produced are correct. There could be a bug in `func` that // produces the wrong number, wrong types, or wrong stores of // values, and we need to catch that here. + let mut store = caller.store.as_context_mut().opaque(); for (i, (ret, ty)) in returns.into_iter().zip(ty.results()).enumerate() { if ret.ty() != ty { return Err(Trap::new( "function attempted to return an incompatible value", )); } - if !ret.comes_from_same_store(store) { + if !ret.comes_from_same_store(&store) { return Err(Trap::new( "cross-`Store` values are not currently supported", )); } unsafe { - ret.write_value_to(store, values_vec.add(i)); + ret.write_value_to(&mut store, values_vec.add(i)); } } @@ -1100,65 +970,54 @@ impl Func { /// # Ok(()) /// # } /// ``` - pub fn typed(&self) -> Result<&TypedFunc> + pub fn typed(&self, store: S) -> Result> where Params: WasmParams, Results: WasmResults, + S: AsContext, { // First type-check that the params/results are all valid... - let ty = self.ty(); + let ty = self.ty(store); Params::typecheck(ty.params()).context("type mismatch with parameters")?; Results::typecheck(ty.results()).context("type mismatch with results")?; // ... then we can construct the typed version of this function // (unsafely), which should be safe since we just did the type check above. - unsafe { Ok(self.typed_unchecked::()) } - } - - /// An unchecked version of [`Func::typed`] which does not perform a - /// typecheck and simply assumes that the type declared here matches the - /// type of this function. - /// - /// The semantics of this function are the same as [`Func::typed`] except - /// that no error is returned because no typechecking is done. - /// - /// # Unsafety - /// - /// This function only safe to call if `typed` would otherwise return `Ok` - /// for the same `Params` and `Results` specified. If `typed` would return - /// an error then the returned `TypedFunc` is memory unsafe to invoke. - pub unsafe fn typed_unchecked(&self) -> &TypedFunc - where - Params: WasmParams, - Results: WasmResults, - { - assert_eq!( - mem::size_of::>(), - mem::size_of_val(self) - ); - &*(self as *const Func as *const TypedFunc) - } -} - -impl fmt::Debug for Func { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Func") + unsafe { Ok(TypedFunc::new_unchecked(*self)) } } } +/// Prepares for entrance into WebAssembly. +/// +/// This function will set up context such that `closure` is allowed to call a +/// raw trampoline or a raw WebAssembly function. This *must* be called to do +/// things like catch traps and set up GC properly. +/// +/// The `closure` provided receives a default "callee" `VMContext` parameter it +/// can pass to the called wasm function, if desired. #[inline] pub(crate) fn invoke_wasm_and_catch_traps( - store: &Store, - closure: impl FnMut(), + store: &mut StoreOpaque<'_>, + closure: impl FnMut(*mut VMContext), ) -> Result<(), Trap> { unsafe { - let _reset = if store.externref_activations_table().stack_canary().is_some() { - None + let exit = if store.externref_activations_table().stack_canary().is_some() { + false } else { - Some(enter_wasm_init(store)?) + enter_wasm(store)?; + true }; - wasmtime_runtime::catch_traps(store, closure).map_err(|e| Trap::from_runtime(store, e)) + let result = wasmtime_runtime::catch_traps( + store.vminterrupts(), + store.signal_handler(), + store.default_callee(), + closure, + ); + if exit { + exit_wasm(store); + } + result.map_err(Trap::from_runtime) } } @@ -1178,10 +1037,9 @@ pub(crate) fn invoke_wasm_and_catch_traps( /// guaranteed all wasm frames have been walked. /// /// This function may fail if the the stack limit can't be set because an -/// interrupt already happened. Otherwise it returns a value that resets the -/// various limits on `Drop`. +/// interrupt already happened. #[inline] -fn enter_wasm_init<'a>(store: &'a Store) -> Result { +fn enter_wasm(store: &mut StoreOpaque<'_>) -> Result<(), Trap> { let stack_pointer = psm::stack_pointer() as usize; // Determine the stack pointer where, after which, any wasm code will @@ -1219,7 +1077,6 @@ fn enter_wasm_init<'a>(store: &'a Store) -> Result { // considered interrupted. interrupts.stack_limit.store(usize::max_value(), Relaxed); return Err(Trap::new_wasm( - Some(store), None, wasmtime_environ::ir::TrapCode::Interrupt, backtrace::Backtrace::new_unresolved(), @@ -1231,22 +1088,18 @@ fn enter_wasm_init<'a>(store: &'a Store) -> Result { .externref_activations_table() .set_stack_canary(Some(stack_pointer)); - return Ok(Reset(store)); - - struct Reset<'a>(&'a Store); + Ok(()) +} - impl Drop for Reset<'_> { - #[inline] - fn drop(&mut self) { - self.0.externref_activations_table().set_stack_canary(None); +#[inline] +fn exit_wasm(store: &mut StoreOpaque<'_>) { + store.externref_activations_table().set_stack_canary(None); - // see docs above for why this uses `Relaxed` - self.0 - .interrupts() - .stack_limit - .store(usize::max_value(), Relaxed); - } - } + // see docs above for why this uses `Relaxed` + store + .interrupts() + .stack_limit + .store(usize::max_value(), Relaxed); } /// A trait implemented for types which can be returned from closures passed to @@ -1266,7 +1119,7 @@ pub unsafe trait WasmRet { // Same as `WasmTy::compatible_with_store`. #[doc(hidden)] - fn compatible_with_store(&self, store: &Store) -> bool; + fn compatible_with_store(&self, store: &StoreOpaque) -> bool; // Similar to `WasmTy::into_abi_for_arg` but used when host code is // returning a value into Wasm, rather than host code passing an argument to @@ -1275,7 +1128,11 @@ pub unsafe trait WasmRet { // `invoke_wasm_and_catch_traps` is on the stack, and therefore this method // is unsafe. #[doc(hidden)] - unsafe fn into_abi_for_ret(self, store: &Store, ptr: Self::Retptr) -> Result; + unsafe fn into_abi_for_ret( + self, + store: &mut StoreOpaque, + ptr: Self::Retptr, + ) -> Result; #[doc(hidden)] fn func_type(params: impl Iterator) -> FuncType; @@ -1302,11 +1159,15 @@ where type Retptr = (); type Fallible = Result; - fn compatible_with_store(&self, store: &Store) -> bool { + fn compatible_with_store(&self, store: &StoreOpaque) -> bool { ::compatible_with_store(self, store) } - unsafe fn into_abi_for_ret(self, store: &Store, _retptr: ()) -> Result { + unsafe fn into_abi_for_ret( + self, + store: &mut StoreOpaque, + _retptr: (), + ) -> Result { Ok(::into_abi(self, store)) } @@ -1335,7 +1196,7 @@ where type Retptr = ::Retptr; type Fallible = Self; - fn compatible_with_store(&self, store: &Store) -> bool { + fn compatible_with_store(&self, store: &StoreOpaque) -> bool { match self { Ok(x) => ::compatible_with_store(x, store), Err(_) => true, @@ -1344,7 +1205,7 @@ where unsafe fn into_abi_for_ret( self, - store: &Store, + store: &mut StoreOpaque, retptr: Self::Retptr, ) -> Result { self.and_then(|val| val.into_abi_for_ret(store, retptr)) @@ -1380,13 +1241,13 @@ macro_rules! impl_wasm_host_results { type Fallible = Result; #[inline] - fn compatible_with_store(&self, _store: &Store) -> bool { + fn compatible_with_store(&self, _store: &StoreOpaque) -> bool { let ($($t,)*) = self; $( $t.compatible_with_store(_store) && )* true } #[inline] - unsafe fn into_abi_for_ret(self, _store: &Store, ptr: Self::Retptr) -> Result { + unsafe fn into_abi_for_ret(self, _store: &mut StoreOpaque, ptr: Self::Retptr) -> Result { let ($($t,)*) = self; let abi = ($($t.into_abi(_store),)*); Ok(<($($t::Abi,)*) as HostAbi>::into_abi(abi, ptr)) @@ -1458,8 +1319,10 @@ macro_rules! impl_host_abi { type Abi = (); type Retptr = (); + #[inline] unsafe fn into_abi(self, _ptr: Self::Retptr) -> Self::Abi {} + #[inline] unsafe fn call(f: impl FnOnce(Self::Retptr) -> Self::Abi) -> Self { f(()) } @@ -1535,9 +1398,9 @@ for_each_function_signature!(impl_host_abi); /// /// This trait should not be implemented by external users, it's only intended /// as an implementation detail of this crate. -pub trait IntoFunc { +pub trait IntoFunc: Send + Sync + 'static { #[doc(hidden)] - fn into_func(self, store: Option<&Store>) -> (FuncType, InstanceHandle, VMTrampoline); + fn into_func(self, engine: &Engine) -> (InstanceHandle, VMTrampoline); } /// A structure representing the *caller's* context when creating a function @@ -1559,12 +1422,29 @@ pub trait IntoFunc { /// be removed in the future at some point after interface types is implemented. If /// you're relying on this Caller type it's recommended to become familiar with /// interface types to ensure that your use case is covered by the proposal. -pub struct Caller<'a> { - store: &'a Store, - caller_vmctx: *mut VMContext, +pub struct Caller<'a, T> { + pub(crate) store: StoreContextMut<'a, T>, + caller: &'a InstanceHandle, } -impl Caller<'_> { +impl Caller<'_, T> { + unsafe fn with(caller: *mut VMContext, f: impl FnOnce(Caller<'_, T>) -> R) -> R { + assert!(!caller.is_null()); + let instance = InstanceHandle::from_vmctx(caller); + let store = StoreContextMut::from_raw(instance.store()); + f(Caller { + store, + caller: &instance, + }) + } + + fn sub_caller(&mut self) -> Caller<'_, T> { + Caller { + store: self.store.as_context_mut(), + caller: self.caller, + } + } + /// Looks up an export from the caller's module by the `name` given. /// /// Note that this function is only implemented for the `Extern::Memory` @@ -1588,21 +1468,16 @@ impl Caller<'_> { /// /// It's recommended to take care when calling this API and gracefully /// handling a `None` return value. - pub fn get_export(&self, name: &str) -> Option { + pub fn get_export(&mut self, name: &str) -> Option { unsafe { - if self.caller_vmctx.is_null() { - return None; - } - let instance = InstanceHandle::from_vmctx(self.caller_vmctx); - let handle = self.store.existing_instance_handle(instance); - let index = handle.module().exports.get(name)?; + let index = self.caller.module().exports.get(name)?; match index { // Only allow memory/functions for now to emulate what interface // types will once provide EntityIndex::Memory(_) | EntityIndex::Function(_) => { Some(Extern::from_wasmtime_export( - &handle.lookup_by_declaration(&index), - &handle.store, + self.caller.lookup_by_declaration(&index), + &mut self.store.as_context_mut().opaque(), )) } _ => None, @@ -1610,20 +1485,76 @@ impl Caller<'_> { } } - /// Get a reference to the caller's store. - #[inline] - pub fn store(&self) -> &Store { + /// Access the underlying data owned by this `Store`. + /// + /// Same as [`Store::data`]. + pub fn data(&self) -> &T { + self.store.data() + } + + /// Access the underlying data owned by this `Store`. + /// + /// Same as [`Store::data_mut`]. + pub fn data_mut(&mut self) -> &mut T { + self.store.data_mut() + } + + /// Perform garbage collection of `ExternRef`s. + /// + /// Same as [`Store::gc`]. + pub fn gc(&mut self) { + self.store.gc() + } + + /// Returns the fuel consumed by this store. + /// + /// For more information see [`Store::fuel_consumed`]. + pub fn fuel_consumed(&self) -> Option { + self.store.fuel_consumed() + } + + /// Inject more fuel into this store to be consumed when executing wasm code. + /// + /// For more information see [`Store::add_fuel`] + pub fn add_fuel(&mut self, fuel: u64) -> Result<()> { + self.store.add_fuel(fuel) + } + + /// Configures this `Store` to trap whenever fuel runs out. + /// + /// For more information see [`Store::out_of_fuel_trap`] + pub fn out_of_fuel_trap(&mut self) { + self.store.out_of_fuel_trap() + } + + /// Configures this `Store` to yield while executing futures whenever fuel + /// runs out. + /// + /// For more information see [`Store::out_of_fuel_async_yield`] + pub fn out_of_fuel_async_yield(&mut self, injection_count: u32, fuel_to_inject: u64) { self.store + .out_of_fuel_async_yield(injection_count, fuel_to_inject) } } -#[inline(never)] -#[cold] -unsafe fn raise_cross_store_trap() -> ! { +impl AsContext for Caller<'_, T> { + type Data = T; + fn as_context(&self) -> StoreContext<'_, T> { + self.store.as_context() + } +} + +impl AsContextMut for Caller<'_, T> { + fn as_context_mut(&mut self) -> StoreContextMut<'_, T> { + self.store.as_context_mut() + } +} + +fn cross_store_trap() -> Box { #[derive(Debug)] struct CrossStoreError; - impl std::error::Error for CrossStoreError {} + impl Error for CrossStoreError {} impl fmt::Display for CrossStoreError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -1635,7 +1566,7 @@ unsafe fn raise_cross_store_trap() -> ! { } } - raise_user_trap(Box::new(CrossStoreError)); + Box::new(CrossStoreError) } macro_rules! impl_into_func { @@ -1644,29 +1575,29 @@ macro_rules! impl_into_func { // delegating to the implementation below which does have the leading // `Caller` parameter. #[allow(non_snake_case)] - impl IntoFunc<($($args,)*), R> for F + impl IntoFunc for F where - F: Fn($($args),*) -> R + 'static, + F: Fn($($args),*) -> R + Send + Sync + 'static, $($args: WasmTy,)* R: WasmRet, { - fn into_func(self, store: Option<&Store>) -> (FuncType, InstanceHandle, VMTrampoline) { - let f = move |_: Caller<'_>, $($args:$args),*| { + fn into_func(self, engine: &Engine) -> (InstanceHandle, VMTrampoline) { + let f = move |_: Caller<'_, T>, $($args:$args),*| { self($($args),*) }; - f.into_func(store) + f.into_func(engine) } } #[allow(non_snake_case)] - impl IntoFunc<(Caller<'_>, $($args,)*), R> for F + impl IntoFunc, $($args,)*), R> for F where - F: Fn(Caller<'_>, $($args),*) -> R + 'static, + F: Fn(Caller<'_, T>, $($args),*) -> R + Send + Sync + 'static, $($args: WasmTy,)* R: WasmRet, { - fn into_func(self, store: Option<&Store>) -> (FuncType, InstanceHandle, VMTrampoline) { + fn into_func(self, engine: &Engine) -> (InstanceHandle, VMTrampoline) { /// This shim is called by Wasm code, constructs a `Caller`, /// calls the wrapped host function, and returns the translated /// result back to Wasm. @@ -1674,20 +1605,20 @@ macro_rules! impl_into_func { /// Note that this shim's ABI must *exactly* match that expected /// by Cranelift, since Cranelift is generating raw function /// calls directly to this function. - unsafe extern "C" fn wasm_to_host_shim( + unsafe extern "C" fn wasm_to_host_shim( vmctx: *mut VMContext, caller_vmctx: *mut VMContext, $( $args: $args::Abi, )* retptr: R::Retptr, ) -> R::Abi where - F: Fn(Caller<'_>, $( $args ),*) -> R + 'static, + F: Fn(Caller<'_, T>, $( $args ),*) -> R + 'static, $( $args: WasmTy, )* R: WasmRet, { - enum CallResult { - Ok(T), - Trap(Trap), + enum CallResult { + Ok(U), + Trap(Box), Panic(Box), } @@ -1697,7 +1628,7 @@ macro_rules! impl_into_func { // destructors. As a result anything requiring a destructor // should be part of this block, and the long-jmp-ing // happens after the block in handling `CallResult`. - let result = { + let result = Caller::with(caller_vmctx, |mut caller| { let state = (*vmctx).host_state(); // Double-check ourselves in debug mode, but we control // the `Any` here so an unsafe downcast should also @@ -1705,17 +1636,13 @@ macro_rules! impl_into_func { debug_assert!(state.is::()); let func = &*(state as *const _ as *const F); - let store = wasmtime_runtime::with_last_info(|last| { - last.and_then(|s| s.downcast_ref::()) - .cloned() - .expect("function called without thread state") - }); - let ret = { panic::catch_unwind(AssertUnwindSafe(|| { + let mut _store = caller.sub_caller().store.opaque(); + $(let $args = $args::from_abi($args, &mut _store);)* func( - Caller { store: &store, caller_vmctx }, - $( $args::from_abi($args, &store), )* + caller.sub_caller(), + $( $args, )* ) })) }; @@ -1733,24 +1660,23 @@ macro_rules! impl_into_func { // Because the wrapped function is not `unsafe`, we // can't assume it returned a value that is // compatible with this store. + let mut store = caller.store.opaque(); if !ret.compatible_with_store(&store) { - // Explicitly drop all locals with destructors prior to raising the trap - drop(store); - drop(ret); - raise_cross_store_trap(); + CallResult::Trap(cross_store_trap()) + } else { + match ret.into_abi_for_ret(&mut store, retptr) { + Ok(val) => CallResult::Ok(val), + Err(trap) => CallResult::Trap(trap.into()), + } } - match ret.into_abi_for_ret(&store, retptr) { - Ok(val) => CallResult::Ok(val), - Err(trap) => CallResult::Trap(trap), - } } } - }; + }); match result { CallResult::Ok(val) => val, - CallResult::Trap(trap) => raise_user_trap(trap.into()), + CallResult::Trap(trap) => raise_user_trap(trap), CallResult::Panic(panic) => wasmtime_runtime::resume_panic(panic), } } @@ -1797,27 +1723,24 @@ macro_rules! impl_into_func { $(.chain(Some($args::valtype())))* ); + let shared_signature_id = engine.signatures().register(ty.as_wasm_func_type()); + let trampoline = host_trampoline::<$($args,)* R>; - // If not given a store, use a default signature index that is guaranteed to trap. - // If the function is called indirectly without first being associated with a store (a bug condition). - let shared_signature_id = store - .map(|s| s.signatures().borrow_mut().register(ty.as_wasm_func_type(), trampoline)) - .unwrap_or(VMSharedSignatureIndex::default()); let instance = unsafe { crate::trampoline::create_raw_function( std::slice::from_raw_parts_mut( - wasm_to_host_shim:: as *mut _, + wasm_to_host_shim:: as *mut _, 0, ), + shared_signature_id, Box::new(self), - shared_signature_id ) .expect("failed to create raw function") }; - (ty, instance, trampoline) + (instance, trampoline) } } } @@ -1825,55 +1748,116 @@ macro_rules! impl_into_func { for_each_function_signature!(impl_into_func); -#[test] -fn wasm_ty_roundtrip() -> Result<(), anyhow::Error> { - use crate::*; - let store = Store::default(); - let debug = Func::wrap(&store, |a: i32, b: u32, c: f32, d: i64, e: u64, f: f64| { - assert_eq!(a, -1); - assert_eq!(b, 1); - assert_eq!(c, 2.0); - assert_eq!(d, -3); - assert_eq!(e, 3); - assert_eq!(f, 4.0); - }); - let module = Module::new( - store.engine(), - r#" - (module - (import "" "" (func $debug (param i32 i32 f32 i64 i64 f64))) - (func (export "foo") (param i32 i32 f32 i64 i64 f64) - (if (i32.ne (local.get 0) (i32.const -1)) - (then unreachable) - ) - (if (i32.ne (local.get 1) (i32.const 1)) - (then unreachable) - ) - (if (f32.ne (local.get 2) (f32.const 2)) - (then unreachable) - ) - (if (i64.ne (local.get 3) (i64.const -3)) - (then unreachable) - ) - (if (i64.ne (local.get 4) (i64.const 3)) - (then unreachable) - ) - (if (f64.ne (local.get 5) (f64.const 4)) - (then unreachable) - ) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - local.get 4 - local.get 5 - call $debug - ) - ) - "#, - )?; - let instance = Instance::new(&store, &module, &[debug.into()])?; - let foo = instance.get_typed_func::<(i32, u32, f32, i64, u64, f64), ()>("foo")?; - foo.call((-1, 1, 2.0, -3, 3, 4.0))?; - Ok(()) +/// Represents a host function. +/// +/// This differs from `Func` in that it is not associated with a `Store`. +/// Host functions are associated with a `Config`. +pub(crate) struct HostFunc { + instance: InstanceHandle, + trampoline: VMTrampoline, + export: ExportFunction, + engine: Engine, +} + +impl HostFunc { + /// Analog of [`Func::new`] + pub fn new( + engine: &Engine, + ty: FuncType, + func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static, + ) -> Self { + let ty_clone = ty.clone(); + + // Create a trampoline that converts raw u128 values to `Val` + let func = Box::new(move |caller_vmctx, values_vec: *mut u128| unsafe { + Caller::with(caller_vmctx, |caller| { + Func::invoke(caller, &ty_clone, values_vec, &func) + }) + }); + + let (instance, trampoline) = crate::trampoline::create_function(&ty, func, engine) + .expect("failed to create function"); + HostFunc::_new(engine, instance, trampoline) + } + + /// Analog of [`Func::wrap`] + pub fn wrap( + engine: &Engine, + func: impl IntoFunc, + ) -> Self { + let (instance, trampoline) = func.into_func(engine); + HostFunc::_new(engine, instance, trampoline) + } + + fn _new(engine: &Engine, instance: InstanceHandle, trampoline: VMTrampoline) -> Self { + let idx = EntityIndex::Function(FuncIndex::from_u32(0)); + let export = match instance.lookup_by_declaration(&idx) { + wasmtime_runtime::Export::Function(f) => f, + _ => unreachable!(), + }; + + HostFunc { + instance, + trampoline, + export, + engine: engine.clone(), + } + } + + /// Inserts this `HostFunc` into a `Store`, returning the `Func` pointing to + /// it. + /// + /// # Unsafety + /// + /// Can only be inserted into stores with a matching `T` relative to when + /// this `HostFunc` was first created. + pub unsafe fn to_func(self: &Arc, store: &mut StoreOpaque<'_>) -> Func { + self.register_trampoline(store); + let me = self.clone(); + Func(store.store_data_mut().insert(FuncData::SharedHost(me))) + } + + /// Same as [`HostFunc::to_func`], different ownership. + unsafe fn into_func(self, store: &mut StoreOpaque<'_>) -> Func { + self.register_trampoline(store); + Func(store.store_data_mut().insert(FuncData::Host(self))) + } + + unsafe fn register_trampoline(&self, store: &mut StoreOpaque<'_>) { + let idx = self.export.anyfunc.as_ref().type_index; + store.register_host_trampoline(idx, self.trampoline); + } +} + +impl Drop for HostFunc { + fn drop(&mut self) { + unsafe { + self.engine + .signatures() + .unregister(self.export.anyfunc.as_ref().type_index); + + // Host functions are always allocated with the default (on-demand) + // allocator + OnDemandInstanceAllocator::default().deallocate(&self.instance); + } + } +} + +impl FuncData { + fn trampoline(&self) -> VMTrampoline { + match self { + FuncData::StoreOwned { trampoline, .. } => *trampoline, + FuncData::SharedHost(host) => host.trampoline, + FuncData::Host(host) => host.trampoline, + } + } + + #[inline] + fn export(&self) -> &ExportFunction { + match self { + FuncData::StoreOwned { export, .. } => export, + FuncData::SharedHost(host) => &host.export, + FuncData::Host(host) => &host.export, + } + } } diff --git a/crates/wasmtime/src/func/typed.rs b/crates/wasmtime/src/func/typed.rs index 107810f84b1e..969d734f3e28 100644 --- a/crates/wasmtime/src/func/typed.rs +++ b/crates/wasmtime/src/func/typed.rs @@ -1,5 +1,6 @@ use super::{invoke_wasm_and_catch_traps, HostAbi}; -use crate::{ExternRef, Func, Store, Trap, ValType}; +use crate::store::StoreOpaque; +use crate::{AsContextMut, ExternRef, Func, Trap, ValType}; use anyhow::{bail, Result}; use std::marker; use std::mem::{self, MaybeUninit}; @@ -12,7 +13,7 @@ use wasmtime_runtime::{VMContext, VMFunctionBody}; /// The function within a [`TypedFunc`] is statically known to have `Params` as its /// parameters and `Results` as its results. /// -/// This structure is created via [`Func::typed`] or [`Func::typed_unchecked`]. +/// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`]. /// For more documentation about this see those methods. #[repr(transparent)] pub struct TypedFunc { @@ -34,6 +35,25 @@ where Params: WasmParams, Results: WasmResults, { + /// An unchecked version of [`Func::typed`] which does not perform a + /// typecheck and simply assumes that the type declared here matches the + /// type of this function. + /// + /// The semantics of this function are the same as [`Func::typed`] except + /// that no error is returned because no typechecking is done. + /// + /// # Unsafety + /// + /// This function only safe to call if `typed` would otherwise return `Ok` + /// for the same `Params` and `Results` specified. If `typed` would return + /// an error then the returned `TypedFunc` is memory unsafe to invoke. + pub unsafe fn new_unchecked(func: Func) -> TypedFunc { + TypedFunc { + _a: marker::PhantomData, + func, + } + } + /// Returns the underlying [`Func`] that this is wrapping, losing the static /// type information in the process. pub fn func(&self) -> &Func { @@ -51,12 +71,13 @@ where /// /// This function will panic if it is called when the underlying [`Func`] is /// connected to an asynchronous store. - pub fn call(&self, params: Params) -> Result { + pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result { + let mut store = store.as_context_mut().opaque(); assert!( - !cfg!(feature = "async") || !self.func.store().async_support(), + !cfg!(feature = "async") || !store.async_support(), "must use `call_async` with async stores" ); - unsafe { self._call(params) } + unsafe { self._call(&mut store, params) } } /// Invokes this WebAssembly function with the specified parameters. @@ -72,55 +93,65 @@ where /// connected to a synchronous store. #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - pub async fn call_async(&self, params: Params) -> Result { + pub async fn call_async( + &self, + mut store: impl AsContextMut, + params: Params, + ) -> Result + where + T: Send, + { + let mut store = store.as_context_mut().opaque_send(); assert!( - self.func.store().async_support(), + store.async_support(), "must use `call` with non-async stores" ); - self.func - .store() - .on_fiber(|| unsafe { self._call(params) }) + store + .on_fiber(|store| unsafe { self._call(store, params) }) .await? } - unsafe fn _call(&self, params: Params) -> Result { + unsafe fn _call(&self, store: &mut StoreOpaque<'_>, params: Params) -> Result { // Validate that all runtime values flowing into this store indeed // belong within this store, otherwise it would be unsafe for store // values to cross each other. - if !params.compatible_with_store(&self.func.instance.store) { - return Err(Trap::new( - "attempt to pass cross-`Store` value to Wasm as function argument", - )); - } + let params = match params.into_abi(store) { + Some(abi) => abi, + None => { + return Err(Trap::new( + "attempt to pass cross-`Store` value to Wasm as function argument", + )) + } + }; + + // Try to capture only a single variable (a tuple) in the closure below. + // This means the size of the closure is one pointer and is much more + // efficient to move in memory. This closure is actually invoked on the + // other side of a C++ shim, so it can never be inlined enough to make + // the memory go away, so the size matters here for performance. + let mut captures = ( + self.func.caller_checked_anyfunc(store), + MaybeUninit::uninit(), + params, + false, + ); - let params = MaybeUninit::new(params); - let mut ret = MaybeUninit::uninit(); - let mut called = false; - let mut returned = false; - let result = invoke_wasm_and_catch_traps(&self.func.instance.store, || { - called = true; - let params = ptr::read(params.as_ptr()); - let anyfunc = self.func.export.anyfunc.as_ref(); - let result = params.invoke::( - &self.func.instance.store, + let result = invoke_wasm_and_catch_traps(store, |callee| { + let (anyfunc, ret, params, returned) = &mut captures; + let anyfunc = anyfunc.as_ref(); + let result = Params::invoke::( anyfunc.func_ptr.as_ptr(), anyfunc.vmctx, - ptr::null_mut(), + callee, + *params, ); ptr::write(ret.as_mut_ptr(), result); - returned = true + *returned = true }); - - // This can happen if we early-trap due to interrupts or other - // pre-flight checks, so we need to be sure the parameters are at least - // dropped at some point. - if !called { - drop(params.assume_init()); - } + let (_, ret, _, returned) = captures; debug_assert_eq!(result.is_ok(), returned); result?; - - Ok(ret.assume_init()) + Ok(Results::from_abi(store, ret.assume_init())) } } @@ -132,7 +163,7 @@ where /// stable over time. /// /// For more information see [`Func::wrap`] and [`Func::typed`] -pub unsafe trait WasmTy { +pub unsafe trait WasmTy: Send { #[doc(hidden)] type Abi: Copy; #[doc(hidden)] @@ -147,11 +178,11 @@ pub unsafe trait WasmTy { #[doc(hidden)] fn valtype() -> ValType; #[doc(hidden)] - fn compatible_with_store(&self, store: &Store) -> bool; + fn compatible_with_store(&self, store: &StoreOpaque) -> bool; #[doc(hidden)] - fn into_abi(self, store: &Store) -> Self::Abi; + fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi; #[doc(hidden)] - unsafe fn from_abi(abi: Self::Abi, store: &Store) -> Self; + unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self; } macro_rules! primitives { @@ -163,15 +194,15 @@ macro_rules! primitives { ValType::$ty } #[inline] - fn compatible_with_store(&self, _: &Store) -> bool { + fn compatible_with_store(&self, _: &StoreOpaque) -> bool { true } #[inline] - fn into_abi(self, _store: &Store) -> Self::Abi { + fn into_abi(self, _store: &mut StoreOpaque) -> Self::Abi { self } #[inline] - unsafe fn from_abi(abi: Self::Abi, _store: &Store) -> Self { + unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self { abi } } @@ -196,18 +227,16 @@ unsafe impl WasmTy for Option { } #[inline] - fn compatible_with_store(&self, _store: &Store) -> bool { + fn compatible_with_store(&self, _store: &StoreOpaque) -> bool { true } #[inline] - fn into_abi(self, store: &Store) -> Self::Abi { + fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi { if let Some(x) = self { let abi = x.inner.as_raw(); unsafe { - store - .externref_activations_table() - .insert_with_gc(x.inner, store.module_info_lookup()); + store.insert_vmexternref(x.inner); } abi } else { @@ -216,7 +245,7 @@ unsafe impl WasmTy for Option { } #[inline] - unsafe fn from_abi(abi: Self::Abi, _store: &Store) -> Self { + unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self { if abi.is_null() { None } else { @@ -236,26 +265,26 @@ unsafe impl WasmTy for Option { } #[inline] - fn compatible_with_store<'a>(&self, store: &Store) -> bool { + fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool { if let Some(f) = self { - Store::same(&store, f.store()) + store.store_data().contains(f.0) } else { true } } #[inline] - fn into_abi(self, _store: &Store) -> Self::Abi { + fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi { if let Some(f) = self { - f.caller_checked_anyfunc().as_ptr() + f.caller_checked_anyfunc(store).as_ptr() } else { ptr::null_mut() } } #[inline] - unsafe fn from_abi(abi: Self::Abi, store: &Store) -> Self { - Func::from_caller_checked_anyfunc(&store, abi) + unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self { + Func::from_caller_checked_anyfunc(store, abi) } } @@ -264,19 +293,20 @@ unsafe impl WasmTy for Option { /// /// This is implemented for bare types that can be passed to wasm as well as /// tuples of those types. -pub unsafe trait WasmParams { +pub unsafe trait WasmParams: Send { + #[doc(hidden)] + type Abi: Copy; #[doc(hidden)] fn typecheck(params: impl ExactSizeIterator) -> Result<()>; #[doc(hidden)] - fn compatible_with_store(&self, store: &Store) -> bool; + fn into_abi(self, store: &mut StoreOpaque) -> Option; #[doc(hidden)] unsafe fn invoke( - self, - store: &Store, func: *const VMFunctionBody, vmctx1: *mut VMContext, vmctx2: *mut VMContext, - ) -> R; + abi: Self::Abi, + ) -> R::ResultAbi; } // Forward an impl from `T` to `(T,)` for convenience if there's only one @@ -285,20 +315,22 @@ unsafe impl WasmParams for T where T: WasmTy, { + type Abi = <(T,) as WasmParams>::Abi; + fn typecheck(params: impl ExactSizeIterator) -> Result<()> { - <(T,)>::typecheck(params) + <(T,) as WasmParams>::typecheck(params) } - fn compatible_with_store(&self, store: &Store) -> bool { - ::compatible_with_store(self, store) + #[inline] + fn into_abi(self, store: &mut StoreOpaque) -> Option { + <(T,) as WasmParams>::into_abi((self,), store) } unsafe fn invoke( - self, - store: &Store, func: *const VMFunctionBody, vmctx1: *mut VMContext, vmctx2: *mut VMContext, - ) -> R { - <(T,)>::invoke((self,), store, func, vmctx1, vmctx2) + abi: Self::Abi, + ) -> R::ResultAbi { + <(T,) as WasmParams>::invoke::(func, vmctx1, vmctx2, abi) } } @@ -306,6 +338,8 @@ macro_rules! impl_wasm_params { ($n:tt $($t:ident)*) => { #[allow(non_snake_case)] unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) { + type Abi = ($($t::Abi,)*); + fn typecheck(mut params: impl ExactSizeIterator) -> Result<()> { let mut _n = 0; $( @@ -322,28 +356,34 @@ macro_rules! impl_wasm_params { } } - fn compatible_with_store(&self, _store: &Store) -> bool { + fn into_abi(self, _store: &mut StoreOpaque) -> Option { let ($($t,)*) = self; - $($t.compatible_with_store(_store)&&)* true + $( + let $t = if $t.compatible_with_store(_store) { + $t.into_abi(_store) + } else { + return None; + }; + )* + Some(($($t,)*)) } unsafe fn invoke( - self, - store: &Store, func: *const VMFunctionBody, vmctx1: *mut VMContext, vmctx2: *mut VMContext, - ) -> R { + abi: Self::Abi, + ) -> R::ResultAbi { let fnptr = mem::transmute::< *const VMFunctionBody, unsafe extern "C" fn( *mut VMContext, *mut VMContext, $($t::Abi,)* - R::Retptr, - ) -> R::Abi, + ::Retptr, + ) -> ::Abi, >(func); - let ($($t,)*) = self; + let ($($t,)*) = abi; // Use the `call` function to acquire a `retptr` which we'll // forward to the native function. Once we have it we also // convert all our arguments to abi arguments to go to the raw @@ -351,8 +391,8 @@ macro_rules! impl_wasm_params { // // Upon returning `R::call` will convert all the returns back // into `R`. - R::call(store, |retptr| { - fnptr(vmctx1, vmctx2, $($t.into_abi(store),)* retptr) + ::call(|retptr| { + fnptr(vmctx1, vmctx2, $($t,)* retptr) }) } } @@ -369,11 +409,9 @@ for_each_function_signature!(impl_wasm_params); /// `TypedFunc` is not currently supported. pub unsafe trait WasmResults: WasmParams { #[doc(hidden)] - type Abi: Copy; - #[doc(hidden)] - type Retptr: Copy; + type ResultAbi: HostAbi; #[doc(hidden)] - unsafe fn call(store: &Store, f: impl FnOnce(Self::Retptr) -> Self::Abi) -> Self; + unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self; } // Forwards from a bare type `T` to the 1-tuple type `(T,)` @@ -381,11 +419,10 @@ unsafe impl WasmResults for T where (T::Abi,): HostAbi, { - type Abi = <(T,) as WasmResults>::Abi; - type Retptr = <(T,) as WasmResults>::Retptr; + type ResultAbi = <(T,) as WasmResults>::ResultAbi; - unsafe fn call(store: &Store, f: impl FnOnce(Self::Retptr) -> Self::Abi) -> Self { - <(T,) as WasmResults>::call(store, f).0 + unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self { + <(T,) as WasmResults>::from_abi(store, abi).0 } } @@ -395,15 +432,10 @@ macro_rules! impl_wasm_results { unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) where ($($t::Abi,)*): HostAbi { - type Abi = <($($t::Abi,)*) as HostAbi>::Abi; - type Retptr = <($($t::Abi,)*) as HostAbi>::Retptr; - - unsafe fn call(store: &Store, f: impl FnOnce(Self::Retptr) -> Self::Abi) -> Self { - // Delegate via the host abi to figure out what the actual ABI - // for dealing with this tuple type is, and then we can re-tuple - // everything and create actual values via `from_abi` after the - // call is complete. - let ($($t,)*) = <($($t::Abi,)*) as HostAbi>::call(f); + type ResultAbi = ($($t::Abi,)*); + + unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self { + let ($($t,)*) = abi; ($($t::from_abi($t, store),)*) } } diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 7a30f850aafd..b82cecacd1de 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -1,21 +1,20 @@ -use crate::trampoline::StoreInstanceHandle; +use crate::store::{InstanceId, StoreData, StoreOpaque, StoreOpaqueSend, Stored}; use crate::types::matching; use crate::{ - Engine, Export, Extern, Func, Global, InstanceType, Memory, Module, Store, Table, Trap, - TypedFunc, + AsContext, AsContextMut, Engine, Export, Extern, Func, Global, InstanceType, Memory, Module, + StoreContextMut, Table, Trap, TypedFunc, }; use anyhow::{anyhow, bail, Context, Error, Result}; use std::mem; -use std::rc::Rc; +use std::sync::Arc; use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::wasm::{ EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex, }; use wasmtime_environ::Initializer; use wasmtime_runtime::{ - Imports, InstanceAllocationRequest, InstantiationError, RuntimeInstance, VMContext, - VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, - VMTableImport, + Imports, InstanceAllocationRequest, InstantiationError, VMContext, VMFunctionBody, + VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport, }; /// An instantiated WebAssembly module. @@ -34,11 +33,8 @@ use wasmtime_runtime::{ /// /// When interacting with any wasm code you'll want to make an [`Instance`] to /// call any code or execute anything! -#[derive(Clone)] -pub struct Instance { - pub(crate) store: Store, - pub(crate) items: RuntimeInstance, -} +#[derive(Copy, Clone)] +pub struct Instance(Stored); impl Instance { /// Creates a new [`Instance`] from the previously compiled [`Module`] and @@ -99,7 +95,19 @@ impl Instance { /// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727 /// [`ExternType`]: crate::ExternType - pub fn new(store: &Store, module: &Module, imports: &[Extern]) -> Result { + pub fn new( + mut store: impl AsContextMut, + module: &Module, + imports: &[Extern], + ) -> Result { + Instance::_new(&mut store.as_context_mut().opaque(), module, imports) + } + + fn _new( + store: &mut StoreOpaque<'_>, + module: &Module, + imports: &[Extern], + ) -> Result { assert!( !store.async_support(), "cannot use `new` when async support is enabled on the config" @@ -109,10 +117,12 @@ impl Instance { // small but should be kept in sync (modulo the async bits). let mut i = Instantiator::new(store, module, imports)?; loop { - if let Some((instance, items)) = i.step()? { - Instantiator::start_raw(&instance)?; - if let Some(items) = items { - break Ok(Instance::from_wasmtime(&items, store)); + if let Some((id, instance)) = i.step(store)? { + if let Some(start) = store.instance(id).module().start_func { + Instantiator::start_raw(store, id, start)?; + } + if let Some(instance) = instance { + break Ok(instance); } } } @@ -133,8 +143,20 @@ impl Instance { /// an [`asynchronous config`](crate::Config::async_support). #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - pub async fn new_async( - store: &Store, + pub async fn new_async( + mut store: impl AsContextMut, + module: &Module, + imports: &[Extern], + ) -> Result + where + T: Send, + { + Instance::_new_async(store.as_context_mut().opaque_send(), module, imports).await + } + + #[cfg(feature = "async")] + async fn _new_async<'a>( + mut store: StoreOpaqueSend<'a>, module: &Module, imports: &[Extern], ) -> Result { @@ -145,57 +167,55 @@ impl Instance { // NB: this is the same code as `Instance::new`. It's intentionally // small but should be kept in sync (modulo the async bits). - let mut i = Instantiator::new(store, module, imports)?; + let mut i = Instantiator::new(&mut store.opaque(), module, imports)?; loop { - if let Some((instance, items)) = i.step()? { - if instance.handle.module().start_func.is_some() { + let step = i.step(&mut store.opaque())?; + if let Some((id, instance)) = step { + let start = store.instance(id).module().start_func; + if let Some(start) = start { store - .on_fiber(|| Instantiator::start_raw(&instance)) + .on_fiber(|store| Instantiator::start_raw(store, id, start)) .await??; } - if let Some(items) = items { - break Ok(Instance::from_wasmtime(&items, store)); + if let Some(instance) = instance { + break Ok(instance); } } } } - pub(crate) fn from_wasmtime(handle: &RuntimeInstance, store: &Store) -> Instance { - Instance { - items: handle.clone(), - store: store.clone(), - } + pub(crate) fn from_wasmtime(handle: RuntimeInstance, store: &mut StoreOpaque) -> Instance { + Instance(store.store_data_mut().insert(handle)) } /// Returns the type signature of this instance. - pub fn ty(&self) -> InstanceType { + pub fn ty(&self, store: impl AsContext) -> InstanceType { + let store = store.as_context(); + let items = &store[self.0]; let mut ty = InstanceType::new(); - for export in self.exports() { - ty.add_named_export(export.name(), export.ty()); + for (name, item) in items.iter() { + ty.add_named_export(name, item.ty(&store)); } ty } - pub(crate) fn wasmtime_export(&self) -> &RuntimeInstance { - &self.items + pub(crate) fn items<'a>(&self, store: &'a StoreData) -> &'a RuntimeInstance { + &store[self.0] } - /// Returns the associated [`Store`] that this `Instance` is compiled into. - /// - /// This is the [`Store`] that generally serves as a sort of global cache - /// for various instance-related things. - pub fn store(&self) -> &Store { - &self.store + pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { + store.store_data().contains(self.0) } /// Returns the list of exported items from this [`Instance`]. - pub fn exports<'instance>( - &'instance self, - ) -> impl ExactSizeIterator> + 'instance { - self.items.iter().map(move |(name, item)| { - let extern_ = unsafe { Extern::from_wasmtime_export(item, &self.store) }; - Export::new(name, extern_) - }) + pub fn exports<'a, T: 'a>( + &'a self, + store: impl Into>, + ) -> impl ExactSizeIterator> + 'a { + let items = &store.into().store_data()[self.0]; + items + .iter() + .map(|(name, item)| Export::new(name, item.clone())) } /// Looks up an exported [`Extern`] value by name. @@ -204,17 +224,17 @@ impl Instance { /// the value, if found. /// /// Returns `None` if there was no export named `name`. - pub fn get_export(&self, name: &str) -> Option { - let export = self.items.get(name)?; - Some(unsafe { Extern::from_wasmtime_export(export, &self.store) }) + pub fn get_export(&self, store: impl AsContextMut, name: &str) -> Option { + let store = store.as_context(); + store[self.0].get(name).cloned() } /// Looks up an exported [`Func`] value by name. /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a function. - pub fn get_func(&self, name: &str) -> Option { - self.get_export(name)?.into_func() + pub fn get_func(&self, store: impl AsContextMut, name: &str) -> Option { + self.get_export(store, name)?.into_func() } /// Looks up an exported [`Func`] value by name and with its type. @@ -224,47 +244,51 @@ impl Instance { /// /// Returns an error if `name` isn't a function export or if the export's /// type did not match `Params` or `Results` - pub fn get_typed_func(&self, name: &str) -> Result> + pub fn get_typed_func( + &self, + mut store: S, + name: &str, + ) -> Result> where Params: crate::WasmParams, Results: crate::WasmResults, + S: AsContextMut, { let f = self - .get_export(name) + .get_export(store.as_context_mut(), name) .and_then(|f| f.into_func()) .ok_or_else(|| anyhow!("failed to find function export `{}`", name))?; - Ok(f.typed::()?.clone()) + Ok(f.typed::(store)?) } /// Looks up an exported [`Table`] value by name. /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a table. - pub fn get_table(&self, name: &str) -> Option
{ - self.get_export(name)?.into_table() + pub fn get_table(&self, store: impl AsContextMut, name: &str) -> Option
{ + self.get_export(store, name)?.into_table() } /// Looks up an exported [`Memory`] value by name. /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a memory. - pub fn get_memory(&self, name: &str) -> Option { - self.get_export(name)?.into_memory() + pub fn get_memory(&self, store: impl AsContextMut, name: &str) -> Option { + self.get_export(store, name)?.into_memory() } /// Looks up an exported [`Global`] value by name. /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a global. - pub fn get_global(&self, name: &str) -> Option { - self.get_export(name)?.into_global() + pub fn get_global(&self, store: impl AsContextMut, name: &str) -> Option { + self.get_export(store, name)?.into_global() } } struct Instantiator<'a> { in_progress: Vec>, cur: ImportsBuilder<'a>, - store: &'a Store, } struct ImportsBuilder<'a> { @@ -273,7 +297,7 @@ struct ImportsBuilder<'a> { tables: PrimaryMap, memories: PrimaryMap, globals: PrimaryMap, - instances: PrimaryMap, + instances: PrimaryMap, modules: PrimaryMap, initializer: usize, module: Module, @@ -289,7 +313,11 @@ impl<'a> Instantiator<'a> { /// directives of a module. /// /// This doesn't do much work itself beyond setting things up. - fn new(store: &'a Store, module: &Module, imports: &'a [Extern]) -> Result> { + fn new( + store: &mut StoreOpaque<'_>, + module: &Module, + imports: &'a [Extern], + ) -> Result> { if !Engine::same(store.engine(), module.engine()) { bail!("cross-`Engine` instantiation is not currently supported"); } @@ -301,7 +329,7 @@ impl<'a> Instantiator<'a> { bail!("expected {} imports, found {}", expected, imports.len()); } for import in imports { - if !import.comes_from_same_store(store) { + if !import.comes_from_same_store(&store) { bail!("cross-`Store` instantiation is not currently supported"); } } @@ -309,7 +337,6 @@ impl<'a> Instantiator<'a> { Ok(Instantiator { in_progress: Vec::new(), cur: ImportsBuilder::new(module, ImportSource::Runtime(imports)), - store, }) } @@ -333,9 +360,12 @@ impl<'a> Instantiator<'a> { /// created. If this is `None` callers need to keep calling this function /// since the instance created was simply for a recursive instance /// defined here. - fn step(&mut self) -> Result)>> { + fn step( + &mut self, + store: &mut StoreOpaque<'_>, + ) -> Result)>> { if self.cur.initializer == 0 { - self.store.bump_resource_counts(&self.cur.module)?; + store.bump_resource_counts(&self.cur.module)?; } // Read the current module's initializer and move forward the @@ -364,7 +394,8 @@ impl<'a> Instantiator<'a> { matching::MatchCx { signatures: self.cur.module.signatures(), types: self.cur.module.types(), - store: self.store, + store_data: store.store_data(), + engine: store.engine(), } .extern_(&expected_ty, head) .with_context(|| { @@ -374,7 +405,7 @@ impl<'a> Instantiator<'a> { }; format!("incompatible import type for `{}{}`", name, extra) })?; - self.cur.push(head); + self.cur.push(head.clone(), store); } // Otherwise if arguments are coming from our outer @@ -419,9 +450,9 @@ impl<'a> Instantiator<'a> { // and then push that item into our own index space. We eschew // type-checking since only valid modules should reach this point. Some(Initializer::AliasInstanceExport { instance, export }) => { - let export = &self.cur.instances[*instance][export]; - let item = unsafe { Extern::from_wasmtime_export(export, self.store) }; - self.cur.push(&item); + let instance = self.cur.instances[*instance]; + let export = store[instance.0][export].clone(); + self.cur.push(export, store); } // A recursive instantiation of an instance. @@ -486,8 +517,8 @@ impl<'a> Instantiator<'a> { // Note that in all cases we return the raw instance handle to get // the start function executed by the outer context. None => { - let instance = self.instantiate_raw()?; - let items = self.runtime_instance(&instance); + let instance = self.instantiate_raw(store)?; + let items = self.runtime_instance(store, instance); let items = match self.in_progress.pop() { Some(imports) => { self.cur = imports; @@ -503,85 +534,99 @@ impl<'a> Instantiator<'a> { Ok(None) } - fn instantiate_raw(&self) -> Result { + fn instantiate_raw(&mut self, store: &mut StoreOpaque<'_>) -> Result { let compiled_module = self.cur.module.compiled_module(); // Register the module just before instantiation to ensure we keep the module // properly referenced while in use by the store. - self.store.modules().borrow_mut().register(&self.cur.module); + store.modules_mut().register(&self.cur.module); unsafe { - let engine = self.store.engine(); - let allocator = engine.allocator(); - - let instance = allocator.allocate(InstanceAllocationRequest { - module: compiled_module.module().clone(), - finished_functions: compiled_module.finished_functions(), - imports: self.cur.build(), - shared_signatures: self.cur.module.signatures().as_module_map().into(), - host_state: Box::new(()), - interrupts: self.store.interrupts(), - externref_activations_table: self.store.externref_activations_table() - as *const VMExternRefActivationsTable - as *mut _, - module_info_lookup: Some(self.store.module_info_lookup()), - limiter: self.store.limiter().as_ref(), - })?; + let mut instance = store + .engine() + .allocator() + .allocate(InstanceAllocationRequest { + module: compiled_module.module().clone(), + finished_functions: compiled_module.finished_functions(), + imports: self.cur.build(), + shared_signatures: self.cur.module.signatures().as_module_map().into(), + host_state: Box::new(()), + store: Some(store.traitobj), + })?; // After we've created the `InstanceHandle` we still need to run - // initialization to set up data/elements/etc. We do this after adding - // the `InstanceHandle` to the store though. This is required for safety - // because the start function (for example) may trap, but element - // initializers may have run which placed elements into other instance's - // tables. This means that from this point on, regardless of whether - // initialization is successful, we need to keep the instance alive. - let instance = self.store.add_instance(instance, false); - allocator - .initialize(&instance.handle, engine.config().features.bulk_memory) + // initialization to set up data/elements/etc. We do this after + // adding the `InstanceHandle` to the store though. This is required + // for safety because the start function (for example) may trap, but + // element initializers may have run which placed elements into + // other instance's tables. This means that from this point on, + // regardless of whether initialization is successful, we need to + // keep the instance alive. + // + // Note that we `clone` the instance handle just to make easier + // working the the borrow checker here easier. Technically the `&mut + // instance` has somewhat of a borrow on `store` (which + // conflicts with the borrow on `store.engine`) but this doesn't + // matter in practice since initialization isn't even running any + // code here anyway. + let id = store.add_instance(instance.clone(), false); + store + .engine() + .allocator() + .initialize( + &mut instance, + compiled_module.module(), + store.engine().config().features.bulk_memory, + ) .map_err(|e| -> Error { match e { - InstantiationError::Trap(trap) => { - Trap::from_runtime(self.store, trap).into() - } + InstantiationError::Trap(trap) => Trap::from_runtime(trap).into(), other => other.into(), } })?; - Ok(instance) + Ok(id) } } - fn start_raw(instance: &StoreInstanceHandle) -> Result<()> { - let start_func = instance.handle.module().start_func; - + fn start_raw( + store: &mut StoreOpaque<'_>, + instance: InstanceId, + start: FuncIndex, + ) -> Result<()> { // If a start function is present, invoke it. Make sure we use all the // trap-handling configuration in `store` as well. - if let Some(start) = start_func { - let f = match instance - .handle - .lookup_by_declaration(&EntityIndex::Function(start)) - { - wasmtime_runtime::Export::Function(f) => f, - _ => unreachable!(), // valid modules shouldn't hit this - }; - let vmctx_ptr = instance.handle.vmctx_ptr(); - unsafe { - super::func::invoke_wasm_and_catch_traps(&instance.store, || { - mem::transmute::< - *const VMFunctionBody, - unsafe extern "C" fn(*mut VMContext, *mut VMContext), - >(f.anyfunc.as_ref().func_ptr.as_ptr())( - f.anyfunc.as_ref().vmctx, vmctx_ptr - ) - })?; - } + let instance = store.instance(instance); + let f = match instance.lookup_by_declaration(&EntityIndex::Function(start)) { + wasmtime_runtime::Export::Function(f) => f, + _ => unreachable!(), // valid modules shouldn't hit this + }; + let vmctx = instance.vmctx_ptr(); + unsafe { + super::func::invoke_wasm_and_catch_traps(store, |_default_callee| { + mem::transmute::< + *const VMFunctionBody, + unsafe extern "C" fn(*mut VMContext, *mut VMContext), + >(f.anyfunc.as_ref().func_ptr.as_ptr())( + f.anyfunc.as_ref().vmctx, vmctx + ) + })?; } Ok(()) } - fn runtime_instance(&self, instance: &StoreInstanceHandle) -> RuntimeInstance { + fn runtime_instance(&mut self, store: &mut StoreOpaque<'_>, instance: InstanceId) -> Instance { + // We use an unsafe `clone()` here to work around the borrow checker. + // Technically our instance is a borrow of `store`, but we need the + // borrow again later when calling `Extern::from_wasmtime_export` (and a + // mutable one at that). + // + // The mutability in `from_wasmtime_export` only mutates `StoreData` + // since we're adding ids, but it definitely doesn't deallocate + // `instance` (nothing does that except `Drop` for `Store`), so this in + // theory should be safe. + let instance = unsafe { store.instance(instance).clone() }; let exports = instance - .handle .module() .exports .iter() @@ -591,18 +636,16 @@ impl<'a> Instantiator<'a> { // means we need to handle that here, otherwise we defer to the // instance to load the values. let item = match index { - EntityIndex::Instance(i) => { - wasmtime_runtime::Export::Instance(self.cur.instances[*i].clone()) - } - EntityIndex::Module(i) => { - wasmtime_runtime::Export::Module(Box::new(self.cur.modules[*i].clone())) - } - index => instance.handle.lookup_by_declaration(index), + EntityIndex::Instance(i) => Extern::Instance(self.cur.instances[*i].clone()), + EntityIndex::Module(i) => Extern::Module(self.cur.modules[*i].clone()), + index => unsafe { + Extern::from_wasmtime_export(instance.lookup_by_declaration(index), store) + }, }; (name.clone(), item) }) .collect(); - Rc::new(exports) + Instance::from_wasmtime(Arc::new(exports), store) } } @@ -622,25 +665,25 @@ impl<'a> ImportsBuilder<'a> { } } - fn push(&mut self, item: &Extern) { + fn push(&mut self, item: Extern, store: &mut StoreOpaque<'_>) { match item { Extern::Func(i) => { - self.functions.push(i.vmimport()); + self.functions.push(i.vmimport(store)); } Extern::Global(i) => { - self.globals.push(i.vmimport()); + self.globals.push(i.vmimport(store)); } Extern::Table(i) => { - self.tables.push(i.vmimport()); + self.tables.push(i.vmimport(store)); } Extern::Memory(i) => { - self.memories.push(i.vmimport()); + self.memories.push(i.vmimport(store)); } Extern::Instance(i) => { - self.instances.push(i.items.clone()); + self.instances.push(i); } Extern::Module(m) => { - self.modules.push(m.clone()); + self.modules.push(m); } } } @@ -655,6 +698,8 @@ impl<'a> ImportsBuilder<'a> { } } +pub(crate) type RuntimeInstance = Arc>; + /// An internal structure to this crate to build an `Instance` from a list of /// items with names. This is intended to stay private for now, it'll need an /// audit of APIs if publicly exported. @@ -669,22 +714,11 @@ impl InstanceBuilder { } pub(crate) fn insert(&mut self, name: &str, item: impl Into) { - let items = Rc::get_mut(&mut self.items).unwrap(); - let export = match item.into() { - Extern::Func(i) => wasmtime_runtime::Export::Function(i.wasmtime_export().clone()), - Extern::Memory(i) => wasmtime_runtime::Export::Memory(i.wasmtime_export().clone()), - Extern::Table(i) => wasmtime_runtime::Export::Table(i.wasmtime_export().clone()), - Extern::Global(i) => wasmtime_runtime::Export::Global(i.wasmtime_export().clone()), - Extern::Instance(i) => wasmtime_runtime::Export::Instance(i.items.clone()), - Extern::Module(i) => wasmtime_runtime::Export::Module(Box::new(i.clone())), - }; - items.insert(name.to_string(), export); + let items = Arc::get_mut(&mut self.items).unwrap(); + items.insert(name.to_string(), item.into()); } - pub(crate) fn finish(self, store: &Store) -> Instance { - Instance { - store: store.clone(), - items: self.items, - } + pub(crate) fn finish(self, store: &mut StoreOpaque) -> Instance { + Instance::from_wasmtime(self.items, store) } } diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 332f63b4d22e..3cc9bf5c16bf 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -270,8 +270,8 @@ //! ``` #![allow(unknown_lints)] -#![deny(missing_docs, broken_intra_doc_links)] -#![doc(test(attr(deny(warnings))))] +//#![deny(missing_docs, broken_intra_doc_links)] +//#![doc(test(attr(deny(warnings))))] #![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))] #![cfg_attr(nightlydoc, feature(doc_cfg))] #![cfg_attr(not(feature = "default"), allow(dead_code, unused_imports))] @@ -305,7 +305,9 @@ pub use crate::linker::*; pub use crate::memory::*; pub use crate::module::{FrameInfo, FrameSymbol, Module}; pub use crate::r#ref::ExternRef; -pub use crate::store::*; +pub use crate::store::{ + AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut, +}; pub use crate::trap::*; pub use crate::types::*; pub use crate::values::*; @@ -324,7 +326,30 @@ cfg_if::cfg_if! { fn _assert_send_sync() { fn _assert() {} + fn _assert_send(_t: T) {} _assert::(); _assert::(); _assert::(); + _assert::<(Func, TypedFunc<(), ()>, Global, Table, Memory)>(); + _assert::(); + _assert::(); + _assert::>(); + _assert::>(); + _assert::>(); + _assert::>(); + _assert::>(); + _assert::(); + + #[cfg(feature = "async")] + fn _call_async(s: &mut Store<()>, f: Func) { + _assert_send(f.call_async(&mut *s, &[])) + } + #[cfg(feature = "async")] + fn _typed_call_async(s: &mut Store<()>, f: TypedFunc<(), ()>) { + _assert_send(f.call_async(&mut *s, ())) + } + #[cfg(feature = "async")] + fn _instantiate_async(s: &mut Store<()>, m: &Module) { + _assert_send(Instance::new_async(s, m, &[])) + } } diff --git a/crates/wasmtime/src/limits.rs b/crates/wasmtime/src/limits.rs index fc65aa2e90f6..9f2d5553552e 100644 --- a/crates/wasmtime/src/limits.rs +++ b/crates/wasmtime/src/limits.rs @@ -7,7 +7,7 @@ pub(crate) const DEFAULT_MEMORY_LIMIT: usize = 10000; /// [`Store::new_with_limits`](crate::Store::new_with_limits) can be used /// with a resource limiter to take into account non-WebAssembly resource /// usage to determine if a linear memory or table should be grown. -pub trait ResourceLimiter { +pub trait ResourceLimiter: Send + Sync + 'static { /// Notifies the resource limiter that an instance's linear memory has been requested to grow. /// /// * `current` is the current size of the linear memory in WebAssembly page units. @@ -23,7 +23,7 @@ pub trait ResourceLimiter { /// /// Returning `true` when a maximum has been exceeded will have no effect as the linear memory /// will not be grown. - fn memory_growing(&self, current: u32, desired: u32, maximum: Option) -> bool; + fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option) -> bool; /// Notifies the resource limiter that an instance's table has been requested to grow. /// @@ -39,7 +39,7 @@ pub trait ResourceLimiter { /// /// Returning `true` when a maximum has been exceeded will have no effect as the table will /// not be grown. - fn table_growing(&self, current: u32, desired: u32, maximum: Option) -> bool; + fn table_growing(&mut self, current: u32, desired: u32, maximum: Option) -> bool; /// The maximum number of instances that can be created for a [`Store`](crate::Store). /// @@ -72,11 +72,11 @@ pub trait ResourceLimiter { pub(crate) struct ResourceLimiterProxy(pub T); impl wasmtime_runtime::ResourceLimiter for ResourceLimiterProxy { - fn memory_growing(&self, current: u32, desired: u32, maximum: Option) -> bool { + fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option) -> bool { self.0.memory_growing(current, desired, maximum) } - fn table_growing(&self, current: u32, desired: u32, maximum: Option) -> bool { + fn table_growing(&mut self, current: u32, desired: u32, maximum: Option) -> bool { self.0.table_growing(current, desired, maximum) } @@ -180,14 +180,14 @@ impl Default for StoreLimits { } impl ResourceLimiter for StoreLimits { - fn memory_growing(&self, _current: u32, desired: u32, _maximum: Option) -> bool { + fn memory_growing(&mut self, _current: u32, desired: u32, _maximum: Option) -> bool { match self.memory_pages { Some(limit) if desired > limit => false, _ => true, } } - fn table_growing(&self, _current: u32, desired: u32, _maximum: Option) -> bool { + fn table_growing(&mut self, _current: u32, desired: u32, _maximum: Option) -> bool { match self.table_elements { Some(limit) if desired > limit => false, _ => true, diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index cd2be6a32690..6b3071968ff4 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -1,11 +1,18 @@ +use crate::func::HostFunc; use crate::instance::InstanceBuilder; use crate::{ - Extern, ExternType, Func, FuncType, ImportType, Instance, IntoFunc, Module, Store, Trap, + AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance, + IntoFunc, Module, Trap, Val, }; use anyhow::{anyhow, bail, Context, Error, Result}; use log::warn; use std::collections::hash_map::{Entry, HashMap}; -use std::rc::Rc; +#[cfg(feature = "async")] +use std::future::Future; +use std::marker; +#[cfg(feature = "async")] +use std::pin::Pin; +use std::sync::Arc; /// Structure used to link wasm modules/instances together. /// @@ -31,21 +38,66 @@ use std::rc::Rc; /// Names in a `Linker` cannot be defined twice, but allowing duplicates by /// shadowing the previous definition can be controlled with the /// [`Linker::allow_shadowing`] method. -pub struct Linker { - store: Store, - string2idx: HashMap, usize>, - strings: Vec>, - map: HashMap, +pub struct Linker { + engine: Engine, + string2idx: HashMap, usize>, + strings: Vec>, + map: HashMap>, allow_shadowing: bool, + _marker: marker::PhantomData T>, } -#[derive(Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Hash, PartialEq, Eq)] struct ImportKey { name: usize, module: usize, } -impl Linker { +enum Definition { + Extern(Extern), + HostFunc { + func: Arc, + t: marker::PhantomData T>, + }, +} + +macro_rules! generate_wrap_async_func { + ($num:tt $($args:ident)*) => (paste::paste!{ + /// TODO + #[allow(non_snake_case)] + #[cfg(feature = "async")] + #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] + pub fn []<$($args,)* R>( + &mut self, + module: &str, + name: &str, + func: impl for<'a> Fn(Caller<'a, T>, $($args),*) -> Box + Send + 'a> + Send + Sync + 'static, + ) -> Result<&mut Self> + where + $($args: crate::WasmTy,)* + R: crate::WasmRet, + { + assert!( + self.engine.config().async_support, + concat!( + "cannot use `func_wrap", + $num, + "_async` without enabling async support on the config", + ), + ); + self.func_wrap(module, name, move |mut caller: Caller<'_, T>, $($args: $args),*| { + let async_cx = caller.store.as_context_mut().opaque().async_cx(); + let mut future = Pin::from(func(caller, $($args),*)); + match unsafe { async_cx.block_on(future.as_mut()) } { + Ok(ret) => ret.into_fallible(), + Err(e) => R::fallible_from_trap(e), + } + }) + } + }) +} + +impl Linker { /// Creates a new [`Linker`]. /// /// This function will create a new [`Linker`] which is ready to start @@ -62,16 +114,22 @@ impl Linker { /// let mut linker = Linker::new(&store); /// // ... /// ``` - pub fn new(store: &Store) -> Linker { + pub fn new(engine: &Engine) -> Linker { Linker { - store: store.clone(), + engine: engine.clone(), map: HashMap::new(), string2idx: HashMap::new(), strings: Vec::new(), allow_shadowing: false, + _marker: marker::PhantomData, } } + /// TODO + pub fn engine(&self) -> &Engine { + &self.engine + } + /// Configures whether this [`Linker`] will shadow previous duplicate /// definitions of the same signature. /// @@ -97,7 +155,7 @@ impl Linker { /// # Ok(()) /// # } /// ``` - pub fn allow_shadowing(&mut self, allow: bool) -> &mut Linker { + pub fn allow_shadowing(&mut self, allow: bool) -> &mut Self { self.allow_shadowing = allow; self } @@ -146,7 +204,9 @@ impl Linker { name: &str, item: impl Into, ) -> Result<&mut Self> { - self._define(module, Some(name), item.into()) + let key = self.import_key(module, Some(name)); + self.insert(key, Definition::Extern(item.into()))?; + Ok(self) } /// Same as [`Linker::define`], except only the name of the import is @@ -156,17 +216,63 @@ impl Linker { /// where one-level names are allowed (in addition to two-level names). /// Otherwise this method need not be used. pub fn define_name(&mut self, name: &str, item: impl Into) -> Result<&mut Self> { - self._define(name, None, item.into()) + let key = self.import_key(name, None); + self.insert(key, Definition::Extern(item.into()))?; + Ok(self) } - fn _define(&mut self, module: &str, name: Option<&str>, item: Extern) -> Result<&mut Self> { - if !item.comes_from_same_store(&self.store) { - bail!("all linker items must be from the same store"); - } - self.insert(module, name, item)?; + pub fn func_new( + &mut self, + module: &str, + name: &str, + ty: FuncType, + func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static, + ) -> Result<&mut Self> { + let func = HostFunc::new(&self.engine, ty, func); + let key = self.import_key(module, Some(name)); + self.insert( + key, + Definition::HostFunc { + func: Arc::new(func), + t: marker::PhantomData, + }, + )?; Ok(self) } + #[cfg(feature = "async")] + #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] + pub fn func_new_async( + &mut self, + module: &str, + name: &str, + ty: FuncType, + func: F, + ) -> Result<&mut Self> + where + F: for<'a> Fn( + Caller<'a, T>, + &'a [Val], + &'a mut [Val], + ) -> Box> + Send + 'a> + + Send + + Sync + + 'static, + { + assert!( + self.engine.config().async_support, + "cannot use `func_new_async` without enabling async support in the config" + ); + self.func_new(module, name, ty, move |mut caller, params, results| { + let async_cx = caller.store.as_context_mut().opaque().async_cx(); + let mut future = Pin::from(func(caller, params, results)); + match unsafe { async_cx.block_on(future.as_mut()) } { + Ok(Ok(())) => Ok(()), + Ok(Err(trap)) | Err(trap) => Err(trap), + } + }) + } + /// Convenience wrapper to define a function import. /// /// This method is a convenience wrapper around [`Linker::define`] which @@ -203,15 +309,26 @@ impl Linker { /// # Ok(()) /// # } /// ``` - pub fn func( + pub fn func_wrap( &mut self, module: &str, name: &str, - func: impl IntoFunc, + func: impl IntoFunc, ) -> Result<&mut Self> { - self._define(module, Some(name), Func::wrap(&self.store, func).into()) + let func = HostFunc::wrap(&self.engine, func); + let key = self.import_key(module, Some(name)); + self.insert( + key, + Definition::HostFunc { + func: Arc::new(func), + t: marker::PhantomData, + }, + )?; + Ok(self) } + for_each_function_signature!(generate_wrap_async_func); + /// Convenience wrapper to define an entire [`Instance`] in this linker. /// /// This function is a convenience wrapper around [`Linker::define`] which @@ -257,12 +374,15 @@ impl Linker { /// # Ok(()) /// # } /// ``` - pub fn instance(&mut self, module_name: &str, instance: &Instance) -> Result<&mut Self> { - if !Store::same(&self.store, instance.store()) { - bail!("all linker items must be from the same store"); - } - for export in instance.exports() { - self.insert(module_name, Some(export.name()), export.into_extern())?; + pub fn instance( + &mut self, + mut store: impl AsContextMut, + module_name: &str, + instance: Instance, + ) -> Result<&mut Self> { + for export in instance.exports(store.as_context_mut()) { + let key = self.import_key(module_name, Some(export.name())); + self.insert(key, Definition::Extern(export.into_extern()))?; } Ok(self) } @@ -373,63 +493,57 @@ impl Linker { /// # Ok(()) /// # } /// ``` - pub fn module(&mut self, module_name: &str, module: &Module) -> Result<&mut Self> { + pub fn module( + &mut self, + mut store: impl AsContextMut, + module_name: &str, + module: &Module, + ) -> Result<&mut Self> { match ModuleKind::categorize(module)? { - ModuleKind::Command => self.command(module_name, module), + ModuleKind::Command => self.command(store, module_name, module), ModuleKind::Reactor => { - let instance = self.instantiate(&module)?; + let instance = self.instantiate(&mut store, &module)?; - if let Some(export) = instance.get_export("_initialize") { + if let Some(export) = instance.get_export(&mut store, "_initialize") { if let Extern::Func(func) = export { - func.typed::<(), ()>() - .and_then(|f| f.call(()).map_err(Into::into)) + func.typed::<(), (), _>(&store) + .and_then(|f| f.call(&mut store, ()).map_err(Into::into)) .context("calling the Reactor initialization function")?; } } - self.instance(module_name, &instance) + self.instance(store, module_name, instance) } } } - fn command(&mut self, module_name: &str, module: &Module) -> Result<&mut Self> { + fn command( + &mut self, + mut store: impl AsContextMut, + module_name: &str, + module: &Module, + ) -> Result<&mut Self> { for export in module.exports() { if let Some(func_ty) = export.ty().func() { - let imports = self - .compute_imports(module)? - .into_iter() - .map(|e| e.wasmtime_export()) - .collect::>(); + let imports = self.compute_imports(&mut store, module)?; let module = module.clone(); let export_name = export.name().to_owned(); let func = Func::new( - &self.store, + &mut store, func_ty.clone(), - move |caller, params, results| { - let store = caller.store(); - - // Note that the unsafety here is due to the validity of - // `i` and the validity of `i` within `store`. For our - // case though these items all come from `imports` above - // so they're all valid. They're also all kept alive by - // the store itself used here so this should be safe. - let imports = imports - .iter() - .map(|i| unsafe { Extern::from_wasmtime_export(&i, &store) }) - .collect::>(); - + move |mut caller, params, results| { // Create a new instance for this command execution. - let instance = Instance::new(&store, &module, &imports)?; + let instance = Instance::new(&mut caller, &module, &imports)?; // `unwrap()` everything here because we know the instance contains a // function export with the given name and signature because we're // iterating over the module it was instantiated from. let command_results = instance - .get_export(&export_name) + .get_export(&mut caller, &export_name) .unwrap() .into_func() .unwrap() - .call(params) + .call(&mut caller, params) .map_err(|error| error.downcast::().unwrap())?; // Copy the return values into the output slice. @@ -442,7 +556,8 @@ impl Linker { Ok(()) }, ); - self.insert(module_name, Some(export.name()), func.into())?; + let key = self.import_key(module_name, Some(export.name())); + self.insert(key, Definition::Extern(func.into()))?; } else if export.name() == "memory" && export.ty().memory().is_some() { // Allow an exported "memory" memory for now. } else if export.name() == "__indirect_function_table" && export.ty().table().is_some() @@ -487,41 +602,40 @@ impl Linker { /// Returns an error if any shadowing violations happen while defining new /// items. pub fn alias(&mut self, module: &str, as_module: &str) -> Result<()> { + let module = self.intern_str(module); + let as_module = self.intern_str(as_module); let items = self + .map .iter() - .filter(|(m, _, _)| *m == module) - .map(|(_, name, item)| (name.to_string(), item)) + .filter(|(key, _def)| key.module == module) + .map(|(key, def)| (key.name, def.clone())) .collect::>(); for (name, item) in items { - self.define(as_module, &name, item)?; + self.insert( + ImportKey { + module: as_module, + name, + }, + item, + )?; } Ok(()) } - fn insert(&mut self, module: &str, name: Option<&str>, item: Extern) -> Result<()> { - let key = self.import_key(module, name); - let desc = || match name { - Some(name) => format!("{}::{}", module, name), - None => module.to_string(), - }; + fn insert(&mut self, key: ImportKey, item: Definition) -> Result<()> { match self.map.entry(key) { Entry::Occupied(_) if !self.allow_shadowing => { - bail!("import of `{}` defined twice", desc(),) + let module = &self.strings[key.module]; + let desc = match self.strings.get(key.name) { + Some(name) => format!("{}::{}", module, name), + None => module.to_string(), + }; + bail!("import of `{}` defined twice", desc) } Entry::Occupied(mut o) => { o.insert(item); } Entry::Vacant(v) => { - // If shadowing is not allowed, check for an existing host function - if !self.allow_shadowing { - if let Extern::Func(_) = &item { - if let Some(name) = name { - if self.store.get_host_func(module, name).is_some() { - bail!("import of `{}` defined twice", desc(),) - } - } - } - } v.insert(item); } } @@ -541,7 +655,7 @@ impl Linker { if let Some(idx) = self.string2idx.get(string) { return *idx; } - let string: Rc = string.into(); + let string: Arc = string.into(); let idx = self.strings.len(); self.strings.push(string.clone()); self.string2idx.insert(string, idx); @@ -589,26 +703,42 @@ impl Linker { /// # Ok(()) /// # } /// ``` - pub fn instantiate(&self, module: &Module) -> Result { - let imports = self.compute_imports(module)?; - - Instance::new(&self.store, module, &imports) + pub fn instantiate( + &self, + mut store: impl AsContextMut, + module: &Module, + ) -> Result { + let imports = self.compute_imports(&mut store, module)?; + Instance::new(store, module, &imports) } /// Attempts to instantiate the `module` provided. This is the same as [`Linker::instantiate`], /// except for async `Store`s. #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - pub async fn instantiate_async(&self, module: &Module) -> Result { - let imports = self.compute_imports(module)?; - - Instance::new_async(&self.store, module, &imports).await + pub async fn instantiate_async( + &self, + mut store: impl AsContextMut, + module: &Module, + ) -> Result + where + T: Send, + { + let imports = self.compute_imports(&mut store, module)?; + Instance::new_async(store, module, &imports).await } - fn compute_imports(&self, module: &Module) -> Result> { + fn compute_imports( + &self, + mut store: impl AsContextMut, + module: &Module, + ) -> Result> { module .imports() - .map(|import| self.get(&import).ok_or_else(|| self.link_error(&import))) + .map(|import| { + self.get(&mut store, &import) + .ok_or_else(|| self.link_error(&import)) + }) .collect() } @@ -620,11 +750,6 @@ impl Linker { anyhow!("unknown import: `{}` has not been defined", desc) } - /// Returns the [`Store`] that this linker is connected to. - pub fn store(&self) -> &Store { - &self.store - } - /// Returns an iterator over all items defined in this `Linker`, in arbitrary order. /// /// The iterator returned will yield 3-tuples where the first two elements @@ -633,12 +758,15 @@ impl Linker { /// /// Note that multiple `Extern` items may be defined for the same /// module/name pair. - pub fn iter(&self) -> impl Iterator { + pub fn iter<'a: 'p, 'p>( + &'a self, + mut store: impl AsContextMut + 'p, + ) -> impl Iterator + 'p { self.map.iter().map(move |(key, item)| { ( &*self.strings[key.module], &*self.strings[key.name], - item.clone(), + item.to_extern(&mut store), ) }) } @@ -647,17 +775,16 @@ impl Linker { /// provided. /// /// Returns `None` if no match was found. - pub fn get(&self, import: &ImportType) -> Option { - if let Some(ext) = self.get_extern(import) { + pub fn get( + &self, + mut store: impl AsContextMut, + import: &ImportType, + ) -> Option { + if let Some(ext) = self.get_extern(&mut store, import) { return Some(ext); } match import.ty() { - // For function imports, check with the store for a host func - ExternType::Func(_) => self - .store - .get_host_func(import.module(), import.name()?) - .map(Into::into), ExternType::Instance(t) => { // This is a key location where the module linking proposal is // implemented. This logic allows single-level imports of an instance to @@ -677,10 +804,11 @@ impl Linker { if import.name().is_none() { let mut builder = InstanceBuilder::new(); for export in t.exports() { - let item = self.get(&export.as_import(import.module()))?; + let item = + self.get_extern(&mut store, &export.as_import(import.module()))?; builder.insert(export.name(), item); } - Some(builder.finish(&self.store).into()) + Some(builder.finish(&mut store.as_context_mut().opaque()).into()) } else { None } @@ -695,16 +823,17 @@ impl Linker { /// if the module/name have been defined twice. pub fn get_by_name<'a: 'p, 'p>( &'a self, + mut store: impl AsContextMut + 'p, module: &'p str, name: Option<&'p str>, - ) -> impl Iterator + 'p { + ) -> impl Iterator + 'p { self.map .iter() .filter(move |(key, _item)| { &*self.strings[key.module] == module && self.strings.get(key.name).map(|s| &**s) == name }) - .map(|(_, item)| item) + .map(move |(_, item)| item.to_extern(&mut store)) } /// Returns the single item defined for the `module` and `name` pair. @@ -713,12 +842,17 @@ impl Linker { /// a single `Extern` item. If the `module` and `name` pair isn't defined /// in this linker then an error is returned. If more than one value exists /// for the `module` and `name` pairs, then an error is returned as well. - pub fn get_one_by_name(&self, module: &str, name: Option<&str>) -> Result { + pub fn get_one_by_name( + &self, + store: impl AsContextMut, + module: &str, + name: Option<&str>, + ) -> Result { let err_msg = || match name { Some(name) => format!("named `{}` in `{}`", name, module), None => format!("named `{}`", module), }; - let mut items = self.get_by_name(module, name); + let mut items = self.get_by_name(store, module, name); let ret = items .next() .ok_or_else(|| anyhow!("no item {}", err_msg()))?; @@ -732,8 +866,12 @@ impl Linker { /// /// An export with an empty string is considered to be a "default export". /// "_start" is also recognized for compatibility. - pub fn get_default(&self, module: &str) -> Result { - let mut items = self.get_by_name(module, Some("")); + pub fn get_default( + &self, + mut store: impl AsContextMut, + module: &str, + ) -> Result { + let mut items = self.get_by_name(&mut store, module, Some("")); if let Some(external) = items.next() { if items.next().is_some() { bail!("too many items named `` in `{}`", module); @@ -743,9 +881,10 @@ impl Linker { } bail!("default export in '{}' is not a function", module); } + drop(items); // For compatibility, also recognize "_start". - let mut items = self.get_by_name(module, Some("_start")); + let mut items = self.get_by_name(&mut store, module, Some("_start")); if let Some(external) = items.next() { if items.next().is_some() { bail!("too many items named `_start` in `{}`", module); @@ -755,16 +894,17 @@ impl Linker { } bail!("`_start` in '{}' is not a function", module); } + drop(items); // Otherwise return a no-op function. - Ok(Func::new( - &self.store, - FuncType::new(None, None), - move |_, _, _| Ok(()), - )) + Ok(Func::wrap(store, || {})) } - fn get_extern(&self, import: &ImportType) -> Option { + fn get_extern( + &self, + store: impl AsContextMut, + import: &ImportType, + ) -> Option { let key = ImportKey { module: *self.string2idx.get(import.module())?, name: match import.name() { @@ -772,7 +912,42 @@ impl Linker { None => usize::max_value(), }, }; - self.map.get(&key).cloned() + Some(self.map.get(&key)?.to_extern(store)) + } +} + +impl Default for Linker { + fn default() -> Linker { + Linker::new(&Engine::default()) + } +} + +impl Definition { + fn to_extern(&self, mut store: impl AsContextMut) -> Extern { + match self { + Definition::Extern(e) => e.clone(), + + // Note the unsafety here is due to calling `to_func`. The + // requirement here is that the `T` that was originally used to + // create the `HostFunc` matches the `T` on the store. This is done + // with the `T` on `Definition` (and `Linker`) as well as the + // `Data=T` bound above. + Definition::HostFunc { func, .. } => unsafe { + func.to_func(&mut store.as_context_mut().opaque()).into() + }, + } + } +} + +impl Clone for Definition { + fn clone(&self) -> Definition { + match self { + Definition::Extern(e) => Definition::Extern(e.clone()), + Definition::HostFunc { func, t } => Definition::HostFunc { + func: func.clone(), + t: *t, + }, + } } } diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/memory.rs index 57aee031ffad..a6b25c030578 100644 --- a/crates/wasmtime/src/memory.rs +++ b/crates/wasmtime/src/memory.rs @@ -1,6 +1,7 @@ -use crate::trampoline::{generate_memory_export, StoreInstanceHandle}; -use crate::{MemoryType, Store}; -use anyhow::{anyhow, Result}; +use crate::store::{StoreData, StoreOpaque, Stored}; +use crate::trampoline::generate_memory_export; +use crate::{AsContext, AsContextMut, MemoryType, StoreContext, StoreContextMut}; +use anyhow::{bail, Result}; use std::slice; /// Error for out of bounds [`Memory`] access. @@ -42,6 +43,8 @@ impl std::error::Error for MemoryAccessError {} /// /// # `Memory` and Safety /// +/// TODO +/// /// Linear memory is a lynchpin of safety for WebAssembly, but it turns out /// there are very few ways to safely inspect the contents of a memory from the /// host (Rust). This is because memory safety is quite tricky when working with @@ -212,6 +215,8 @@ impl std::error::Error for MemoryAccessError {} /// /// ## `Memory` Safety and Threads /// +/// TODO +/// /// Currently the `wasmtime` crate does not implement the wasm threads proposal, /// but it is planned to do so. It's additionally worthwhile discussing how this /// affects memory safety and what was previously just discussed as well. @@ -241,10 +246,7 @@ impl std::error::Error for MemoryAccessError {} /// [interface types]: https://github.com/webassembly/interface-types /// [open an issue]: https://github.com/bytecodealliance/wasmtime/issues/new #[derive(Clone)] -pub struct Memory { - pub(crate) instance: StoreInstanceHandle, - wasmtime_export: wasmtime_runtime::ExportMemory, -} +pub struct Memory(Stored); impl Memory { /// Creates a new WebAssembly memory given the configuration of `ty`. @@ -270,12 +272,15 @@ impl Memory { /// # Ok(()) /// # } /// ``` - pub fn new(store: &Store, ty: MemoryType) -> Result { - let (instance, wasmtime_export) = generate_memory_export(store, &ty)?; - Ok(Memory { - instance, - wasmtime_export, - }) + pub fn new(mut store: impl AsContextMut, ty: MemoryType) -> Result { + Memory::_new(&mut store.as_context_mut().opaque(), ty) + } + + fn _new(store: &mut StoreOpaque<'_>, ty: MemoryType) -> Result { + unsafe { + let export = generate_memory_export(store, &ty)?; + Ok(Memory::from_wasmtime_memory(export, store)) + } } /// Returns the underlying type of this memory. @@ -295,8 +300,10 @@ impl Memory { /// # Ok(()) /// # } /// ``` - pub fn ty(&self) -> MemoryType { - MemoryType::from_wasmtime_memory(&self.wasmtime_export.memory.memory) + pub fn ty(&self, store: impl AsContext) -> MemoryType { + let store = store.as_context(); + let ty = &store[self.0].memory.memory; + MemoryType::from_wasmtime_memory(&ty) } /// Safely reads memory contents at the given offset into a buffer. @@ -305,16 +312,20 @@ impl Memory { /// /// If offset + buffer length exceed the current memory capacity, then the /// buffer is left untouched and a [`MemoryAccessError`] is returned. - pub fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), MemoryAccessError> { - unsafe { - let slice = self - .data_unchecked() - .get(offset..) - .and_then(|s| s.get(..buffer.len())) - .ok_or(MemoryAccessError { _private: () })?; - buffer.copy_from_slice(slice); - Ok(()) - } + pub fn read( + &self, + store: impl AsContext, + offset: usize, + buffer: &mut [u8], + ) -> Result<(), MemoryAccessError> { + let store = store.as_context(); + let slice = self + .data(&store) + .get(offset..) + .and_then(|s| s.get(..buffer.len())) + .ok_or(MemoryAccessError { _private: () })?; + buffer.copy_from_slice(slice); + Ok(()) } /// Safely writes contents of a buffer to this memory at the given offset. @@ -322,79 +333,53 @@ impl Memory { /// If the offset + buffer length exceed current memory capacity, then none /// of the buffer is written to memory and a [`MemoryAccessError`] is /// returned. - pub fn write(&self, offset: usize, buffer: &[u8]) -> Result<(), MemoryAccessError> { - unsafe { - self.data_unchecked_mut() - .get_mut(offset..) - .and_then(|s| s.get_mut(..buffer.len())) - .ok_or(MemoryAccessError { _private: () })? - .copy_from_slice(buffer); - Ok(()) - } + pub fn write( + &self, + mut store: impl AsContextMut, + offset: usize, + buffer: &[u8], + ) -> Result<(), MemoryAccessError> { + let mut context = store.as_context_mut(); + self.data_mut(&mut context) + .get_mut(offset..) + .and_then(|s| s.get_mut(..buffer.len())) + .ok_or(MemoryAccessError { _private: () })? + .copy_from_slice(buffer); + Ok(()) } /// Returns this memory as a slice view that can be read natively in Rust. - /// - /// # Safety - /// - /// This is an unsafe operation because there is no guarantee that the - /// following operations do not happen concurrently while the slice is in - /// use: - /// - /// * Data could be modified by calling into a wasm module. - /// * Memory could be relocated through growth by calling into a wasm - /// module. - /// * When threads are supported, non-atomic reads will race with other - /// writes. - /// - /// Extreme care need be taken when the data of a `Memory` is read. The - /// above invariants all need to be upheld at a bare minimum, and in - /// general you'll need to ensure that while you're looking at slice you're - /// the only one who can possibly look at the slice and read/write it. - /// - /// Be sure to keep in mind that `Memory` is reference counted, meaning - /// that there may be other users of this `Memory` instance elsewhere in - /// your program. Additionally `Memory` can be shared and used in any number - /// of wasm instances, so calling any wasm code should be considered - /// dangerous while you're holding a slice of memory. - /// - /// For more information and examples see the documentation on the - /// [`Memory`] type. - pub unsafe fn data_unchecked(&self) -> &[u8] { - self.data_unchecked_mut() + pub fn data<'a, T: 'a>(&self, store: impl Into>) -> &'a [u8] { + unsafe { + let store = store.into(); + let definition = *store[self.0].definition; + slice::from_raw_parts(definition.base, definition.current_length) + } } /// Returns this memory as a slice view that can be read and written /// natively in Rust. - /// - /// # Safety - /// - /// All of the same safety caveats of [`Memory::data_unchecked`] apply - /// here, doubly so because this is returning a mutable slice! As a - /// double-extra reminder, remember that `Memory` is reference counted, so - /// you can very easily acquire two mutable slices by simply calling this - /// function twice. Extreme caution should be used when using this method, - /// and in general you probably want to result to unsafe accessors and the - /// `data` methods below. - /// - /// For more information and examples see the documentation on the - /// [`Memory`] type. - pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { - let definition = &*self.wasmtime_export.definition; - slice::from_raw_parts_mut(definition.base, definition.current_length) + pub fn data_mut<'a, T: 'a>(&self, store: impl Into>) -> &'a mut [u8] { + unsafe { + let store = store.into(); + let definition = *store[self.0].definition; + slice::from_raw_parts_mut(definition.base, definition.current_length) + } } /// Returns the base pointer, in the host's address space, that the memory /// is located at. /// + /// TODO + /// /// When reading and manipulating memory be sure to read up on the caveats /// of [`Memory::data_unchecked`] to make sure that you can safely /// read/write the memory. /// /// For more information and examples see the documentation on the /// [`Memory`] type. - pub fn data_ptr(&self) -> *mut u8 { - unsafe { (*self.wasmtime_export.definition).base } + pub fn data_ptr(&self, store: impl AsContext) -> *mut u8 { + unsafe { (*store.as_context()[self.0].definition).base } } /// Returns the byte length of this memory. @@ -403,13 +388,13 @@ impl Memory { /// /// For more information and examples see the documentation on the /// [`Memory`] type. - pub fn data_size(&self) -> usize { - unsafe { (*self.wasmtime_export.definition).current_length } + pub fn data_size(&self, store: impl AsContext) -> usize { + unsafe { (*store.as_context()[self.0].definition).current_length } } /// Returns the size, in pages, of this wasm memory. - pub fn size(&self) -> u32 { - (self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32 + pub fn size(&self, store: impl AsContext) -> u32 { + (self.data_size(store) / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32 } /// Grows this WebAssembly memory by `delta` pages. @@ -447,38 +432,54 @@ impl Memory { /// # Ok(()) /// # } /// ``` - pub fn grow(&self, delta: u32) -> Result { - let index = self - .instance - .memory_index(unsafe { &*self.wasmtime_export.definition }); - self.instance - .memory_grow(index, delta) - .ok_or_else(|| anyhow!("failed to grow memory by `{}`", delta)) + pub fn grow(&self, mut store: impl AsContextMut, delta: u32) -> Result { + let mut store = store.as_context_mut().opaque(); + let mem = self.wasmtime_memory(&mut store); + unsafe { + match (*mem).grow(delta, store.limiter()) { + Some(size) => { + let vm = (*mem).vmmemory(); + *store[self.0].definition = vm; + Ok(size) + } + None => bail!("failed to grow memory by `{}`", delta), + } + } + } + + fn wasmtime_memory(&self, store: &mut StoreOpaque<'_>) -> *mut wasmtime_runtime::Memory { + unsafe { + let export = &store[self.0]; + let mut handle = wasmtime_runtime::InstanceHandle::from_vmctx(export.vmctx); + let idx = handle.memory_index(&*export.definition); + handle.get_defined_memory(idx) + } } pub(crate) unsafe fn from_wasmtime_memory( - wasmtime_export: &wasmtime_runtime::ExportMemory, - store: &Store, + wasmtime_export: wasmtime_runtime::ExportMemory, + store: &mut StoreOpaque, ) -> Memory { - Memory { - instance: store.existing_vmctx(wasmtime_export.vmctx), - wasmtime_export: wasmtime_export.clone(), - } + Memory(store.store_data_mut().insert(wasmtime_export)) } - pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Memory { - &self.wasmtime_export.memory.memory + pub(crate) fn wasmtime_ty<'a>( + &self, + store: &'a StoreData, + ) -> &'a wasmtime_environ::wasm::Memory { + &store[self.0].memory.memory } - pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMMemoryImport { + pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMMemoryImport { + let export = &store[self.0]; wasmtime_runtime::VMMemoryImport { - from: self.wasmtime_export.definition, - vmctx: self.wasmtime_export.vmctx, + from: export.definition, + vmctx: export.vmctx, } } - pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportMemory { - &self.wasmtime_export + pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { + store.store_data().contains(self.0) } } @@ -495,7 +496,7 @@ impl Memory { /// /// Note that this is a relatively new and experimental feature and it is recommended /// to be familiar with wasmtime runtime code to use it. -pub unsafe trait LinearMemory { +pub unsafe trait LinearMemory: Send + Sync + 'static { /// Returns the number of allocated wasm pages. fn size(&self) -> u32; @@ -507,7 +508,7 @@ pub unsafe trait LinearMemory { /// /// Returns `None` if memory can't be grown by the specified amount /// of wasm pages. - fn grow(&self, delta: u32) -> Option; + fn grow(&mut self, delta: u32) -> Option; /// Return the allocated memory as a mutable pointer to u8. fn as_ptr(&self) -> *mut u8; @@ -569,11 +570,12 @@ mod tests { let mut cfg = Config::new(); cfg.static_memory_maximum_size(0) .dynamic_memory_guard_size(0); - let store = Store::new(&Engine::new(&cfg).unwrap()); + let mut store = Store::new(&Engine::new(&cfg).unwrap(), ()); let ty = MemoryType::new(Limits::new(1, None)); - let mem = Memory::new(&store, ty).unwrap(); - assert_eq!(mem.wasmtime_export.memory.offset_guard_size, 0); - match mem.wasmtime_export.memory.style { + let mem = Memory::new(&mut store, ty).unwrap(); + let store = store.as_context(); + assert_eq!(store[mem.0].memory.offset_guard_size, 0); + match &store[mem.0].memory.style { wasmtime_environ::MemoryStyle::Dynamic => {} other => panic!("unexpected style {:?}", other), } diff --git a/crates/wasmtime/src/module/registry.rs b/crates/wasmtime/src/module/registry.rs index 668711910660..8d6e6886ac86 100644 --- a/crates/wasmtime/src/module/registry.rs +++ b/crates/wasmtime/src/module/registry.rs @@ -33,24 +33,6 @@ fn func_by_pc(module: &CompiledModule, pc: usize) -> Option<(DefinedFuncIndex, u pub struct ModuleRegistry(BTreeMap>); impl ModuleRegistry { - /// Fetches frame information about a program counter in a backtrace. - /// - /// Returns an object if this `pc` is known to some previously registered - /// module, or returns `None` if no information can be found. The boolean - /// returned indicates whether the original module has unparsed debug - /// information due to the compiler's configuration. - pub fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, bool)> { - let module = self.module(pc)?; - module - .lookup_frame_info(pc) - .map(|info| (info, module.has_unparsed_debuginfo())) - } - - /// Fetches trap information about a program counter in a backtrace. - pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { - self.module(pc)?.lookup_trap_info(pc) - } - /// Fetches information about a registered module given a program counter value. pub fn lookup_module(&self, pc: usize) -> Option> { self.module(pc) @@ -132,87 +114,6 @@ struct RegisteredModule { } impl RegisteredModule { - /// Determines if the related module has unparsed debug information. - pub fn has_unparsed_debuginfo(&self) -> bool { - self.module.has_unparsed_debuginfo() - } - - /// Fetches frame information about a program counter in a backtrace. - /// - /// Returns an object if this `pc` is known to this module, or returns `None` - /// if no information can be found. - pub fn lookup_frame_info(&self, pc: usize) -> Option { - let (index, offset) = func_by_pc(&self.module, pc)?; - let info = self.module.func_info(index); - let pos = Self::instr_pos(offset, &info.address_map); - - // In debug mode for now assert that we found a mapping for `pc` within - // the function, because otherwise something is buggy along the way and - // not accounting for all the instructions. This isn't super critical - // though so we can omit this check in release mode. - debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc); - - let instr = match pos { - Some(pos) => info.address_map.instructions[pos].srcloc, - None => info.address_map.start_srcloc, - }; - - // Use our wasm-relative pc to symbolize this frame. If there's a - // symbolication context (dwarf debug info) available then we can try to - // look this up there. - // - // Note that dwarf pcs are code-section-relative, hence the subtraction - // from the location of `instr`. Also note that all errors are ignored - // here for now since technically wasm modules can always have any - // custom section contents. - let mut symbols = Vec::new(); - - if let Some(s) = &self.module.symbolize_context().ok().and_then(|c| c) { - let to_lookup = (instr.bits() as u64) - s.code_section_offset(); - if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) { - while let Ok(Some(frame)) = frames.next() { - symbols.push(FrameSymbol { - name: frame - .function - .as_ref() - .and_then(|l| l.raw_name().ok()) - .map(|s| s.to_string()), - file: frame - .location - .as_ref() - .and_then(|l| l.file) - .map(|s| s.to_string()), - line: frame.location.as_ref().and_then(|l| l.line), - column: frame.location.as_ref().and_then(|l| l.column), - }); - } - } - } - - let module = self.module.module(); - let index = module.func_index(index); - - Some(FrameInfo { - module_name: module.name.clone(), - func_index: index.index() as u32, - func_name: module.func_names.get(&index).cloned(), - instr, - func_start: info.address_map.start_srcloc, - symbols, - }) - } - - /// Fetches trap information about a program counter in a backtrace. - pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { - let (index, offset) = func_by_pc(&self.module, pc)?; - let info = self.module.func_info(index); - let idx = info - .traps - .binary_search_by_key(&offset, |info| info.code_offset) - .ok()?; - Some(&info.traps[idx]) - } - fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option { // Use our relative position from the start of the function to find the // machine instruction that corresponds to `pc`, which then allows us to @@ -306,6 +207,7 @@ impl ModuleInfo for RegisteredModule { struct GlobalRegisteredModule { start: usize, module: Arc, + wasm_backtrace_details_env_used: bool, /// Note that modules can be instantiated in many stores, so the purpose of /// this field is to keep track of how many stores have registered a /// module. Information is only removed from the global registry when this @@ -335,30 +237,69 @@ impl GlobalModuleRegistry { pub(crate) fn is_wasm_pc(pc: usize) -> bool { let modules = GLOBAL_MODULES.lock().unwrap(); - match modules.0.range(pc..).next() { - Some((end, entry)) => { - if pc < entry.start || *end < pc { - return false; + match modules.module(pc) { + Some(entry) => match func_by_pc(&entry.module, pc) { + Some((index, offset)) => { + let info = entry.module.func_info(index); + RegisteredModule::instr_pos(offset, &info.address_map).is_some() } - - match func_by_pc(&entry.module, pc) { - Some((index, offset)) => { - let info = entry.module.func_info(index); - RegisteredModule::instr_pos(offset, &info.address_map).is_some() - } - None => false, - } - } + None => false, + }, None => false, } } + fn module(&self, pc: usize) -> Option<&GlobalRegisteredModule> { + let (end, info) = self.0.range(pc..).next()?; + if pc < info.start || *end < pc { + return None; + } + Some(info) + } + + // Work with the global instance of `GlobalModuleRegistry`. Note that only + // shared access is allowed, this isn't intended to mutate the contents. + // + // (right now we use a `Mutex` but if motivated we could one day use a + // `RwLock`) + pub(crate) fn with(f: impl FnOnce(&GlobalModuleRegistry) -> R) -> R { + f(&GLOBAL_MODULES.lock().unwrap()) + } + + /// Fetches frame information about a program counter in a backtrace. + /// + /// Returns an object if this `pc` is known to some previously registered + /// module, or returns `None` if no information can be found. The first + /// boolean returned indicates whether the original module has unparsed + /// debug information due to the compiler's configuration. The second + /// boolean indicates whether the engine used to compile this module is + /// using environment variables to control debuginfo parsing. + pub(crate) fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, bool, bool)> { + let module = self.module(pc)?; + module.lookup_frame_info(pc).map(|info| { + ( + info, + module.has_unparsed_debuginfo(), + module.wasm_backtrace_details_env_used, + ) + }) + } + + /// Fetches trap information about a program counter in a backtrace. + pub(crate) fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { + self.module(pc)?.lookup_trap_info(pc) + } + /// Registers a new region of code, described by `(start, end)` and with /// the given function information, with the global information. fn register(&mut self, start: usize, end: usize, module: &Module) { let info = self.0.entry(end).or_insert_with(|| GlobalRegisteredModule { start, module: module.compiled_module().clone(), + wasm_backtrace_details_env_used: module + .engine() + .config() + .wasm_backtrace_details_env_used, references: 0, }); @@ -380,6 +321,89 @@ impl GlobalModuleRegistry { } } +impl GlobalRegisteredModule { + /// Determines if the related module has unparsed debug information. + pub fn has_unparsed_debuginfo(&self) -> bool { + self.module.has_unparsed_debuginfo() + } + + /// Fetches frame information about a program counter in a backtrace. + /// + /// Returns an object if this `pc` is known to this module, or returns `None` + /// if no information can be found. + pub fn lookup_frame_info(&self, pc: usize) -> Option { + let (index, offset) = func_by_pc(&self.module, pc)?; + let info = self.module.func_info(index); + let pos = RegisteredModule::instr_pos(offset, &info.address_map); + + // In debug mode for now assert that we found a mapping for `pc` within + // the function, because otherwise something is buggy along the way and + // not accounting for all the instructions. This isn't super critical + // though so we can omit this check in release mode. + debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc); + + let instr = match pos { + Some(pos) => info.address_map.instructions[pos].srcloc, + None => info.address_map.start_srcloc, + }; + + // Use our wasm-relative pc to symbolize this frame. If there's a + // symbolication context (dwarf debug info) available then we can try to + // look this up there. + // + // Note that dwarf pcs are code-section-relative, hence the subtraction + // from the location of `instr`. Also note that all errors are ignored + // here for now since technically wasm modules can always have any + // custom section contents. + let mut symbols = Vec::new(); + + if let Some(s) = &self.module.symbolize_context().ok().and_then(|c| c) { + let to_lookup = (instr.bits() as u64) - s.code_section_offset(); + if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) { + while let Ok(Some(frame)) = frames.next() { + symbols.push(FrameSymbol { + name: frame + .function + .as_ref() + .and_then(|l| l.raw_name().ok()) + .map(|s| s.to_string()), + file: frame + .location + .as_ref() + .and_then(|l| l.file) + .map(|s| s.to_string()), + line: frame.location.as_ref().and_then(|l| l.line), + column: frame.location.as_ref().and_then(|l| l.column), + }); + } + } + } + + let module = self.module.module(); + let index = module.func_index(index); + + Some(FrameInfo { + module_name: module.name.clone(), + func_index: index.index() as u32, + func_name: module.func_names.get(&index).cloned(), + instr, + func_start: info.address_map.start_srcloc, + symbols, + }) + } + + /// Fetches trap information about a program counter in a backtrace. + pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { + let (index, offset) = func_by_pc(&self.module, pc)?; + let info = self.module.func_info(index); + let idx = info + .traps + .binary_search_by_key(&offset, |info| info.code_offset) + .ok()?; + Some(&info.traps[idx]) + } +} + /// Description of a frame in a backtrace for a [`Trap`]. /// /// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each @@ -522,7 +546,7 @@ impl FrameSymbol { #[test] fn test_frame_info() -> Result<(), anyhow::Error> { use crate::*; - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new( store.engine(), r#" @@ -538,18 +562,20 @@ fn test_frame_info() -> Result<(), anyhow::Error> { "#, )?; // Create an instance to ensure the frame information is registered. - Instance::new(&store, &module, &[])?; - let modules = store.modules().borrow(); - for (i, alloc) in module.compiled_module().finished_functions() { - let (start, end) = unsafe { - let ptr = (**alloc).as_ptr(); - let len = (**alloc).len(); - (ptr as usize, ptr as usize + len) - }; - for pc in start..end { - let (frame, _) = modules.lookup_frame_info(pc).unwrap(); - assert!(frame.func_index() == i.as_u32()); + Instance::new(&mut store, &module, &[])?; + + GlobalModuleRegistry::with(|modules| { + for (i, alloc) in module.compiled_module().finished_functions() { + let (start, end) = unsafe { + let ptr = (**alloc).as_ptr(); + let len = (**alloc).len(); + (ptr as usize, ptr as usize + len) + }; + for pc in start..end { + let (frame, _, _) = modules.lookup_frame_info(pc).unwrap(); + assert!(frame.func_index() == i.as_u32()); + } } - } + }); Ok(()) } diff --git a/crates/wasmtime/src/ref.rs b/crates/wasmtime/src/ref.rs index 6ea86655ab0e..7bd380b796e9 100644 --- a/crates/wasmtime/src/ref.rs +++ b/crates/wasmtime/src/ref.rs @@ -13,7 +13,7 @@ impl ExternRef { /// Creates a new instance of `ExternRef` wrapping the given value. pub fn new(value: T) -> ExternRef where - T: 'static + Any, + T: 'static + Any + Send + Sync, { let inner = VMExternRef::new(value); ExternRef { inner } diff --git a/crates/wasmtime/src/signatures.rs b/crates/wasmtime/src/signatures.rs index 2faf3fa05977..74cb2496a0fc 100644 --- a/crates/wasmtime/src/signatures.rs +++ b/crates/wasmtime/src/signatures.rs @@ -24,15 +24,6 @@ pub struct SignatureCollection { } impl SignatureCollection { - /// Creates a new, empty signature collection given a signature registry. - pub fn new(registry: &SignatureRegistry) -> Self { - Self { - registry: registry.0.clone(), - signatures: PrimaryMap::new(), - trampolines: HashMap::new(), - } - } - /// Creates a signature collection for a module given the module's signatures /// and trampolines. pub fn new_for_module( @@ -74,26 +65,26 @@ impl SignatureCollection { .map(|(_, trampoline)| *trampoline) } - /// Registers a single function with the collection. - /// - /// Returns the shared signature index for the function. - pub fn register( - &mut self, - ty: &WasmFuncType, - trampoline: VMTrampoline, - ) -> VMSharedSignatureIndex { - let index = self.registry.write().unwrap().register(ty); - - let entry = match self.trampolines.entry(index) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => e.insert((0, trampoline)), - }; - - // Increment the ref count - entry.0 += 1; - - index - } + ///// Registers a single function with the collection. + ///// + ///// Returns the shared signature index for the function. + //pub fn register( + // &mut self, + // ty: &WasmFuncType, + // trampoline: VMTrampoline, + //) -> VMSharedSignatureIndex { + // let index = self.registry.write().unwrap().register(ty); + + // let entry = match self.trampolines.entry(index) { + // Entry::Occupied(e) => e.into_mut(), + // Entry::Vacant(e) => e.insert((0, trampoline)), + // }; + + // // Increment the ref count + // entry.0 += 1; + + // index + //} } impl Drop for SignatureCollection { @@ -259,4 +250,18 @@ impl SignatureRegistry { .get(index.bits() as usize) .and_then(|e| e.as_ref().map(|e| &e.ty).cloned()) } + + /// Registers a single function with the collection. + /// + /// Returns the shared signature index for the function. + pub fn register(&self, ty: &WasmFuncType) -> VMSharedSignatureIndex { + self.0.write().unwrap().register(ty) + } + + /// Registers a single function with the collection. + /// + /// Returns the shared signature index for the function. + pub unsafe fn unregister(&self, sig: VMSharedSignatureIndex) { + self.0.write().unwrap().unregister_entry(sig, 1) + } } diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index c3737b3bffb4..74df1790a697 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -1,36 +1,26 @@ -use crate::{ - module::ModuleRegistry, signatures::SignatureCollection, trampoline::StoreInstanceHandle, - Engine, Func, Module, ResourceLimiter, ResourceLimiterProxy, Trap, DEFAULT_INSTANCE_LIMIT, - DEFAULT_MEMORY_LIMIT, DEFAULT_TABLE_LIMIT, -}; +use crate::{module::ModuleRegistry, Engine, Module, Trap}; use anyhow::{bail, Result}; -use std::any::{Any, TypeId}; -use std::cell::{Cell, RefCell}; -use std::collections::{hash_map::Entry, HashMap}; +use std::cell::UnsafeCell; +use std::collections::HashMap; use std::convert::TryFrom; +use std::error::Error; use std::fmt; use std::future::Future; -use std::hash::{Hash, Hasher}; +use std::marker; use std::pin::Pin; use std::ptr; -use std::rc::Rc; use std::sync::Arc; use std::task::{Context, Poll}; use wasmtime_runtime::{ - InstanceAllocator, InstanceHandle, ModuleInfo, OnDemandInstanceAllocator, SignalHandler, - TrapInfo, VMCallerCheckedAnyfunc, VMContext, VMExternRef, VMExternRefActivationsTable, - VMInterrupts, VMTrampoline, + InstanceAllocationRequest, InstanceAllocator, InstanceHandle, ModuleInfo, + OnDemandInstanceAllocator, SignalHandler, VMCallerCheckedAnyfunc, VMContext, VMExternRef, + VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex, VMTrampoline, }; -/// Used to associate instances with the store. -/// -/// This is needed to track if the instance was allocated explicitly with the on-demand -/// instance allocator. -struct StoreInstance { - handle: InstanceHandle, - // Stores whether or not to use the on-demand allocator to deallocate the instance - ondemand: bool, -} +mod context; +pub use self::context::*; +mod data; +pub use self::data::*; /// A `Store` is a collection of WebAssembly instances and host-defined items. /// @@ -63,34 +53,79 @@ struct StoreInstance { /// You can create a store with default configuration settings using /// `Store::default()`. This will create a brand new [`Engine`] with default /// ocnfiguration (see [`Config`](crate::Config) for more information). -#[derive(Clone)] -pub struct Store { - inner: Rc, +pub struct Store { + inner: Box>, } -pub(crate) struct StoreInner { +pub struct StoreInner { + // This `StoreInner` structure has references to itself. These aren't + // immediately evident, however, so we need to tell the compiler that it + // contains self-references. This notably suppresses `noalias` annotations + // when this shows up in compiled code because types of this structure do + // indeed alias itself. The best example of this is `StoreOpaque` which + // contains a `&mut StoreInner` and a `*mut dyn Store` which are actually + // the same pointer, indeed aliasing! + // + // It's somewhat unclear to me at this time if this is 100% sufficient to + // get all the right codegen in all the right places. For example does + // `Store` need to internally contain a `Pin>>`? Do the + // contexts need to contain `Pin<&mut StoreInner>`? I'm not familiar + // enough with `Pin` to understand if it's appropriate here (we do, for + // example want to allow movement in and out of `data: T`, just not movement + // of most of the other members). It's also not clear if using `Pin` in a + // few places buys us much other than a bunch of `unsafe` that we already + // sort of hand-wave away. + // + // In any case this seems like a good mid-ground for now where we're at + // least telling the compiler something about all the aliasing happening + // within a `Store`. + _marker: marker::PhantomPinned, + engine: Engine, interrupts: Arc, - instances: RefCell>, - signal_handler: RefCell>>>, + instances: Vec, + signal_handler: Option>>, externref_activations_table: VMExternRefActivationsTable, - modules: RefCell, - // The signatures and trampolines for `Func` objects - signatures: RefCell, + modules: ModuleRegistry, + host_trampolines: HashMap, // Numbers of resources instantiated in this store. - instance_count: Cell, - memory_count: Cell, - table_count: Cell, + instance_count: usize, + memory_count: usize, + table_count: usize, /// An adjustment to add to the fuel consumed value in `interrupts` above /// to get the true amount of fuel consumed. - fuel_adj: Cell, + fuel_adj: i64, + async_state: AsyncState, + out_of_gas_behavior: OutOfGas, + store_data: StoreData, + limiter: Option>, + default_callee: InstanceHandle, + data: T, +} + +struct AsyncState { #[cfg(feature = "async")] - current_suspend: Cell<*const wasmtime_fiber::Suspend, (), Result<(), Trap>>>, + current_suspend: + UnsafeCell<*const wasmtime_fiber::Suspend, (), Result<(), Trap>>>, #[cfg(feature = "async")] - current_poll_cx: Cell<*mut Context<'static>>, - out_of_gas_behavior: Cell, - context_values: RefCell>>, - limiter: Option>, + current_poll_cx: UnsafeCell<*mut Context<'static>>, +} + +// Lots of pesky unsafe cells and pointers in this structure. This means we need +// to declare explicitly that we use this in a threadsafe fashion. +#[cfg(feature = "async")] +unsafe impl Send for AsyncState {} +#[cfg(feature = "async")] +unsafe impl Sync for AsyncState {} + +/// Used to associate instances with the store. +/// +/// This is needed to track if the instance was allocated explicitly with the on-demand +/// instance allocator. +struct StoreInstance { + handle: InstanceHandle, + // Stores whether or not to use the on-demand allocator to deallocate the instance + ondemand: bool, } #[derive(Copy, Clone)] @@ -102,251 +137,86 @@ enum OutOfGas { }, } -struct HostInfoKey(VMExternRef); - -impl PartialEq for HostInfoKey { - fn eq(&self, rhs: &Self) -> bool { - VMExternRef::eq(&self.0, &rhs.0) - } -} - -impl Eq for HostInfoKey {} - -impl Hash for HostInfoKey { - fn hash(&self, hasher: &mut H) - where - H: Hasher, - { - VMExternRef::hash(&self.0, hasher); - } -} - -impl Store { - /// Creates a new [`Store`] to be associated with the given [`Engine`]. +impl Store { + /// Creates a new [`Store`] to be associated with the given [`Engine`] and + /// `data` provided. /// - /// The created [`Store`] will place no additional limits on the size of linear - /// memories or tables at runtime. Linear memories and tables will be allowed to - /// grow to any upper limit specified in their definitions. + /// The created [`Store`] will place no additional limits on the size of + /// linear memories or tables at runtime. Linear memories and tables will + /// be allowed to grow to any upper limit specified in their definitions. /// - /// The store will limit the number of instances, linear memories, and tables created to 10,000. + /// The store will limit the number of instances, linear memories, and + /// tables created to 10,000. /// - /// Use [`Store::new_with_limits`] with a [`StoreLimitsBuilder`](crate::StoreLimitsBuilder) to - /// specify different limits for the store. - pub fn new(engine: &Engine) -> Self { - Self::new_(engine, None) - } - - /// Creates a new [`Store`] to be associated with the given [`Engine`] and using the supplied - /// resource limiter. - /// - /// A [`ResourceLimiter`] can be implemented by hosts to control the size of WebAssembly - /// linear memories and tables when a request is made to grow them. - /// - /// [`StoreLimitsBuilder`](crate::StoreLimitsBuilder) can be used to create a - /// [`StoreLimits`](crate::StoreLimits) that implements [`ResourceLimiter`] using - /// static limit values. + /// Use [`Store::limiter`] with a + /// [`StoreLimitsBuilder`](crate::StoreLimitsBuilder) to specify different + /// limits for the store. /// - /// # Example - /// - /// ```rust - /// # use wasmtime::{Engine, Store, StoreLimitsBuilder}; - /// // Place a limit on linear memories so they cannot grow beyond 1 MiB - /// let engine = Engine::default(); - /// let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memory_pages(16).build()); - /// ``` - pub fn new_with_limits(engine: &Engine, limiter: impl ResourceLimiter + 'static) -> Self { - Self::new_(engine, Some(Rc::new(ResourceLimiterProxy(limiter)))) - } - - fn new_(engine: &Engine, limiter: Option>) -> Self { - // Ensure that wasmtime_runtime's signal handlers are configured. This - // is the per-program initialization required for handling traps, such - // as configuring signals, vectored exception handlers, etc. - wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_pc); - - Self { - inner: Rc::new(StoreInner { - engine: engine.clone(), - interrupts: Arc::new(Default::default()), - instances: RefCell::new(Vec::new()), - signal_handler: RefCell::new(None), - externref_activations_table: VMExternRefActivationsTable::new(), - modules: RefCell::new(ModuleRegistry::default()), - signatures: RefCell::new(SignatureCollection::new(engine.signatures())), - instance_count: Default::default(), - memory_count: Default::default(), - table_count: Default::default(), - fuel_adj: Cell::new(0), + /// TODO + pub fn new(engine: &Engine, data: T) -> Self { + let finished_functions = &Default::default(); + let default_callee = unsafe { + OnDemandInstanceAllocator::default() + .allocate(InstanceAllocationRequest { + host_state: Box::new(()), + finished_functions, + shared_signatures: None.into(), + imports: Default::default(), + module: Arc::new(wasmtime_environ::Module::default()), + store: None, + }) + .expect("failed to allocate default callee") + }; + let mut inner = Box::new(StoreInner { + _marker: marker::PhantomPinned, + engine: engine.clone(), + interrupts: Default::default(), + instances: Vec::new(), + signal_handler: None, + externref_activations_table: VMExternRefActivationsTable::new(), + modules: ModuleRegistry::default(), + host_trampolines: HashMap::default(), + instance_count: 0, + memory_count: 0, + table_count: 0, + fuel_adj: 0, + async_state: AsyncState { #[cfg(feature = "async")] - current_suspend: Cell::new(ptr::null()), + current_suspend: UnsafeCell::new(ptr::null()), #[cfg(feature = "async")] - current_poll_cx: Cell::new(ptr::null_mut()), - out_of_gas_behavior: Cell::new(OutOfGas::Trap), - context_values: RefCell::new(HashMap::new()), - limiter, - }), - } - } - - /// Gets a host function from the [`Config`](crate::Config) associated with this [`Store`]. - /// - /// Returns `None` if the given host function is not defined. - pub fn get_host_func(&self, module: &str, name: &str) -> Option { - self.inner - .engine - .config() - .get_host_func(module, name) - .map(|f| { - // This call is safe because we know the function is coming from the - // config associated with this store - unsafe { f.to_func(self) } - }) - } - - /// Returns the [`Engine`] that this store is associated with. - #[inline] - pub fn engine(&self) -> &Engine { - &self.inner.engine - } - - /// Gets a context value from the store. - /// - /// Returns a reference to the context value if present. - pub fn get(&self) -> Option<&T> { - let values = self.inner.context_values.borrow(); - - // Safety: a context value cannot be removed once added and therefore the address is - // stable for the life of the store - values - .get(&TypeId::of::()) - .map(|v| unsafe { &*(v.downcast_ref::().unwrap() as *const T) }) - } - - /// Sets a context value into the store. - /// - /// Returns the given value as an error if an existing value is already set. - pub fn set(&self, value: T) -> Result<(), T> { - let mut values = self.inner.context_values.borrow_mut(); - - match values.entry(value.type_id()) { - Entry::Occupied(_) => Err(value), - Entry::Vacant(v) => { - v.insert(Box::new(value)); - Ok(()) - } - } - } - - pub(crate) fn limiter(&self) -> &Option> { - &self.inner.limiter - } - - pub(crate) fn signatures(&self) -> &RefCell { - &self.inner.signatures - } - - pub(crate) fn lookup_trampoline(&self, anyfunc: &VMCallerCheckedAnyfunc) -> VMTrampoline { - // Look up the trampoline with the store's trampolines (from `Func`). - if let Some(trampoline) = self - .inner - .signatures - .borrow() - .trampoline(anyfunc.type_index) - { - return trampoline; - } - - // Look up the trampoline with the registered modules - if let Some(trampoline) = self.inner.modules.borrow().lookup_trampoline(anyfunc) { - return trampoline; - } - - // Lastly, check with the engine (for `HostFunc`) - self.inner - .engine - .host_func_signatures() - .trampoline(anyfunc.type_index) - .expect("trampoline missing") - } - - pub(crate) fn bump_resource_counts(&self, module: &Module) -> Result<()> { - fn bump(slot: &Cell, max: usize, amt: usize, desc: &str) -> Result<()> { - let new = slot.get().saturating_add(amt); - if new > max { - bail!( - "resource limit exceeded: {} count too high at {}", - desc, - new - ); - } - slot.set(new); - Ok(()) - } - - let module = module.env_module(); - let memories = module.memory_plans.len() - module.num_imported_memories; - let tables = module.table_plans.len() - module.num_imported_tables; - let (max_instances, max_memories, max_tables) = self.limits(); - - bump(&self.inner.instance_count, max_instances, 1, "instance")?; - bump(&self.inner.memory_count, max_memories, memories, "memory")?; - bump(&self.inner.table_count, max_tables, tables, "table")?; - - Ok(()) - } - - pub(crate) unsafe fn add_instance( - &self, - handle: InstanceHandle, - ondemand: bool, - ) -> StoreInstanceHandle { - self.inner.instances.borrow_mut().push(StoreInstance { - handle: handle.clone(), - ondemand, + current_poll_cx: UnsafeCell::new(ptr::null_mut()), + }, + out_of_gas_behavior: OutOfGas::Trap, + store_data: StoreData::new(), + limiter: None, + default_callee, + data, }); - StoreInstanceHandle { - store: self.clone(), - handle, - } - } - - pub(crate) fn existing_instance_handle(&self, handle: InstanceHandle) -> StoreInstanceHandle { - debug_assert!( - self.inner - .instances - .borrow() - .iter() - .any(|i| i.handle.vmctx_ptr() == handle.vmctx_ptr()) - || self.inner.engine.host_func_anyfunc(&handle).is_some() - ); - StoreInstanceHandle { - store: self.clone(), - handle, + let store = StoreContextMut(&mut *inner).opaque().traitobj; + unsafe { + inner.default_callee.set_store(store); } + Self { inner } } - pub(crate) unsafe fn existing_vmctx(&self, cx: *mut VMContext) -> StoreInstanceHandle { - self.existing_instance_handle(InstanceHandle::from_vmctx(cx)) + /// Access the underlying data owned by this `Store`. + pub fn data(&self) -> &T { + self.inner.data() } - #[cfg_attr(not(target_os = "linux"), allow(dead_code))] // not used on all platforms - pub(crate) fn set_signal_handler(&self, handler: Option>>) { - *self.inner.signal_handler.borrow_mut() = handler; + /// Access the underlying data owned by this `Store`. + pub fn data_mut(&mut self) -> &mut T { + self.inner.data_mut() } - #[inline] - pub(crate) fn interrupts(&self) -> &VMInterrupts { - &self.inner.interrupts + /// TODO + pub fn limiter(&mut self, limiter: impl crate::ResourceLimiter) { + self.inner.limiter = Some(Box::new(crate::limits::ResourceLimiterProxy(limiter))); } - /// Returns whether the stores `a` and `b` refer to the same underlying - /// `Store`. - /// - /// Because the `Store` type is reference counted multiple clones may point - /// to the same underlying storage, and this method can be used to determine - /// whether two stores are indeed the same. - pub fn same(a: &Store, b: &Store) -> bool { - Rc::ptr_eq(&a.inner, &b.inner) + /// Returns the [`Engine`] that this store is associated with. + pub fn engine(&self) -> &Engine { + self.inner.engine() } /// Creates an [`InterruptHandle`] which can be used to interrupt the @@ -429,37 +299,12 @@ impl Store { /// # } /// ``` pub fn interrupt_handle(&self) -> Result { - if self.engine().config().tunables.interruptable { - Ok(InterruptHandle { - interrupts: self.inner.interrupts.clone(), - }) - } else { - bail!("interrupts aren't enabled for this `Store`") - } - } - - #[inline] - pub(crate) fn externref_activations_table(&self) -> &VMExternRefActivationsTable { - &self.inner.externref_activations_table - } - - #[inline] - pub(crate) fn modules(&self) -> &RefCell { - &self.inner.modules - } - - #[inline] - pub(crate) fn module_info_lookup(&self) -> &dyn wasmtime_runtime::ModuleInfoLookup { - self.inner.as_ref() + self.inner.interrupt_handle() } /// Perform garbage collection of `ExternRef`s. - pub fn gc(&self) { - // For this crate's API, we ensure that `set_stack_canary` invariants - // are upheld for all host-->Wasm calls. - unsafe { - wasmtime_runtime::gc(self.inner.as_ref(), &self.inner.externref_activations_table); - } + pub fn gc(&mut self) { + self.inner.gc() } /// Returns the amount of fuel consumed by this store's execution so far. @@ -469,11 +314,7 @@ impl Store { /// function will return `None`. Also note that fuel, if enabled, must be /// originally configured via [`Store::add_fuel`]. pub fn fuel_consumed(&self) -> Option { - if !self.engine().config().tunables.consume_fuel { - return None; - } - let consumed = unsafe { *self.inner.interrupts.fuel_consumed.get() }; - Some(u64::try_from(self.inner.fuel_adj.get() + consumed).unwrap()) + self.inner.fuel_consumed() } /// Adds fuel to this [`Store`] for wasm to consume while executing. @@ -496,38 +337,8 @@ impl Store { /// /// This function will panic if the store's [`Config`](crate::Config) did /// not have fuel consumption enabled. - pub fn add_fuel(&self, fuel: u64) -> Result<()> { - anyhow::ensure!( - self.engine().config().tunables.consume_fuel, - "fuel is not configured in this store" - ); - - // Fuel is stored as an i64, so we need to cast it. If the provided fuel - // value overflows that just assume that i64::max will suffice. Wasm - // execution isn't fast enough to burn through i64::max fuel in any - // reasonable amount of time anyway. - let fuel = i64::try_from(fuel).unwrap_or(i64::max_value()); - let adj = self.inner.fuel_adj.get(); - let consumed_ptr = unsafe { &mut *self.inner.interrupts.fuel_consumed.get() }; - - match (consumed_ptr.checked_sub(fuel), adj.checked_add(fuel)) { - // If we succesfully did arithmetic without overflowing then we can - // just update our fields. - (Some(consumed), Some(adj)) => { - self.inner.fuel_adj.set(adj); - *consumed_ptr = consumed; - } - - // Otherwise something overflowed. Make sure that we preserve the - // amount of fuel that's already consumed, but otherwise assume that - // we were given infinite fuel. - _ => { - self.inner.fuel_adj.set(i64::max_value()); - *consumed_ptr = (*consumed_ptr + adj) - i64::max_value(); - } - } - - Ok(()) + pub fn add_fuel(&mut self, fuel: u64) -> Result<()> { + self.inner.add_fuel(fuel) } /// Configures a [`Store`] to generate a [`Trap`] whenever it runs out of @@ -540,8 +351,8 @@ impl Store { /// aborted. /// /// This is the default behavior for running out of fuel. - pub fn out_of_fuel_trap(&self) { - self.inner.out_of_gas_behavior.set(OutOfGas::Trap); + pub fn out_of_fuel_trap(&mut self) { + self.inner.out_of_fuel_trap() } /// Configures a [`Store`] to yield execution of async WebAssembly code @@ -578,149 +389,446 @@ impl Store { /// /// This method will panic if it is not called on a store associated with an [async /// config](crate::Config::async_support). - pub fn out_of_fuel_async_yield(&self, injection_count: u32, fuel_to_inject: u64) { - assert!( - self.async_support(), - "cannot use `out_of_fuel_async_yield` without enabling async support in the config" - ); - self.inner.out_of_gas_behavior.set(OutOfGas::InjectFuel { - injection_count, - fuel_to_inject, - }); + pub fn out_of_fuel_async_yield(&mut self, injection_count: u32, fuel_to_inject: u64) { + self.inner + .out_of_fuel_async_yield(injection_count, fuel_to_inject) } +} - #[inline] +impl<'a, T> StoreContext<'a, T> { pub(crate) fn async_support(&self) -> bool { - self.inner.engine.config().async_support + self.0.async_support() } - /// Blocks on the asynchronous computation represented by `future` and - /// produces the result here, in-line. + /// Returns the underlying [`Engine`] this store is connected to. + pub fn engine(&self) -> &Engine { + self.0.engine() + } + + /// Access the underlying data owned by this `Store`. /// - /// This function is designed to only work when it's currently executing on - /// a native fiber. This fiber provides the ability for us to handle the - /// future's `Pending` state as "jump back to whomever called the fiber in - /// an asynchronous fashion and propagate `Pending`". This tight coupling - /// with `on_fiber` below is what powers the asynchronicity of calling wasm. - /// Note that the asynchronous part only applies to host functions, wasm - /// itself never really does anything asynchronous at this time. + /// Same as [`Store::data`]. + pub fn data(&self) -> &T { + self.0.data() + } + + /// Returns the fuel consumed by this store. /// - /// This function takes a `future` and will (appear to) synchronously wait - /// on the result. While this function is executing it will fiber switch - /// to-and-from the original frame calling `on_fiber` which should be a - /// guarantee due to how async stores are configured. + /// For more information see [`Store::fuel_consumed`]. + pub fn fuel_consumed(&self) -> Option { + self.0.fuel_consumed() + } +} + +impl<'a, T> StoreContextMut<'a, T> { + /// Access the underlying data owned by this `Store`. /// - /// The return value here is either the output of the future `T`, or a trap - /// which represents that the asynchronous computation was cancelled. It is - /// not recommended to catch the trap and try to keep executing wasm, so - /// we've tried to liberally document this. - #[cfg(feature = "async")] - pub(crate) fn block_on( - &self, - mut future: Pin<&mut dyn Future>, - ) -> Result { - debug_assert!(self.async_support()); + /// Same as [`Store::data`]. + pub fn data(&self) -> &T { + self.0.data() + } - // Take our current `Suspend` context which was configured as soon as - // our fiber started. Note that we must load it at the front here and - // save it on our stack frame. While we're polling the future other - // fibers may be started for recursive computations, and the current - // suspend context is only preserved at the edges of the fiber, not - // during the fiber itself. - // - // For a little bit of extra safety we also replace the current value - // with null to try to catch any accidental bugs on our part early. - // This is all pretty unsafe so we're trying to be careful... - // - // Note that there should be a segfaulting test in `async_functions.rs` - // if this `Reset` is removed. - let suspend = self.inner.current_suspend.replace(ptr::null()); - let _reset = Reset(&self.inner.current_suspend, suspend); - assert!(!suspend.is_null()); + /// Access the underlying data owned by this `Store`. + /// + /// Same as [`Store::data_mut`]. + pub fn data_mut(&mut self) -> &mut T { + self.0.data_mut() + } - loop { - let future_result = unsafe { - let current_poll_cx = self.inner.current_poll_cx.replace(ptr::null_mut()); - let _reset = Reset(&self.inner.current_poll_cx, current_poll_cx); - assert!(!current_poll_cx.is_null()); - future.as_mut().poll(&mut *current_poll_cx) - }; - match future_result { - Poll::Ready(t) => break Ok(t), - Poll::Pending => {} - } + /// Perform garbage collection of `ExternRef`s. + /// + /// Same as [`Store::gc`]. + pub fn gc(&mut self) { + self.0.gc() + } - unsafe { - let before = wasmtime_runtime::TlsRestore::take() - .map_err(|e| Trap::from_runtime(self, e))?; - let res = (*suspend).suspend(()); - before.replace().map_err(|e| Trap::from_runtime(self, e))?; - res?; - } - } + /// Returns the fuel consumed by this store. + /// + /// For more information see [`Store::fuel_consumed`]. + pub fn fuel_consumed(&self) -> Option { + self.0.fuel_consumed() } - /// Executes a synchronous computation `func` asynchronously on a new fiber. + /// Inject more fuel into this store to be consumed when executing wasm code. /// - /// This function will convert the synchronous `func` into an asynchronous - /// future. This is done by running `func` in a fiber on a separate native - /// stack which can be suspended and resumed from. + /// For more information see [`Store::add_fuel`] + pub fn add_fuel(&mut self, fuel: u64) -> Result<()> { + self.0.add_fuel(fuel) + } + + /// Configures this `Store` to trap whenever fuel runs out. /// - /// Most of the nitty-gritty here is how we juggle the various contexts - /// necessary to suspend the fiber later on and poll sub-futures. It's hoped - /// that the various comments are illuminating as to what's going on here. - #[cfg(feature = "async")] - pub(crate) async fn on_fiber(&self, func: impl FnOnce() -> R) -> Result { - let config = self.inner.engine.config(); + /// For more information see [`Store::out_of_fuel_trap`] + pub fn out_of_fuel_trap(&mut self) { + self.0.out_of_fuel_trap() + } - debug_assert!(self.async_support()); - debug_assert!(config.async_stack_size > 0); + /// Configures this `Store` to yield while executing futures whenever fuel + /// runs out. + /// + /// For more information see [`Store::out_of_fuel_async_yield`] + pub fn out_of_fuel_async_yield(&mut self, injection_count: u32, fuel_to_inject: u64) { + self.0 + .out_of_fuel_async_yield(injection_count, fuel_to_inject) + } - let stack = self - .inner - .engine - .allocator() - .allocate_fiber_stack() - .map_err(|e| Trap::from(anyhow::Error::from(e)))?; + pub(crate) fn store_data(self) -> &'a StoreData { + self.0.store_data() + } +} - let mut slot = None; - let fiber = wasmtime_fiber::Fiber::new(stack, |keep_going, suspend| { - // First check and see if we were interrupted/dropped, and only - // continue if we haven't been. - keep_going?; - - // Configure our store's suspension context for the rest of the - // execution of this fiber. Note that a raw pointer is stored here - // which is only valid for the duration of this closure. - // Consequently we at least replace it with the previous value when - // we're done. This reset is also required for correctness because - // otherwise our value will overwrite another active fiber's value. - // There should be a test that segfaults in `async_functions.rs` if - // this `Replace` is removed. - let prev = self.inner.current_suspend.replace(suspend); - let _reset = Reset(&self.inner.current_suspend, prev); - - slot = Some(func()); - Ok(()) - }) - .map_err(|e| Trap::from(anyhow::Error::from(e)))?; +impl StoreInner { + fn data(&self) -> &T { + &self.data + } - // Once we have the fiber representing our synchronous computation, we - // wrap that in a custom future implementation which does the - // translation from the future protocol to our fiber API. - FiberFuture { fiber, store: self }.await?; + fn data_mut(&mut self) -> &mut T { + &mut self.data + } - return Ok(slot.unwrap()); + pub fn async_support(&self) -> bool { + self.engine().config().async_support + } - struct FiberFuture<'a> { - fiber: wasmtime_fiber::Fiber<'a, Result<(), Trap>, (), Result<(), Trap>>, - store: &'a Store, - } + pub fn engine(&self) -> &Engine { + &self.engine + } - impl Future for FiberFuture<'_> { - type Output = Result<(), Trap>; + pub fn limiter(&mut self) -> Option<&mut dyn wasmtime_runtime::ResourceLimiter> { + self.limiter.as_mut().map(|l| &mut **l) + } + + pub fn store_data(&self) -> &StoreData { + &self.store_data + } + + pub fn store_data_mut(&mut self) -> &mut StoreData { + &mut self.store_data + } + + pub fn register_host_trampoline( + &mut self, + idx: VMSharedSignatureIndex, + trampoline: VMTrampoline, + ) { + self.host_trampolines.insert(idx, trampoline); + } + + pub fn interrupt_handle(&self) -> Result { + if self.engine.config().tunables.interruptable { + Ok(InterruptHandle { + interrupts: self.interrupts.clone(), + }) + } else { + bail!("interrupts aren't enabled for this `Store`") + } + } + + #[inline] + pub(crate) fn modules_mut(&mut self) -> &mut ModuleRegistry { + &mut self.modules + } + + pub unsafe fn add_instance(&mut self, handle: InstanceHandle, ondemand: bool) -> InstanceId { + self.instances.push(StoreInstance { + handle: handle.clone(), + ondemand, + }); + InstanceId(self.instances.len() - 1) + } + + pub fn instance(&self, id: InstanceId) -> &InstanceHandle { + &self.instances[id.0].handle + } + + pub fn instance_mut(&mut self, id: InstanceId) -> &mut InstanceHandle { + &mut self.instances[id.0].handle + } + + #[cfg_attr(not(target_os = "linux"), allow(dead_code))] // not used on all platforms + pub fn set_signal_handler(&mut self, handler: Option>>) { + self.signal_handler = handler; + } + + #[inline] + pub fn interrupts(&self) -> &VMInterrupts { + &self.interrupts + } + + #[inline] + pub fn externref_activations_table(&mut self) -> &mut VMExternRefActivationsTable { + &mut self.externref_activations_table + } + + pub fn gc(&mut self) { + // For this crate's API, we ensure that `set_stack_canary` invariants + // are upheld for all host-->Wasm calls. + unsafe { wasmtime_runtime::gc(&self.modules, &mut self.externref_activations_table) } + } + + pub fn bump_resource_counts(&mut self, module: &Module) -> Result<()> { + fn bump(slot: &mut usize, max: usize, amt: usize, desc: &str) -> Result<()> { + let new = slot.saturating_add(amt); + if new > max { + bail!( + "resource limit exceeded: {} count too high at {}", + desc, + new + ); + } + *slot = new; + Ok(()) + } + + let module = module.env_module(); + let memories = module.memory_plans.len() - module.num_imported_memories; + let tables = module.table_plans.len() - module.num_imported_tables; + let (max_instances, max_memories, max_tables) = self.limits(); + + bump(&mut self.instance_count, max_instances, 1, "instance")?; + bump(&mut self.memory_count, max_memories, memories, "memory")?; + bump(&mut self.table_count, max_tables, tables, "table")?; + + Ok(()) + } + + fn limits(&self) -> (usize, usize, usize) { + self.limiter + .as_ref() + .map(|l| (l.instances(), l.memories(), l.tables())) + .unwrap_or(( + crate::limits::DEFAULT_INSTANCE_LIMIT, + crate::limits::DEFAULT_MEMORY_LIMIT, + crate::limits::DEFAULT_TABLE_LIMIT, + )) + } + + pub fn lookup_trampoline(&self, anyfunc: &VMCallerCheckedAnyfunc) -> VMTrampoline { + // Look up the trampoline with the store's trampolines (from `Func`). + if let Some(trampoline) = self.host_trampolines.get(&anyfunc.type_index) { + return *trampoline; + } + + // Look up the trampoline with the registered modules + if let Some(trampoline) = self.modules.lookup_trampoline(anyfunc) { + return trampoline; + } + + panic!("trampoline missing") + } + + #[cfg(feature = "async")] + pub fn async_cx(&self) -> AsyncCx { + debug_assert!(self.async_support()); + AsyncCx { + current_suspend: self.async_state.current_suspend.get(), + current_poll_cx: self.async_state.current_poll_cx.get(), + } + } - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + pub fn fuel_consumed(&self) -> Option { + if !self.engine.config().tunables.consume_fuel { + return None; + } + let consumed = unsafe { *self.interrupts.fuel_consumed.get() }; + Some(u64::try_from(self.fuel_adj + consumed).unwrap()) + } + + fn out_of_fuel_trap(&mut self) { + self.out_of_gas_behavior = OutOfGas::Trap; + } + + fn out_of_fuel_async_yield(&mut self, injection_count: u32, fuel_to_inject: u64) { + assert!( + self.async_support(), + "cannot use `out_of_fuel_async_yield` without enabling async support in the config" + ); + self.out_of_gas_behavior = OutOfGas::InjectFuel { + injection_count, + fuel_to_inject, + }; + } + + /// Yields execution to the caller on out-of-gas + /// + /// This only works on async futures and stores, and assumes that we're + /// executing on a fiber. This will yield execution back to the caller once + /// and when we come back we'll continue with `fuel_to_inject` more fuel. + #[cfg(feature = "async")] + fn out_of_gas_yield(&mut self, fuel_to_inject: u64) -> Result<(), Trap> { + // Small future that yields once and then returns () + #[derive(Default)] + struct Yield { + yielded: bool, + } + + impl Future for Yield { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + if self.yielded { + Poll::Ready(()) + } else { + // Flag ourselves as yielded to return next time, and also + // flag the waker that we're already ready to get + // re-enqueued for another poll. + self.yielded = true; + cx.waker().wake_by_ref(); + Poll::Pending + } + } + } + + let mut future = Yield::default(); + let result = unsafe { self.async_cx().block_on(Pin::new_unchecked(&mut future)) }; + match result { + // If this finished successfully then we were resumed normally via a + // `poll`, so inject some more fuel and keep going. + Ok(()) => { + self.add_fuel(fuel_to_inject).unwrap(); + Ok(()) + } + // If the future was dropped while we were yielded, then we need to + // clean up this fiber. Do so by raising a trap which will abort all + // wasm and get caught on the other side to clean things up. + Err(trap) => Err(trap), + } + } + + fn add_fuel(&mut self, fuel: u64) -> Result<()> { + anyhow::ensure!( + self.engine().config().tunables.consume_fuel, + "fuel is not configured in this store" + ); + + // Fuel is stored as an i64, so we need to cast it. If the provided fuel + // value overflows that just assume that i64::max will suffice. Wasm + // execution isn't fast enough to burn through i64::max fuel in any + // reasonable amount of time anyway. + let fuel = i64::try_from(fuel).unwrap_or(i64::max_value()); + let adj = self.fuel_adj; + let consumed_ptr = unsafe { &mut *self.interrupts.fuel_consumed.get() }; + + match (consumed_ptr.checked_sub(fuel), adj.checked_add(fuel)) { + // If we succesfully did arithmetic without overflowing then we can + // just update our fields. + (Some(consumed), Some(adj)) => { + self.fuel_adj = adj; + *consumed_ptr = consumed; + } + + // Otherwise something overflowed. Make sure that we preserve the + // amount of fuel that's already consumed, but otherwise assume that + // we were given infinite fuel. + _ => { + self.fuel_adj = i64::max_value(); + *consumed_ptr = (*consumed_ptr + adj) - i64::max_value(); + } + } + + Ok(()) + } + + pub fn signal_handler(&self) -> Option<*const SignalHandler<'static>> { + let handler = self.signal_handler.as_ref()?; + Some(&**handler as *const _) + } + + pub fn vminterrupts(&self) -> *mut VMInterrupts { + &*self.interrupts as *const VMInterrupts as *mut VMInterrupts + } + + pub unsafe fn insert_vmexternref(&mut self, r: VMExternRef) { + self.externref_activations_table + .insert_with_gc(r, &self.modules) + } + + pub fn default_callee(&self) -> *mut VMContext { + self.default_callee.vmctx_ptr() + } +} + +impl StoreOpaqueSend<'_> { + /// Executes a synchronous computation `func` asynchronously on a new fiber. + /// + /// This function will convert the synchronous `func` into an asynchronous + /// future. This is done by running `func` in a fiber on a separate native + /// stack which can be suspended and resumed from. + /// + /// Most of the nitty-gritty here is how we juggle the various contexts + /// necessary to suspend the fiber later on and poll sub-futures. It's hoped + /// that the various comments are illuminating as to what's going on here. + #[cfg(feature = "async")] + pub async fn on_fiber( + &mut self, + func: impl FnOnce(&mut StoreOpaque<'_>) -> R + Send, + ) -> Result { + let config = self.engine.config(); + + debug_assert!(self.async_support()); + debug_assert!(config.async_stack_size > 0); + + let mut slot = None; + let future = { + let current_poll_cx = self.async_state.current_poll_cx.get(); + let current_suspend = self.async_state.current_suspend.get(); + let stack = self + .engine + .allocator() + .allocate_fiber_stack() + .map_err(|e| Trap::from(anyhow::Error::from(e)))?; + + let engine = self.engine().clone(); + let slot = &mut slot; + let fiber = wasmtime_fiber::Fiber::new(stack, move |keep_going, suspend| { + // First check and see if we were interrupted/dropped, and only + // continue if we haven't been. + keep_going?; + + // Configure our store's suspension context for the rest of the + // execution of this fiber. Note that a raw pointer is stored here + // which is only valid for the duration of this closure. + // Consequently we at least replace it with the previous value when + // we're done. This reset is also required for correctness because + // otherwise our value will overwrite another active fiber's value. + // There should be a test that segfaults in `async_functions.rs` if + // this `Replace` is removed. + unsafe { + let _reset = Reset(current_suspend, *current_suspend); + *current_suspend = suspend; + + *slot = Some(func(&mut self.opaque())); + Ok(()) + } + }) + .map_err(|e| Trap::from(anyhow::Error::from(e)))?; + + // Once we have the fiber representing our synchronous computation, we + // wrap that in a custom future implementation which does the + // translation from the future protocol to our fiber API. + FiberFuture { + fiber, + current_poll_cx, + engine, + } + }; + future.await?; + + return Ok(slot.unwrap()); + + struct FiberFuture<'a> { + fiber: wasmtime_fiber::Fiber<'a, Result<(), Trap>, (), Result<(), Trap>>, + current_poll_cx: *mut *mut Context<'static>, + engine: Engine, + } + + // TODO + unsafe impl Send for FiberFuture<'_> {} + + impl Future for FiberFuture<'_> { + type Output = Result<(), Trap>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { // We need to carry over this `cx` into our fiber's runtime // for when it tries to poll sub-futures that are created. Doing // this must be done unsafely, however, since `cx` is only alive @@ -736,20 +844,21 @@ impl Store { // On exit from this function, though, we reset the polling // context back to what it was to signify that `Store` no longer // has access to this pointer. - let cx = - unsafe { std::mem::transmute::<&mut Context<'_>, *mut Context<'static>>(cx) }; - let prev = self.store.inner.current_poll_cx.replace(cx); - let _reset = Reset(&self.store.inner.current_poll_cx, prev); - - // After that's set up we resume execution of the fiber, which - // may also start the fiber for the first time. This either - // returns `Ok` saying the fiber finished (yay!) or it returns - // `Err` with the payload passed to `suspend`, which in our case - // is `()`. If `Err` is returned that means the fiber polled a - // future but it said "Pending", so we propagate that here. - match self.fiber.resume(Ok(())) { - Ok(result) => Poll::Ready(result), - Err(()) => Poll::Pending, + unsafe { + let _reset = Reset(self.current_poll_cx, *self.current_poll_cx); + *self.current_poll_cx = + std::mem::transmute::<&mut Context<'_>, *mut Context<'static>>(cx); + + // After that's set up we resume execution of the fiber, which + // may also start the fiber for the first time. This either + // returns `Ok` saying the fiber finished (yay!) or it returns + // `Err` with the payload passed to `suspend`, which in our case + // is `()`. If `Err` is returned that means the fiber polled a + // future but it said "Pending", so we propagate that here. + match self.fiber.resume(Ok(())) { + Ok(result) => Poll::Ready(result), + Err(()) => Poll::Pending, + } } } } @@ -781,160 +890,174 @@ impl Store { } unsafe { - self.store - .engine() + self.engine .allocator() .deallocate_fiber_stack(self.fiber.stack()); } } } } +} - /// Immediately raise a trap on an out-of-gas condition. - fn out_of_gas_trap(&self) -> ! { - #[derive(Debug)] - struct OutOfGasError; - - impl fmt::Display for OutOfGasError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("all fuel consumed by WebAssembly") - } - } - - impl std::error::Error for OutOfGasError {} - unsafe { - wasmtime_runtime::raise_lib_trap(wasmtime_runtime::Trap::User(Box::new(OutOfGasError))) - } - } +#[cfg(feature = "async")] +pub struct AsyncCx { + current_suspend: *mut *const wasmtime_fiber::Suspend, (), Result<(), Trap>>, + current_poll_cx: *mut *mut Context<'static>, +} - /// Yields execution to the caller on out-of-gas +#[cfg(feature = "async")] +impl AsyncCx { + /// Blocks on the asynchronous computation represented by `future` and + /// produces the result here, in-line. /// - /// This only works on async futures and stores, and assumes that we're - /// executing on a fiber. This will yield execution back to the caller once - /// and when we come back we'll continue with `fuel_to_inject` more fuel. - #[cfg(feature = "async")] - fn out_of_gas_yield(&self, fuel_to_inject: u64) { - // Small future that yields once and then returns () - #[derive(Default)] - struct Yield { - yielded: bool, - } + /// This function is designed to only work when it's currently executing on + /// a native fiber. This fiber provides the ability for us to handle the + /// future's `Pending` state as "jump back to whomever called the fiber in + /// an asynchronous fashion and propagate `Pending`". This tight coupling + /// with `on_fiber` below is what powers the asynchronicity of calling wasm. + /// Note that the asynchronous part only applies to host functions, wasm + /// itself never really does anything asynchronous at this time. + /// + /// This function takes a `future` and will (appear to) synchronously wait + /// on the result. While this function is executing it will fiber switch + /// to-and-from the original frame calling `on_fiber` which should be a + /// guarantee due to how async stores are configured. + /// + /// The return value here is either the output of the future `T`, or a trap + /// which represents that the asynchronous computation was cancelled. It is + /// not recommended to catch the trap and try to keep executing wasm, so + /// we've tried to liberally document this. + pub unsafe fn block_on( + &self, + mut future: Pin<&mut (dyn Future + Send)>, + ) -> Result { + // Take our current `Suspend` context which was configured as soon as + // our fiber started. Note that we must load it at the front here and + // save it on our stack frame. While we're polling the future other + // fibers may be started for recursive computations, and the current + // suspend context is only preserved at the edges of the fiber, not + // during the fiber itself. + // + // For a little bit of extra safety we also replace the current value + // with null to try to catch any accidental bugs on our part early. + // This is all pretty unsafe so we're trying to be careful... + // + // Note that there should be a segfaulting test in `async_functions.rs` + // if this `Reset` is removed. + let suspend = *self.current_suspend; + let _reset = Reset(self.current_suspend, suspend); + *self.current_suspend = ptr::null(); + assert!(!suspend.is_null()); - impl Future for Yield { - type Output = (); + loop { + let future_result = { + let poll_cx = *self.current_poll_cx; + let _reset = Reset(self.current_poll_cx, poll_cx); + *self.current_poll_cx = ptr::null_mut(); + assert!(!poll_cx.is_null()); + future.as_mut().poll(&mut *poll_cx) + }; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - if self.yielded { - Poll::Ready(()) - } else { - // Flag ourselves as yielded to return next time, and also - // flag the waker that we're already ready to get - // re-enqueued for another poll. - self.yielded = true; - cx.waker().wake_by_ref(); - Poll::Pending - } + match future_result { + Poll::Ready(t) => break Ok(t), + Poll::Pending => {} } - } - let mut future = Yield::default(); - match self.block_on(unsafe { Pin::new_unchecked(&mut future) }) { - // If this finished successfully then we were resumed normally via a - // `poll`, so inject some more fuel and keep going. - Ok(()) => self.add_fuel(fuel_to_inject).unwrap(), - // If the future was dropped while we were yielded, then we need to - // clean up this fiber. Do so by raising a trap which will abort all - // wasm and get caught on the other side to clean things up. - Err(trap) => unsafe { wasmtime_runtime::raise_user_trap(trap.into()) }, + let before = wasmtime_runtime::TlsRestore::take().map_err(Trap::from_runtime)?; + let res = (*suspend).suspend(()); + before.replace().map_err(Trap::from_runtime)?; + res?; } } +} - fn limits(&self) -> (usize, usize, usize) { - self.inner - .limiter - .as_ref() - .map(|l| (l.instances(), l.memories(), l.tables())) - .unwrap_or(( - DEFAULT_INSTANCE_LIMIT, - DEFAULT_MEMORY_LIMIT, - DEFAULT_TABLE_LIMIT, - )) +unsafe impl wasmtime_runtime::Store for StoreInner { + fn vminterrupts(&self) -> *mut VMInterrupts { + >::vminterrupts(self) } -} -unsafe impl TrapInfo for Store { - #[inline] - fn as_any(&self) -> &dyn Any { - self + fn externref_activations_table( + &mut self, + ) -> ( + &mut VMExternRefActivationsTable, + &dyn wasmtime_runtime::ModuleInfoLookup, + ) { + (&mut self.externref_activations_table, &self.modules) } - fn custom_signal_handler(&self, call: &dyn Fn(&SignalHandler) -> bool) -> bool { - if let Some(handler) = &*self.inner.signal_handler.borrow() { - return call(handler); - } - false + fn limiter(&mut self) -> Option<&mut dyn wasmtime_runtime::ResourceLimiter> { + self.limiter.as_mut().map(|l| &mut **l) } - fn out_of_gas(&self) { - match self.inner.out_of_gas_behavior.get() { - OutOfGas::Trap => self.out_of_gas_trap(), + fn out_of_gas(&mut self) -> Result<(), Box> { + return match &mut self.out_of_gas_behavior { + OutOfGas::Trap => Err(Box::new(OutOfGasError)), #[cfg(feature = "async")] OutOfGas::InjectFuel { injection_count, fuel_to_inject, } => { - if injection_count == 0 { - self.out_of_gas_trap(); + if *injection_count == 0 { + return Err(Box::new(OutOfGasError)); } - self.inner.out_of_gas_behavior.set(OutOfGas::InjectFuel { - injection_count: injection_count - 1, - fuel_to_inject, - }); - self.out_of_gas_yield(fuel_to_inject); + *injection_count -= 1; + let fuel = *fuel_to_inject; + StoreContextMut(self).opaque().out_of_gas_yield(fuel)?; + Ok(()) } #[cfg(not(feature = "async"))] OutOfGas::InjectFuel { .. } => unreachable!(), + }; + + #[derive(Debug)] + struct OutOfGasError; + + impl fmt::Display for OutOfGasError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("all fuel consumed by WebAssembly") + } } - } - #[inline] - fn interrupts(&self) -> &VMInterrupts { - &self.inner.interrupts + impl std::error::Error for OutOfGasError {} } } -impl Default for Store { - fn default() -> Store { - Store::new(&Engine::default()) +impl Default for Store { + fn default() -> Store { + Store::new(&Engine::default(), T::default()) } } -impl fmt::Debug for Store { +impl fmt::Debug for Store { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let inner = &*self.inner as *const StoreInner; - f.debug_struct("Store").field("inner", &inner).finish() + let inner = &*self.inner as *const StoreInner; + f.debug_struct("Store") + .field("inner", &inner) + .field("data", &self.inner.data) + .finish() } } -impl Drop for StoreInner { +impl Drop for StoreInner { fn drop(&mut self) { let allocator = self.engine.allocator(); - let ondemand = OnDemandInstanceAllocator::default(); - for instance in self.instances.borrow().iter() { - unsafe { + unsafe { + let ondemand = OnDemandInstanceAllocator::default(); + for instance in self.instances.iter() { if instance.ondemand { ondemand.deallocate(&instance.handle); } else { allocator.deallocate(&instance.handle); } } + ondemand.deallocate(&self.default_callee); } } } -impl wasmtime_runtime::ModuleInfoLookup for StoreInner { +impl wasmtime_runtime::ModuleInfoLookup for ModuleRegistry { fn lookup(&self, pc: usize) -> Option> { - self.modules.borrow().lookup_module(pc) + self.lookup_module(pc) } } @@ -946,13 +1069,6 @@ pub struct InterruptHandle { interrupts: Arc, } -// The `VMInterrupts` type is a pod-type with no destructor, and we only access -// `interrupts` from other threads, so add in these trait impls which are -// otherwise not available due to the `fuel_consumed` variable in -// `VMInterrupts`. -unsafe impl Send for InterruptHandle {} -unsafe impl Sync for InterruptHandle {} - impl InterruptHandle { /// Flags that execution within this handle's original [`Store`] should be /// interrupted. @@ -966,10 +1082,12 @@ impl InterruptHandle { } } -struct Reset<'a, T: Copy>(&'a Cell, T); +struct Reset(*mut T, T); -impl Drop for Reset<'_, T> { +impl Drop for Reset { fn drop(&mut self) { - self.0.set(self.1); + unsafe { + *self.0 = self.1; + } } } diff --git a/crates/wasmtime/src/store/context.rs b/crates/wasmtime/src/store/context.rs new file mode 100644 index 000000000000..37bc7e0c42ad --- /dev/null +++ b/crates/wasmtime/src/store/context.rs @@ -0,0 +1,224 @@ +use crate::store::{Store, StoreInner}; +use std::ops::{Deref, DerefMut}; + +/// TODO +pub struct StoreContext<'a, T>(pub(super) &'a StoreInner); + +/// TODO +pub struct StoreContextMut<'a, T>(pub(super) &'a mut StoreInner); + +impl<'a, T> StoreContextMut<'a, T> { + // TODO + pub(crate) unsafe fn from_raw( + store: *mut dyn wasmtime_runtime::Store, + ) -> StoreContextMut<'a, T> { + StoreContextMut(&mut *(store as *mut StoreInner)) + } + + /// A helper method to erase the `T` on `Self` so the returned type has no + /// generics. For some more information see [`StoreOpaque`] itself. + /// + /// The primary purpose of this is to help improve compile times where + /// non-generic code can be compiled into libwasmtime.rlib. + pub(crate) fn opaque(mut self) -> StoreOpaque<'a> { + StoreOpaque { + traitobj: self.traitobj(), + inner: self.0, + } + } + + pub(crate) fn opaque_send(mut self) -> StoreOpaqueSend<'a> + where + T: Send, + { + StoreOpaqueSend { + traitobj: self.traitobj(), + inner: self.0, + } + } + + fn traitobj(&mut self) -> *mut dyn wasmtime_runtime::Store { + unsafe { + std::mem::transmute::< + *mut (dyn wasmtime_runtime::Store + '_), + *mut (dyn wasmtime_runtime::Store + 'static), + >(self.0) + } + } +} + +/// TODO +pub trait AsContext { + /// TODO + type Data; + + /// TODO + fn as_context(&self) -> StoreContext<'_, Self::Data>; +} + +/// TODO +pub trait AsContextMut: AsContext { + /// TODO + fn as_context_mut(&mut self) -> StoreContextMut<'_, Self::Data>; +} + +impl AsContext for Store { + type Data = T; + + #[inline] + fn as_context(&self) -> StoreContext<'_, T> { + StoreContext(&self.inner) + } +} + +impl AsContextMut for Store { + #[inline] + fn as_context_mut(&mut self) -> StoreContextMut<'_, T> { + StoreContextMut(&mut self.inner) + } +} + +impl AsContext for StoreContext<'_, T> { + type Data = T; + + #[inline] + fn as_context(&self) -> StoreContext<'_, T> { + StoreContext(&*self.0) + } +} + +impl AsContext for StoreContextMut<'_, T> { + type Data = T; + + #[inline] + fn as_context(&self) -> StoreContext<'_, T> { + StoreContext(&*self.0) + } +} + +impl AsContextMut for StoreContextMut<'_, T> { + #[inline] + fn as_context_mut(&mut self) -> StoreContextMut<'_, T> { + StoreContextMut(&mut *self.0) + } +} + +// forward AsContext for &T +impl AsContext for &'_ T { + type Data = T::Data; + + #[inline] + fn as_context(&self) -> StoreContext<'_, T::Data> { + T::as_context(*self) + } +} + +// forward AsContext for &mut T +impl AsContext for &'_ mut T { + type Data = T::Data; + + #[inline] + fn as_context(&self) -> StoreContext<'_, T::Data> { + T::as_context(*self) + } +} + +// forward AsContextMut for &mut T +impl AsContextMut for &'_ mut T { + #[inline] + fn as_context_mut(&mut self) -> StoreContextMut<'_, T::Data> { + T::as_context_mut(*self) + } +} + +// +impl<'a, T: AsContext> From<&'a T> for StoreContext<'a, T::Data> { + fn from(t: &'a T) -> StoreContext<'a, T::Data> { + t.as_context() + } +} + +impl<'a, T: AsContext> From<&'a mut T> for StoreContext<'a, T::Data> { + fn from(t: &'a mut T) -> StoreContext<'a, T::Data> { + T::as_context(t) + } +} + +impl<'a, T: AsContextMut> From<&'a mut T> for StoreContextMut<'a, T::Data> { + fn from(t: &'a mut T) -> StoreContextMut<'a, T::Data> { + t.as_context_mut() + } +} + +/// This structure is akin to a `StoreContextMut` except that the `T` is +/// "erased" to an opaque type. +/// +/// This structure is used pervasively through wasmtime whenever the `T` isn't +/// needed (quite common!). This allows the compiler to erase generics and +/// compile more code in the wasmtime crate itself instead of monomorphizing +/// everything into consumer crates. The primary purpose of this is to help +/// compile times. +#[doc(hidden)] // this is part of `WasmTy`, but a hidden part, so hide this +pub struct StoreOpaque<'a> { + /// The actual pointer to the `StoreInner` internals. + inner: &'a mut StoreInner, + + /// A raw trait object that can be used to invoke functions with. Note that + /// this is a pointer which aliases with `inner` above, so extreme care + /// needs to be used when using this (the above `inner` cannot be actively + /// borrowed). + pub traitobj: *mut dyn wasmtime_runtime::Store, +} + +pub trait Opaque {} +impl Opaque for T {} + +// Deref impls to forward all methods on `StoreOpaque` to `StoreInner`. +impl<'a> Deref for StoreOpaque<'a> { + type Target = StoreInner; + + #[inline] + fn deref(&self) -> &Self::Target { + &*self.inner + } +} + +impl<'a> DerefMut for StoreOpaque<'a> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.inner + } +} + +pub struct StoreOpaqueSend<'a> { + /// The actual pointer to the `StoreInner` internals. + inner: &'a mut StoreInner, + pub traitobj: *mut dyn wasmtime_runtime::Store, +} + +unsafe impl Send for StoreOpaqueSend<'_> {} + +impl StoreOpaqueSend<'_> { + pub fn opaque(&mut self) -> StoreOpaque<'_> { + StoreOpaque { + inner: &mut *self.inner, + traitobj: self.traitobj, + } + } +} + +impl<'a> Deref for StoreOpaqueSend<'a> { + type Target = StoreInner; + + #[inline] + fn deref(&self) -> &Self::Target { + &*self.inner + } +} + +impl<'a> DerefMut for StoreOpaqueSend<'a> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.inner + } +} diff --git a/crates/wasmtime/src/store/data.rs b/crates/wasmtime/src/store/data.rs new file mode 100644 index 000000000000..bf8d7355aa33 --- /dev/null +++ b/crates/wasmtime/src/store/data.rs @@ -0,0 +1,255 @@ +use crate::store::StoreOpaque; +use crate::{StoreContext, StoreContextMut}; +use std::convert::TryFrom; +use std::fmt; +use std::marker; +use std::ops::Index; +use std::sync::atomic::{AtomicU64, Ordering::SeqCst}; + +// This is defined here, in a private submodule, so we can explicitly reexport +// it only as `pub(crate)`. This avoids a ton of +// crate-private-type-in-public-interface errors that aren't really too +// interesting to deal with. +#[derive(Copy, Clone)] +pub struct InstanceId(pub(super) usize); + +pub struct StoreData { + id: u64, + funcs: Vec, + tables: Vec, + globals: Vec, + instances: Vec, + memories: Vec, +} + +pub trait StoredData: Sized { + fn get<'a>(id: Stored, data: &'a StoreData) -> &'a Self; + fn insert(self, data: &mut StoreData) -> Stored; + fn assert_contained(id: Stored, data: &StoreData); +} + +macro_rules! impl_store_data { + ($($field:ident => $t:ty,)*) => ($( + impl StoredData for $t { + #[inline] + fn get<'a>(id: Stored, data: &'a StoreData) -> &'a Self { + assert!(id.store_id() == data.id, + "object used with the wrong store"); + &data.$field[id.index()] + } + + fn insert(self, data: &mut StoreData) -> Stored { + let index = data.$field.len(); + data.$field.push(self); + Stored::new(data.id, index) + } + + fn assert_contained(id: Stored, data: &StoreData) { + assert!(id.index() < data.$field.len()); + } + } + )*) +} + +impl_store_data! { + funcs => crate::func::FuncData, + tables => wasmtime_runtime::ExportTable, + globals => wasmtime_runtime::ExportGlobal, + instances => crate::instance::RuntimeInstance, + memories => wasmtime_runtime::ExportMemory, +} + +impl StoreData { + pub fn new() -> StoreData { + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + + // Currently we neither recycle ids nor do we allow overlap of ids (e.g. + // the ABA problem). We also only allocate a certain number of bits + // (controlled by INDEX_BITS below) for the id. Make sure that the id + // fits in the allocated bits (currently 40 bits). + // + // Note that this is the maximal number of `Store` instances that a + // process can make before it needs to be restarted. That means this + // needs to be pretty reasonable. At the assumption of creating 10k + // stores per second 40 bits allows that program to run for ~3.5 years. + // Hopefully programs don't run that long. + // + // If a program does indeed run that long then we rest the counter back + // to a known bad value (and it's basically impossible the counter will + // wrap back to zero inbetween this time) and then panic the current + // thread. + let id = NEXT_ID.fetch_add(1, SeqCst); + let upper_bits_used = (id >> (64 - INDEX_BITS)) != 0; + if upper_bits_used { + NEXT_ID.store(1 << (64 - INDEX_BITS), SeqCst); + panic!("store id allocator overflow"); + } + + StoreData { + id, + funcs: Vec::new(), + tables: Vec::new(), + globals: Vec::new(), + instances: Vec::new(), + memories: Vec::new(), + } + } + + pub fn insert(&mut self, data: T) -> Stored + where + T: StoredData, + { + data.insert(self) + } + + pub fn contains(&self, id: Stored) -> bool + where + T: StoredData, + { + if id.store_id() != self.id { + return false; + } + // this should be true as an invariant of our API, but double-check with + // debug assertions enabled. + if cfg!(debug_assertions) { + T::assert_contained(id, self); + } + true + } +} + +impl Index> for StoreData +where + T: StoredData, +{ + type Output = T; + + #[inline] + fn index(&self, index: Stored) -> &Self::Output { + T::get(index, self) + } +} + +// forward StoreContext => StoreData +impl Index for StoreContext<'_, T> +where + StoreData: Index, +{ + type Output = >::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + self.0.store_data.index(index) + } +} + +// forward StoreContextMut => StoreData +impl Index for StoreContextMut<'_, T> +where + StoreData: Index, +{ + type Output = >::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + self.0.store_data.index(index) + } +} + +// forward StoreOpaque => StoreData +impl Index for StoreOpaque<'_> +where + StoreData: Index, +{ + type Output = >::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + self.store_data().index(index) + } +} + +pub struct Stored { + // See documentation below on `INDEX_BITS` for how this is interpreted. + bits: u64, + _marker: marker::PhantomData T>, +} + +// This is the maximal number of bits that the index of an item within a store +// can take up. As this is set to 24 that allows for 16 million items. Note +// that this is not a limit on something like the number of functions within an +// instance, only a limit on the number of externally referenced items in a +// Store. For example this is more equivalent to exported functions of a module +// rather than functions themselves. +// +// The reason for this limitation is that we want `Stored` to fit into a +// 64-bit value (for the C API). This 64-bit value gives us limited, well, uh, +// bits, to work with. We need to pack both a "store id" as well as an index +// within the store into those 64 bits. Given that there's no implementation of +// recycling store IDs at this time it also means that the number of bits +// allocated to the store id represents the maximal number of stores that a +// process can create for its entire lifetime. +// +// These factors led to the choice of bits here for this. This can be moved +// around a bit, but the hope is that this is good enough for all practical +// users. +// +// The choice of 24 means that the limitations of wasmtime are: +// +// * 24 bits for the index, meaning 16 million items maximum. As noted above +// this is 16 million *host references* to wasm items, so this is akin to +// creating 16 million instances within one store or creating an instance +// that has 16 million exported function. If 24 bits isn't enough then we +// may need to look into compile-time options to change this perhaps. +// +// * 40 bits for the store id. This is a whole lot more bits than the index, +// but intentionally so. As the maximal number of stores for the entire +// process that's far more limiting than the number of items within a store +// (which are typically drastically lower than 16 million and/or limited via +// other means, e.g. wasm module validation, instance limits, etc). +// +// So all-in-all we try to maximize the number of store bits without placing +// too many restrictions on the number of items within a store. Using 40 +// bits practically means that if you create 10k stores a second your program +// can run for ~3.5 years. Hopefully that's enough? +// +// If we didn't need to be clever in the C API and returned structs-by-value +// instead of returning 64-bit integers then we could just change this to a +// u64/usize pair which would solve all of these problems. Hopefully, though, +// no one will ever run into these limits... +const INDEX_BITS: usize = 24; + +impl Stored { + fn new(store_id: u64, index: usize) -> Stored { + let masked_index = ((1 << INDEX_BITS) - 1) & index; + if masked_index != index { + panic!("too many items have been allocated into the store"); + } + Stored { + bits: (store_id << INDEX_BITS) | u64::try_from(masked_index).unwrap(), + _marker: marker::PhantomData, + } + } + + fn store_id(&self) -> u64 { + self.bits >> INDEX_BITS + } + + fn index(&self) -> usize { + usize::try_from(self.bits & ((1 << INDEX_BITS) - 1)).unwrap() + } +} + +impl Copy for Stored {} + +impl Clone for Stored { + fn clone(&self) -> Self { + *self + } +} + +impl fmt::Debug for Stored { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "store={}, index={}", self.store_id(), self.index()) + } +} diff --git a/crates/wasmtime/src/trampoline.rs b/crates/wasmtime/src/trampoline.rs index 5ebc436eba13..8c70163ba9cf 100644 --- a/crates/wasmtime/src/trampoline.rs +++ b/crates/wasmtime/src/trampoline.rs @@ -11,53 +11,25 @@ pub use self::func::{create_function, create_raw_function}; use self::global::create_global; use self::memory::create_memory; use self::table::create_table; -use crate::{GlobalType, MemoryType, Store, TableType, Val}; +use crate::store::{InstanceId, StoreOpaque}; +use crate::{GlobalType, MemoryType, TableType, Val}; use anyhow::Result; use std::any::Any; -use std::ops::Deref; use std::sync::Arc; use wasmtime_environ::{entity::PrimaryMap, wasm, Module}; use wasmtime_runtime::{ - Imports, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, - OnDemandInstanceAllocator, VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, - VMSharedSignatureIndex, + Imports, InstanceAllocationRequest, InstanceAllocator, OnDemandInstanceAllocator, + VMFunctionBody, VMFunctionImport, VMSharedSignatureIndex, }; -/// A wrapper around `wasmtime_runtime::InstanceHandle` which pairs it with the -/// `Store` that it's rooted within. The instance is deallocated when `Store` is -/// deallocated, so this is a safe handle in terms of memory management for the -/// `Store`. -pub struct StoreInstanceHandle { - pub store: Store, - pub handle: InstanceHandle, -} - -impl Clone for StoreInstanceHandle { - fn clone(&self) -> StoreInstanceHandle { - StoreInstanceHandle { - store: self.store.clone(), - // Note should be safe because the lifetime of the instance handle - // is tied to the `Store` which this is paired with. - handle: unsafe { self.handle.clone() }, - } - } -} - -impl Deref for StoreInstanceHandle { - type Target = InstanceHandle; - fn deref(&self) -> &InstanceHandle { - &self.handle - } -} - fn create_handle( module: Module, - store: &Store, + store: &mut StoreOpaque<'_>, finished_functions: PrimaryMap, - host_state: Box, + host_state: Box, func_imports: &[VMFunctionImport], shared_signature_id: Option, -) -> Result { +) -> Result { let mut imports = Imports::default(); imports.functions = func_imports; @@ -73,12 +45,7 @@ fn create_handle( imports, shared_signatures: shared_signature_id.into(), host_state, - interrupts: store.interrupts(), - externref_activations_table: store.externref_activations_table() - as *const VMExternRefActivationsTable - as *mut _, - module_info_lookup: Some(store.module_info_lookup()), - limiter: store.limiter().as_ref(), + store: Some(store.traitobj), }, )?; @@ -87,38 +54,38 @@ fn create_handle( } pub fn generate_global_export( - store: &Store, + store: &mut StoreOpaque<'_>, gt: &GlobalType, val: Val, -) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportGlobal)> { +) -> Result { let instance = create_global(store, gt, val)?; let idx = wasm::EntityIndex::Global(wasm::GlobalIndex::from_u32(0)); - match instance.lookup_by_declaration(&idx) { - wasmtime_runtime::Export::Global(g) => Ok((instance, g)), + match store.instance(instance).lookup_by_declaration(&idx) { + wasmtime_runtime::Export::Global(g) => Ok(g), _ => unreachable!(), } } pub fn generate_memory_export( - store: &Store, + store: &mut StoreOpaque<'_>, m: &MemoryType, -) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportMemory)> { +) -> Result { let instance = create_memory(store, m)?; let idx = wasm::EntityIndex::Memory(wasm::MemoryIndex::from_u32(0)); - match instance.lookup_by_declaration(&idx) { - wasmtime_runtime::Export::Memory(m) => Ok((instance, m)), + match store.instance(instance).lookup_by_declaration(&idx) { + wasmtime_runtime::Export::Memory(m) => Ok(m), _ => unreachable!(), } } pub fn generate_table_export( - store: &Store, + store: &mut StoreOpaque<'_>, t: &TableType, -) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportTable)> { +) -> Result { let instance = create_table(store, t)?; let idx = wasm::EntityIndex::Table(wasm::TableIndex::from_u32(0)); - match instance.lookup_by_declaration(&idx) { - wasmtime_runtime::Export::Table(t) => Ok((instance, t)), + match store.instance(instance).lookup_by_declaration(&idx) { + wasmtime_runtime::Export::Table(t) => Ok(t), _ => unreachable!(), } } diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/trampoline/func.rs index b381c561ea01..a4107558ba7f 100644 --- a/crates/wasmtime/src/trampoline/func.rs +++ b/crates/wasmtime/src/trampoline/func.rs @@ -1,6 +1,6 @@ //! Support for a calling of an imported function. -use crate::{Config, FuncType, Store, Trap}; +use crate::{Engine, FuncType, Trap}; use anyhow::Result; use std::any::Any; use std::cmp; @@ -9,7 +9,7 @@ use std::panic::{self, AssertUnwindSafe}; use std::sync::Arc; use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::isa::TargetIsa; -use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex}; +use wasmtime_environ::wasm::SignatureIndex; use wasmtime_environ::{ir, wasm, CompiledFunction, Module, ModuleType}; use wasmtime_jit::trampoline::ir::{ ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind, @@ -25,7 +25,7 @@ use wasmtime_runtime::{ }; struct TrampolineState { - func: Box Result<(), Trap>>, + func: Box Result<(), Trap> + Send + Sync>, #[allow(dead_code)] code_memory: CodeMemory, } @@ -197,20 +197,15 @@ fn make_trampoline( .expect("allocate_for_function") } -fn create_function_trampoline( - config: &Config, +pub fn create_function( ft: &FuncType, - func: Box Result<(), Trap>>, -) -> Result<( - Module, - PrimaryMap, - VMTrampoline, - TrampolineState, -)> { + func: Box Result<(), Trap> + Send + Sync>, + engine: &Engine, +) -> Result<(InstanceHandle, VMTrampoline)> { // Note that we specifically enable reference types here in our ISA because // `Func::new` is intended to be infallible, but our signature may use // reference types which requires safepoints. - let isa = config.target_isa_with_reference_types(); + let isa = engine.config().target_isa_with_reference_types(); let mut sig = blank_sig(&*isa, wasmtime_call_conv(&*isa)); sig.params.extend( @@ -223,27 +218,15 @@ fn create_function_trampoline( ); let mut fn_builder_ctx = FunctionBuilderContext::new(); - let mut module = Module::new(); - let mut finished_functions = PrimaryMap::new(); let mut code_memory = CodeMemory::new(); - // First up we manufacture a trampoline which has the ABI specified by `ft` - // and calls into `stub_fn`... - let sig_id = SignatureIndex::from_u32(u32::max_value() - 1); - module.types.push(ModuleType::Function(sig_id)); - - let func_id = module.functions.push(sig_id); - module - .exports - .insert(String::new(), wasm::EntityIndex::Function(func_id)); - - let trampoline = make_trampoline(isa.as_ref(), &mut code_memory, &mut fn_builder_ctx, &sig); - finished_functions.push(trampoline); + let wasm_trampoline = + make_trampoline(isa.as_ref(), &mut code_memory, &mut fn_builder_ctx, &sig); // ... and then we also need a trampoline with the standard "trampoline ABI" // which enters into the ABI specified by `ft`. Note that this is only used // if `Func::call` is called on an object created by `Func::new`. - let trampoline = trampoline::make_trampoline( + let host_trampoline = trampoline::make_trampoline( &*isa, &mut code_memory, &mut fn_builder_ctx, @@ -253,52 +236,22 @@ fn create_function_trampoline( code_memory.publish(isa.as_ref()); - let trampoline_state = TrampolineState { func, code_memory }; - - Ok((module, finished_functions, trampoline, trampoline_state)) -} - -pub fn create_function( - ft: &FuncType, - func: Box Result<(), Trap>>, - config: &Config, - store: Option<&Store>, -) -> Result<(InstanceHandle, VMTrampoline)> { - let (module, finished_functions, trampoline, trampoline_state) = - create_function_trampoline(config, ft, func)?; - - // If there is no store, use the default signature index which is - // guaranteed to trap if there is ever an indirect call on the function (should not happen) - let shared_signature_id = store - .map(|s| { - s.signatures() - .borrow_mut() - .register(ft.as_wasm_func_type(), trampoline) - }) - .unwrap_or(VMSharedSignatureIndex::default()); + let sig = engine.signatures().register(ft.as_wasm_func_type()); unsafe { - Ok(( - OnDemandInstanceAllocator::default().allocate(InstanceAllocationRequest { - module: Arc::new(module), - finished_functions: &finished_functions, - imports: Imports::default(), - shared_signatures: shared_signature_id.into(), - host_state: Box::new(trampoline_state), - interrupts: std::ptr::null(), - externref_activations_table: std::ptr::null_mut(), - module_info_lookup: None, - limiter: None, - })?, - trampoline, - )) + let instance = create_raw_function( + wasm_trampoline, + sig, + Box::new(TrampolineState { func, code_memory }), + )?; + Ok((instance, host_trampoline)) } } pub unsafe fn create_raw_function( func: *mut [VMFunctionBody], - host_state: Box, - shared_signature_id: VMSharedSignatureIndex, + sig: VMSharedSignatureIndex, + host_state: Box, ) -> Result { let mut module = Module::new(); let mut finished_functions = PrimaryMap::new(); @@ -316,12 +269,9 @@ pub unsafe fn create_raw_function( module: Arc::new(module), finished_functions: &finished_functions, imports: Imports::default(), - shared_signatures: shared_signature_id.into(), + shared_signatures: sig.into(), host_state, - interrupts: std::ptr::null(), - externref_activations_table: std::ptr::null_mut(), - module_info_lookup: None, - limiter: None, + store: None, })?, ) } diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs index 445272d3eb93..37eec1d9e01d 100644 --- a/crates/wasmtime/src/trampoline/global.rs +++ b/crates/wasmtime/src/trampoline/global.rs @@ -1,5 +1,6 @@ -use crate::trampoline::{create_handle, StoreInstanceHandle}; -use crate::{GlobalType, Mutability, Store, Val}; +use crate::store::{InstanceId, StoreOpaque}; +use crate::trampoline::create_handle; +use crate::{GlobalType, Mutability, Val}; use anyhow::Result; use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::{ @@ -8,7 +9,7 @@ use wasmtime_environ::{ }; use wasmtime_runtime::VMFunctionImport; -pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result { +pub fn create_global(store: &mut StoreOpaque<'_>, gt: &GlobalType, val: Val) -> Result { let mut module = Module::new(); let mut func_imports = Vec::new(); let mut externref_init = None; @@ -38,8 +39,9 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result { // Add a function import to the stub module, and then initialize // our global with a `ref.func` to grab that imported function. - let shared_sig_index = f.sig_index(); - shared_signature_id = Some(shared_sig_index); + let f = f.caller_checked_anyfunc(store); + let f = unsafe { f.as_ref() }; + shared_signature_id = Some(f.type_index); let sig_id = SignatureIndex::from_u32(u32::max_value() - 1); module.types.push(ModuleType::Function(sig_id)); let func_index = module.functions.push(sig_id); @@ -52,8 +54,6 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result Result Result unsafe { *(*g.definition).as_externref_mut() = Some(x.inner); }, @@ -87,5 +88,5 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result Result { +pub fn create_memory(store: &mut StoreOpaque<'_>, memory: &MemoryType) -> Result { let mut module = Module::new(); let memory = wasm::Memory { @@ -41,7 +41,7 @@ impl RuntimeLinearMemory for LinearMemoryProxy { self.mem.maximum() } - fn grow(&self, delta: u32) -> Option { + fn grow(&mut self, delta: u32) -> Option { self.mem.grow(delta) } diff --git a/crates/wasmtime/src/trampoline/table.rs b/crates/wasmtime/src/trampoline/table.rs index 3e70baa9acce..fad4d8203dc4 100644 --- a/crates/wasmtime/src/trampoline/table.rs +++ b/crates/wasmtime/src/trampoline/table.rs @@ -1,11 +1,11 @@ -use crate::trampoline::{create_handle, StoreInstanceHandle}; -use crate::Store; +use crate::store::{InstanceId, StoreOpaque}; +use crate::trampoline::create_handle; use crate::{TableType, ValType}; use anyhow::{bail, Result}; use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::{wasm, Module}; -pub fn create_table(store: &Store, table: &TableType) -> Result { +pub fn create_table(store: &mut StoreOpaque<'_>, table: &TableType) -> Result { let mut module = Module::new(); let table = wasm::Table { diff --git a/crates/wasmtime/src/trap.rs b/crates/wasmtime/src/trap.rs index 1d59aa564cb9..602d73eb83b9 100644 --- a/crates/wasmtime/src/trap.rs +++ b/crates/wasmtime/src/trap.rs @@ -1,4 +1,5 @@ -use crate::{FrameInfo, Store}; +use crate::module::GlobalModuleRegistry; +use crate::FrameInfo; use backtrace::Backtrace; use std::fmt; use std::sync::Arc; @@ -136,23 +137,25 @@ impl Trap { /// let trap = wasmtime::Trap::new("unexpected error"); /// assert!(trap.to_string().contains("unexpected error")); /// ``` + #[cold] // traps are exceptional, this helps move handling off the main path pub fn new>(message: I) -> Self { let reason = TrapReason::Message(message.into()); - Trap::new_with_trace(None, None, reason, Backtrace::new_unresolved()) + Trap::new_with_trace(None, reason, Backtrace::new_unresolved()) } /// Creates a new `Trap` representing an explicit program exit with a classic `i32` /// exit status value. + #[cold] // see Trap::new pub fn i32_exit(status: i32) -> Self { Trap::new_with_trace( - None, None, TrapReason::I32Exit(status), Backtrace::new_unresolved(), ) } - pub(crate) fn from_runtime(store: &Store, runtime_trap: wasmtime_runtime::Trap) -> Self { + #[cold] // see Trap::new + pub(crate) fn from_runtime(runtime_trap: wasmtime_runtime::Trap) -> Self { match runtime_trap { wasmtime_runtime::Trap::User(error) => Trap::from(error), wasmtime_runtime::Trap::Jit { @@ -160,44 +163,40 @@ impl Trap { backtrace, maybe_interrupted, } => { - let mut code = store - .modules() - .borrow() - .lookup_trap_info(pc) - .map(|info| info.trap_code) - .unwrap_or(ir::TrapCode::StackOverflow); + let mut code = GlobalModuleRegistry::with(|modules| { + modules + .lookup_trap_info(pc) + .map(|info| info.trap_code) + .unwrap_or(ir::TrapCode::StackOverflow) + }); if maybe_interrupted && code == ir::TrapCode::StackOverflow { code = ir::TrapCode::Interrupt; } - Trap::new_wasm(Some(store), Some(pc), code, backtrace) + Trap::new_wasm(Some(pc), code, backtrace) } wasmtime_runtime::Trap::Wasm { trap_code, backtrace, - } => Trap::new_wasm(Some(store), None, trap_code, backtrace), + } => Trap::new_wasm(None, trap_code, backtrace), wasmtime_runtime::Trap::OOM { backtrace } => { let reason = TrapReason::Message("out of memory".to_string()); - Trap::new_with_trace(Some(store), None, reason, backtrace) + Trap::new_with_trace(None, reason, backtrace) } } } + #[cold] // see Trap::new pub(crate) fn new_wasm( - store: Option<&Store>, trap_pc: Option, code: ir::TrapCode, backtrace: Backtrace, ) -> Self { let code = TrapCode::from_non_user(code); - Trap::new_with_trace(store, trap_pc, TrapReason::InstructionTrap(code), backtrace) + Trap::new_with_trace(trap_pc, TrapReason::InstructionTrap(code), backtrace) } /// Creates a new `Trap`. /// - /// * `store` - this is optionally provided, if available. If `None` we'll - /// look up the last store, if available, used to call wasm code on the - /// stack. - /// /// * `trap_pc` - this is the precise program counter, if available, that /// wasm trapped at. This is used when learning about the wasm stack trace /// to ensure we assign the correct source to every frame. @@ -208,52 +207,40 @@ impl Trap { /// * `native_trace` - this is a captured backtrace from when the trap /// occurred, and this will iterate over the frames to find frames that /// lie in wasm jit code. - fn new_with_trace( - store: Option<&Store>, - trap_pc: Option, - reason: TrapReason, - native_trace: Backtrace, - ) -> Self { + fn new_with_trace(trap_pc: Option, reason: TrapReason, native_trace: Backtrace) -> Self { let mut wasm_trace = Vec::new(); let mut hint_wasm_backtrace_details_env = false; - wasmtime_runtime::with_last_info(|last| { - // If the `store` passed in is `None` then we look at the `last` - // store configured to call wasm, and if that's a `Store` we use - // that. If that all fails then we just don't generate any - // `wasm_trace` information. - if let Some(store) = store.or_else(|| last?.downcast_ref()) { - for frame in native_trace.frames() { - let pc = frame.ip() as usize; - if pc == 0 { - continue; - } - // Note that we need to be careful about the pc we pass in - // here to lookup frame information. This program counter is - // used to translate back to an original source location in - // the origin wasm module. If this pc is the exact pc that - // the trap happened at, then we look up that pc precisely. - // Otherwise backtrace information typically points at the - // pc *after* the call instruction (because otherwise it's - // likely a call instruction on the stack). In that case we - // want to lookup information for the previous instruction - // (the call instruction) so we subtract one as the lookup. - let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 }; - if let Some((info, has_unparsed_debuginfo)) = - store.modules().borrow().lookup_frame_info(pc_to_lookup) - { - wasm_trace.push(info); - - // If this frame has unparsed debug information and the - // store's configuration indicates that we were - // respecting the environment variable of whether to - // do this then we will print out a helpful note in - // `Display` to indicate that more detailed information - // in a trap may be available. - if has_unparsed_debuginfo - && store.engine().config().wasm_backtrace_details_env_used - { - hint_wasm_backtrace_details_env = true; - } + + GlobalModuleRegistry::with(|registry| { + for frame in native_trace.frames() { + let pc = frame.ip() as usize; + if pc == 0 { + continue; + } + // Note that we need to be careful about the pc we pass in + // here to lookup frame information. This program counter is + // used to translate back to an original source location in + // the origin wasm module. If this pc is the exact pc that + // the trap happened at, then we look up that pc precisely. + // Otherwise backtrace information typically points at the + // pc *after* the call instruction (because otherwise it's + // likely a call instruction on the stack). In that case we + // want to lookup information for the previous instruction + // (the call instruction) so we subtract one as the lookup. + let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 }; + if let Some((info, has_unparsed_debuginfo, wasm_backtrace_details_env_used)) = + registry.lookup_frame_info(pc_to_lookup) + { + wasm_trace.push(info); + + // If this frame has unparsed debug information and the + // store's configuration indicates that we were + // respecting the environment variable of whether to + // do this then we will print out a helpful note in + // `Display` to indicate that more detailed information + // in a trap may be available. + if has_unparsed_debuginfo && wasm_backtrace_details_env_used { + hint_wasm_backtrace_details_env = true; } } } @@ -388,7 +375,7 @@ impl From> for Trap { trap.clone() } else { let reason = TrapReason::Error(e.into()); - Trap::new_with_trace(None, None, reason, Backtrace::new_unresolved()) + Trap::new_with_trace(None, reason, Backtrace::new_unresolved()) } } } diff --git a/crates/wasmtime/src/types/matching.rs b/crates/wasmtime/src/types/matching.rs index 7405194c12a9..1f5e1a5d0b0a 100644 --- a/crates/wasmtime/src/types/matching.rs +++ b/crates/wasmtime/src/types/matching.rs @@ -1,4 +1,5 @@ -use crate::{signatures::SignatureCollection, Extern, Store}; +use crate::store::StoreData; +use crate::{signatures::SignatureCollection, Engine, Extern}; use anyhow::{bail, Context, Result}; use wasmtime_environ::wasm::{ EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table, @@ -8,12 +9,13 @@ use wasmtime_jit::TypeTables; pub struct MatchCx<'a> { pub signatures: &'a SignatureCollection, pub types: &'a TypeTables, - pub store: &'a Store, + pub store_data: &'a StoreData, + pub engine: &'a Engine, } impl MatchCx<'_> { pub fn global(&self, expected: &Global, actual: &crate::Global) -> Result<()> { - self.global_ty(expected, actual.wasmtime_ty()) + self.global_ty(expected, actual.wasmtime_ty(self.store_data)) } fn global_ty(&self, expected: &Global, actual: &Global) -> Result<()> { @@ -28,7 +30,7 @@ impl MatchCx<'_> { } pub fn table(&self, expected: &Table, actual: &crate::Table) -> Result<()> { - self.table_ty(expected, actual.wasmtime_ty()) + self.table_ty(expected, actual.wasmtime_ty(self.store_data)) } fn table_ty(&self, expected: &Table, actual: &Table) -> Result<()> { @@ -50,7 +52,7 @@ impl MatchCx<'_> { } pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> Result<()> { - self.memory_ty(expected, actual.wasmtime_ty()) + self.memory_ty(expected, actual.wasmtime_ty(self.store_data)) } fn memory_ty(&self, expected: &Memory, actual: &Memory) -> Result<()> { @@ -72,7 +74,7 @@ impl MatchCx<'_> { pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> Result<()> { let matches = match self.signatures.shared_signature(expected) { - Some(idx) => actual.sig_index() == idx, + Some(idx) => actual.sig_index(self.store_data) == idx, // If our expected signature isn't registered, then there's no way // that `actual` can match it. None => false, @@ -85,11 +87,11 @@ impl MatchCx<'_> { } pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> Result<()> { + let items = actual.items(self.store_data); for (name, expected) in self.types.instance_signatures[expected].exports.iter() { - match actual.items.get(name) { + match items.get(name) { Some(item) => { - let item = unsafe { Extern::from_wasmtime_export(item, self.store) }; - self.extern_(expected, &item) + self.extern_(expected, item) .with_context(|| format!("instance export {:?} incompatible", name))?; } None => bail!("instance type missing export {:?}", name), @@ -104,7 +106,7 @@ impl MatchCx<'_> { // This should only ever be invoked with module linking, and this is an // early check that our `field` assertion below should always work as // well. - assert!(self.store.engine().config().features.module_linking); + assert!(self.engine.config().features.module_linking); let expected_sig = &self.types.module_signatures[expected]; let module = actual.compiled_module().module(); @@ -149,7 +151,8 @@ impl MatchCx<'_> { MatchCx { signatures: actual_signatures, types: actual_types, - store: self.store, + store_data: self.store_data, + engine: self.engine, } .extern_ty_matches(&actual_ty, expected_ty, self.signatures, self.types) .with_context(|| format!("module import {:?} incompatible", name))?; diff --git a/crates/wasmtime/src/unix.rs b/crates/wasmtime/src/unix.rs index 25969be2c678..40c2a57cdda3 100644 --- a/crates/wasmtime/src/unix.rs +++ b/crates/wasmtime/src/unix.rs @@ -9,23 +9,31 @@ //! throughout the `wasmtime` crate with extra functionality that's only //! available on Unix. -use crate::Store; +use crate::{AsContextMut, Store}; /// Extensions for the [`Store`] type only available on Unix. pub trait StoreExt { // TODO: needs more docs? /// The signal handler must be /// [async-signal-safe](http://man7.org/linux/man-pages/man7/signal-safety.7.html). - unsafe fn set_signal_handler(&self, handler: H) + unsafe fn set_signal_handler(&mut self, handler: H) where - H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool; + H: 'static + + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + + Send + + Sync; } -impl StoreExt for Store { - unsafe fn set_signal_handler(&self, handler: H) +impl StoreExt for Store { + unsafe fn set_signal_handler(&mut self, handler: H) where - H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool, + H: 'static + + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + + Send + + Sync, { - self.set_signal_handler(Some(Box::new(handler))); + self.as_context_mut() + .opaque() + .set_signal_handler(Some(Box::new(handler))); } } diff --git a/crates/wasmtime/src/values.rs b/crates/wasmtime/src/values.rs index 7d6adbbedc78..c2d347d2e3b8 100644 --- a/crates/wasmtime/src/values.rs +++ b/crates/wasmtime/src/values.rs @@ -1,5 +1,6 @@ use crate::r#ref::ExternRef; -use crate::{Func, Store, ValType}; +use crate::store::StoreOpaque; +use crate::{Func, ValType}; use anyhow::{bail, Result}; use std::ptr; use wasmtime_runtime::{self as runtime, VMExternRef}; @@ -86,7 +87,7 @@ impl Val { } } - pub(crate) unsafe fn write_value_to(self, store: &Store, p: *mut u128) { + pub(crate) unsafe fn write_value_to(self, store: &mut StoreOpaque, p: *mut u128) { match self { Val::I32(i) => ptr::write(p as *mut i32, i), Val::I64(i) => ptr::write(p as *mut i64, i), @@ -96,15 +97,13 @@ impl Val { Val::ExternRef(None) => ptr::write(p, 0), Val::ExternRef(Some(x)) => { let externref_ptr = x.inner.as_raw(); - store - .externref_activations_table() - .insert_with_gc(x.inner, store.module_info_lookup()); + store.insert_vmexternref(x.inner); ptr::write(p as *mut *mut u8, externref_ptr) } Val::FuncRef(f) => ptr::write( p as *mut *mut runtime::VMCallerCheckedAnyfunc, if let Some(f) = f { - f.caller_checked_anyfunc().as_ptr() + f.caller_checked_anyfunc(store).as_ptr() } else { ptr::null_mut() }, @@ -112,7 +111,11 @@ impl Val { } } - pub(crate) unsafe fn read_value_from(store: &Store, p: *const u128, ty: ValType) -> Val { + pub(crate) unsafe fn read_value_from( + store: &mut StoreOpaque, + p: *const u128, + ty: ValType, + ) -> Val { match ty { ValType::I32 => Val::I32(ptr::read(p as *const i32)), ValType::I64 => Val::I64(ptr::read(p as *const i64)), @@ -174,21 +177,36 @@ impl Val { self.externref().expect("expected externref") } - pub(crate) fn into_table_element(self) -> Result { - match self { - Val::FuncRef(Some(f)) => Ok(runtime::TableElement::FuncRef( - f.caller_checked_anyfunc().as_ptr(), - )), - Val::FuncRef(None) => Ok(runtime::TableElement::FuncRef(ptr::null_mut())), - Val::ExternRef(Some(x)) => Ok(runtime::TableElement::ExternRef(Some(x.inner))), - Val::ExternRef(None) => Ok(runtime::TableElement::ExternRef(None)), + pub(crate) fn into_table_element( + self, + store: &mut StoreOpaque<'_>, + ty: ValType, + ) -> Result { + match (self, ty) { + (Val::FuncRef(Some(f)), ValType::FuncRef) => { + if !f.comes_from_same_store(store) { + bail!("cross-`Store` values are not supported in tables"); + } + Ok(runtime::TableElement::FuncRef( + f.caller_checked_anyfunc(store).as_ptr(), + )) + } + (Val::FuncRef(None), ValType::FuncRef) => { + Ok(runtime::TableElement::FuncRef(ptr::null_mut())) + } + (Val::ExternRef(Some(x)), ValType::ExternRef) => { + Ok(runtime::TableElement::ExternRef(Some(x.inner))) + } + (Val::ExternRef(None), ValType::ExternRef) => { + Ok(runtime::TableElement::ExternRef(None)) + } _ => bail!("value does not match table element type"), } } - pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool { + pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque<'_>) -> bool { match self { - Val::FuncRef(Some(f)) => Store::same(store, f.store()), + Val::FuncRef(Some(f)) => f.comes_from_same_store(store), Val::FuncRef(None) => true, // Integers, floats, vectors, and `externref`s have no association @@ -254,21 +272,21 @@ impl From for Val { pub(crate) fn into_checked_anyfunc( val: Val, - store: &Store, + store: &mut StoreOpaque, ) -> Result<*mut wasmtime_runtime::VMCallerCheckedAnyfunc> { if !val.comes_from_same_store(store) { bail!("cross-`Store` values are not supported"); } Ok(match val { Val::FuncRef(None) => ptr::null_mut(), - Val::FuncRef(Some(f)) => f.caller_checked_anyfunc().as_ptr(), + Val::FuncRef(Some(f)) => f.caller_checked_anyfunc(store).as_ptr(), _ => bail!("val is not funcref"), }) } pub(crate) unsafe fn from_checked_anyfunc( anyfunc: *mut wasmtime_runtime::VMCallerCheckedAnyfunc, - store: &Store, + store: &mut StoreOpaque, ) -> Val { Val::FuncRef(Func::from_caller_checked_anyfunc(store, anyfunc)) } diff --git a/crates/wasmtime/src/windows.rs b/crates/wasmtime/src/windows.rs index 77d3a513ab24..c8f534b6915a 100644 --- a/crates/wasmtime/src/windows.rs +++ b/crates/wasmtime/src/windows.rs @@ -9,23 +9,25 @@ //! throughout the `wasmtime` crate with extra functionality that's only //! available on Windows. -use crate::Store; +use crate::{AsContextMut, Store}; /// Extensions for the [`Store`] type only available on Windows. pub trait StoreExt { /// Configures a custom signal handler to execute. /// /// TODO: needs more documentation. - unsafe fn set_signal_handler(&self, handler: H) + unsafe fn set_signal_handler(&mut self, handler: H) where - H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool; + H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + Send + Sync; } -impl StoreExt for Store { - unsafe fn set_signal_handler(&self, handler: H) +impl StoreExt for Store { + unsafe fn set_signal_handler(&mut self, handler: H) where - H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool, + H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + Send + Sync, { - self.set_signal_handler(Some(Box::new(handler))); + self.as_context_mut() + .opaque() + .set_signal_handler(Some(Box::new(handler))); } } diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index cd8b7bc9d1aa..474169da8bd4 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -3,43 +3,46 @@ use wasmtime::*; /// Return an instance implementing the "spectest" interface used in the /// spec testsuite. -pub fn link_spectest(linker: &mut Linker) -> Result<()> { - linker.func("spectest", "print", || {})?; - linker.func("spectest", "print_i32", |val: i32| println!("{}: i32", val))?; - linker.func("spectest", "print_i64", |val: i64| println!("{}: i64", val))?; - linker.func("spectest", "print_f32", |val: f32| println!("{}: f32", val))?; - linker.func("spectest", "print_f64", |val: f64| println!("{}: f64", val))?; - linker.func("spectest", "print_i32_f32", |i: i32, f: f32| { +pub fn link_spectest(linker: &mut Linker<()>, store: &mut Store<()>) -> Result<()> { + linker.func_wrap("spectest", "print", || {})?; + linker.func_wrap("spectest", "print_i32", |val: i32| println!("{}: i32", val))?; + linker.func_wrap("spectest", "print_i64", |val: i64| println!("{}: i64", val))?; + linker.func_wrap("spectest", "print_f32", |val: f32| println!("{}: f32", val))?; + linker.func_wrap("spectest", "print_f64", |val: f64| println!("{}: f64", val))?; + linker.func_wrap("spectest", "print_i32_f32", |i: i32, f: f32| { println!("{}: i32", i); println!("{}: f32", f); })?; - linker.func("spectest", "print_f64_f64", |f1: f64, f2: f64| { + linker.func_wrap("spectest", "print_f64_f64", |f1: f64, f2: f64| { println!("{}: f64", f1); println!("{}: f64", f2); })?; + // TODO: this is a bummer + let mut store = store; + let ty = GlobalType::new(ValType::I32, Mutability::Const); - let g = Global::new(linker.store(), ty, Val::I32(666))?; + let g = Global::new(&mut store, ty, Val::I32(666))?; linker.define("spectest", "global_i32", g)?; let ty = GlobalType::new(ValType::I64, Mutability::Const); - let g = Global::new(linker.store(), ty, Val::I64(666))?; + let g = Global::new(&mut store, ty, Val::I64(666))?; linker.define("spectest", "global_i64", g)?; let ty = GlobalType::new(ValType::F32, Mutability::Const); - let g = Global::new(linker.store(), ty, Val::F32(0x4426_8000))?; + let g = Global::new(&mut store, ty, Val::F32(0x4426_8000))?; linker.define("spectest", "global_f32", g)?; let ty = GlobalType::new(ValType::F64, Mutability::Const); - let g = Global::new(linker.store(), ty, Val::F64(0x4084_d000_0000_0000))?; + let g = Global::new(&mut store, ty, Val::F64(0x4084_d000_0000_0000))?; linker.define("spectest", "global_f64", g)?; let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20))); - let table = Table::new(linker.store(), ty, Val::FuncRef(None))?; + let table = Table::new(&mut store, ty, Val::FuncRef(None))?; linker.define("spectest", "table", table)?; let ty = MemoryType::new(Limits::new(1, Some(2))); - let memory = Memory::new(linker.store(), ty)?; + let memory = Memory::new(&mut store, ty)?; linker.define("spectest", "memory", memory)?; Ok(()) diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index e18f5e543abd..92efe37377cd 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -36,11 +36,8 @@ pub struct WastContext { /// Wast files have a concept of a "current" module, which is the most /// recently defined. current: Option, - // FIXME(#1479) this is only needed to retain correct trap information after - // we've dropped previous `Instance` values. - modules: Vec, - linker: Linker, - store: Store, + linker: Linker<()>, + store: Store<()>, } enum Outcome> { @@ -59,36 +56,36 @@ impl Outcome { impl WastContext { /// Construct a new instance of `WastContext`. - pub fn new(store: Store) -> Self { + pub fn new(store: Store<()>) -> Self { // Spec tests will redefine the same module/name sometimes, so we need // to allow shadowing in the linker which picks the most recent // definition as what to link when linking. - let mut linker = Linker::new(&store); + let mut linker = Linker::new(store.engine()); linker.allow_shadowing(true); Self { current: None, linker, store, - modules: Vec::new(), } } - fn get_export(&self, module: Option<&str>, name: &str) -> Result { + fn get_export(&mut self, module: Option<&str>, name: &str) -> Result { match module { - Some(module) => self.linker.get_one_by_name(module, Some(name)), + Some(module) => self + .linker + .get_one_by_name(&mut self.store, module, Some(name)), None => self .current .as_ref() .ok_or_else(|| anyhow!("no previous instance found"))? - .get_export(name) + .get_export(&mut self.store, name) .ok_or_else(|| anyhow!("no item named `{}` found", name)), } } fn instantiate(&mut self, module: &[u8]) -> Result> { let module = Module::new(self.store.engine(), module)?; - self.modules.push(module.clone()); - let instance = match self.linker.instantiate(&module) { + let instance = match self.linker.instantiate(&mut self.store, &module) { Ok(i) => i, Err(e) => return e.downcast::().map(Outcome::Trap), }; @@ -97,7 +94,7 @@ impl WastContext { /// Register "spectest" which is used by the spec testsuite. pub fn register_spectest(&mut self) -> Result<()> { - link_spectest(&mut self.linker)?; + link_spectest(&mut self.linker, &mut self.store)?; Ok(()) } @@ -133,7 +130,7 @@ impl WastContext { Outcome::Trap(e) => return Err(e).context("instantiation failed"), }; if let Some(name) = instance_name { - self.linker.instance(name, &instance)?; + self.linker.instance(&mut self.store, name, instance)?; } self.current = Some(instance); Ok(()) @@ -144,11 +141,11 @@ impl WastContext { match name { Some(name) => self.linker.alias(name, as_name), None => { - let current = self + let current = *self .current .as_ref() .ok_or(anyhow!("no previous instance"))?; - self.linker.instance(as_name, current)?; + self.linker.instance(&mut self.store, as_name, current)?; Ok(()) } } @@ -165,7 +162,7 @@ impl WastContext { .get_export(instance_name, field)? .into_func() .ok_or_else(|| anyhow!("no function named `{}`", field))?; - Ok(match func.call(args) { + Ok(match func.call(&mut self.store, args) { Ok(result) => Outcome::Ok(result.into()), Err(e) => Outcome::Trap(e.downcast()?), }) @@ -177,7 +174,7 @@ impl WastContext { .get_export(instance_name, field)? .into_global() .ok_or_else(|| anyhow!("no global named `{}`", field))?; - Ok(Outcome::Ok(vec![global.get()])) + Ok(Outcome::Ok(vec![global.get(&mut self.store)])) } fn assert_return(&self, result: Outcome, results: &[wast::AssertExpression]) -> Result<()> { @@ -233,6 +230,8 @@ impl WastContext { for directive in ast.directives { let sp = directive.span(); + let (line, col) = sp.linecol_in(wast); + println!("run directive on {}:{}:{}", filename, line + 1, col); self.run_directive(directive, &adjust_wast) .with_context(|| { let (line, col) = sp.linecol_in(wast); diff --git a/crates/wiggle/borrow/src/lib.rs b/crates/wiggle/borrow/src/lib.rs index 35266b607526..7abcf5abbe0f 100644 --- a/crates/wiggle/borrow/src/lib.rs +++ b/crates/wiggle/borrow/src/lib.rs @@ -1,11 +1,12 @@ -use std::cell::RefCell; use std::collections::HashMap; +use std::sync::Mutex; use wiggle::{BorrowHandle, GuestError, Region}; pub struct BorrowChecker { /// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking /// overlap, the method calls on this member will be confusing. - bc: RefCell, + // TODO + bc: Mutex, } impl BorrowChecker { @@ -19,7 +20,7 @@ impl BorrowChecker { /// shared or mutable borrow. pub fn new() -> Self { BorrowChecker { - bc: RefCell::new(InnerBorrowChecker::new()), + bc: Mutex::new(InnerBorrowChecker::new()), } } /// Indicates whether any outstanding shared or mutable borrows are known @@ -27,25 +28,25 @@ impl BorrowChecker { /// to be safe to recursively call into a WebAssembly module, or to /// manipulate the WebAssembly memory by any other means. pub fn has_outstanding_borrows(&self) -> bool { - self.bc.borrow().has_outstanding_borrows() + self.bc.lock().unwrap().has_outstanding_borrows() } pub fn shared_borrow(&self, r: Region) -> Result { - self.bc.borrow_mut().shared_borrow(r) + self.bc.lock().unwrap().shared_borrow(r) } pub fn mut_borrow(&self, r: Region) -> Result { - self.bc.borrow_mut().mut_borrow(r) + self.bc.lock().unwrap().mut_borrow(r) } pub fn shared_unborrow(&self, h: BorrowHandle) { - self.bc.borrow_mut().shared_unborrow(h) + self.bc.lock().unwrap().shared_unborrow(h) } pub fn mut_unborrow(&self, h: BorrowHandle) { - self.bc.borrow_mut().mut_unborrow(h) + self.bc.lock().unwrap().mut_unborrow(h) } pub fn is_shared_borrowed(&self, r: Region) -> bool { - self.bc.borrow().is_shared_borrowed(r) + self.bc.lock().unwrap().is_shared_borrowed(r) } pub fn is_mut_borrowed(&self, r: Region) -> bool { - self.bc.borrow().is_mut_borrowed(r) + self.bc.lock().unwrap().is_mut_borrowed(r) } } diff --git a/crates/wiggle/generate/src/funcs.rs b/crates/wiggle/generate/src/funcs.rs index 4fcdcba474ec..33998ee57bd4 100644 --- a/crates/wiggle/generate/src/funcs.rs +++ b/crates/wiggle/generate/src/funcs.rs @@ -63,7 +63,7 @@ pub fn define_func( quote! { #[allow(unreachable_code)] // deals with warnings in noreturn functions pub #asyncness fn #ident( - ctx: &(impl #(#required_impls)+*), + ctx: &mut (impl #(#required_impls)+*), memory: &dyn #rt::GuestMemory, #(#abi_params),* ) -> Result<#abi_ret, #rt::Trap> { diff --git a/crates/wiggle/generate/src/lib.rs b/crates/wiggle/generate/src/lib.rs index cecd610b505b..66ecf4baccdc 100644 --- a/crates/wiggle/generate/src/lib.rs +++ b/crates/wiggle/generate/src/lib.rs @@ -42,7 +42,7 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings) let abi_typename = names.type_ref(&errtype.abi_type(), anon_lifetime()); let user_typename = errtype.typename(); let methodname = names.user_error_conversion_method(&errtype); - quote!(fn #methodname(&self, e: super::#user_typename) -> Result<#abi_typename, #rt::Trap>;) + quote!(fn #methodname(&mut self, e: super::#user_typename) -> Result<#abi_typename, #rt::Trap>;) }); let user_error_conversion = quote! { pub trait UserErrorConversion { diff --git a/crates/wiggle/generate/src/module_trait.rs b/crates/wiggle/generate/src/module_trait.rs index 4ccd1eceffc8..24995600b21e 100644 --- a/crates/wiggle/generate/src/module_trait.rs +++ b/crates/wiggle/generate/src/module_trait.rs @@ -82,9 +82,9 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings }; if is_anonymous { - quote!(#asyncness fn #funcname(&self, #(#args),*) -> #result; ) + quote!(#asyncness fn #funcname(&mut self, #(#args),*) -> #result; ) } else { - quote!(#asyncness fn #funcname<#lifetime>(&self, #(#args),*) -> #result;) + quote!(#asyncness fn #funcname<#lifetime>(&mut self, #(#args),*) -> #result;) } }); diff --git a/crates/wiggle/macro/src/lib.rs b/crates/wiggle/macro/src/lib.rs index 44e01eb8ba07..a386ea2626d4 100644 --- a/crates/wiggle/macro/src/lib.rs +++ b/crates/wiggle/macro/src/lib.rs @@ -95,11 +95,11 @@ use syn::parse_macro_input; /// /// whereas the witx-defined types like `Excuse` and `Errno` come /// /// from the `pub mod types` emitted by the `wiggle::from_witx!` /// /// invocation above. -/// fn int_float_args(&self, _int: u32, _floats: &GuestPtr<[f32]>) +/// fn int_float_args(&mut self, _int: u32, _floats: &GuestPtr<[f32]>) /// -> Result<(), YourRichError> { /// unimplemented!() /// } -/// async fn double_int_return_float(&self, int: u32) +/// async fn double_int_return_float(&mut self, int: u32) /// -> Result { /// Ok(int.checked_mul(2).ok_or(YourRichError::Overflow)? as f32) /// } @@ -121,7 +121,7 @@ use syn::parse_macro_input; /// /// to terminate WebAssembly execution by trapping. /// /// impl types::UserErrorConversion for YourCtxType { -/// fn errno_from_your_rich_error(&self, e: YourRichError) +/// fn errno_from_your_rich_error(&mut self, e: YourRichError) /// -> Result /// { /// println!("Rich error: {:?}", e); @@ -163,7 +163,7 @@ pub fn async_trait(attr: TokenStream, item: TokenStream) -> TokenStream { let _ = parse_macro_input!(attr as syn::parse::Nothing); let item = proc_macro2::TokenStream::from(item); TokenStream::from(quote! { - #[wiggle::async_trait_crate::async_trait(?Send)] + #[wiggle::async_trait_crate::async_trait] #item }) } diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index f05a65cf5e4b..a6e4271325e5 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -1,7 +1,4 @@ -use std::cell::Cell; use std::fmt; -use std::marker; -use std::rc::Rc; use std::slice; use std::str; use std::sync::Arc; @@ -79,7 +76,7 @@ pub mod async_trait_crate { /// the contents of a WebAssembly memory, all `GuestSlice`s and `GuestStr`s /// for the memory must be dropped, at which point /// `GuestMemory::has_outstanding_borrows()` will return `false`. -pub unsafe trait GuestMemory { +pub unsafe trait GuestMemory: Send + Sync { /// Returns the base allocation of this guest memory, located in host /// memory. /// @@ -275,33 +272,6 @@ unsafe impl GuestMemory for Box { } } -unsafe impl GuestMemory for Rc { - fn base(&self) -> (*mut u8, u32) { - T::base(self) - } - fn has_outstanding_borrows(&self) -> bool { - T::has_outstanding_borrows(self) - } - fn is_mut_borrowed(&self, r: Region) -> bool { - T::is_mut_borrowed(self, r) - } - fn is_shared_borrowed(&self, r: Region) -> bool { - T::is_shared_borrowed(self, r) - } - fn mut_borrow(&self, r: Region) -> Result { - T::mut_borrow(self, r) - } - fn shared_borrow(&self, r: Region) -> Result { - T::shared_borrow(self, r) - } - fn mut_unborrow(&self, h: BorrowHandle) { - T::mut_unborrow(self, h) - } - fn shared_unborrow(&self, h: BorrowHandle) { - T::shared_unborrow(self, h) - } -} - unsafe impl GuestMemory for Arc { fn base(&self) -> (*mut u8, u32) { T::base(self) @@ -382,7 +352,6 @@ unsafe impl GuestMemory for Arc { pub struct GuestPtr<'a, T: ?Sized + Pointee> { mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer, - _marker: marker::PhantomData<&'a Cell>, } impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { @@ -392,11 +361,7 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { /// value is a `u32` offset into guest memory. For slices and strings, /// `pointer` is a `(u32, u32)` offset/length pair. pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'a, T> { - GuestPtr { - mem, - pointer, - _marker: marker::PhantomData, - } + GuestPtr { mem, pointer } } /// Returns the offset of this pointer in guest memory. @@ -630,7 +595,7 @@ impl<'a, T> GuestPtr<'a, [T]> { /// of this guest pointer is not equal to the length of the slice provided. pub fn copy_from_slice(&self, slice: &[T]) -> Result<(), GuestError> where - T: GuestTypeTransparent<'a> + Copy, + T: GuestTypeTransparent<'a> + Copy + 'a, { // bounds check ... let mut self_slice = self.as_slice_mut()?; diff --git a/crates/wiggle/test-helpers/src/lib.rs b/crates/wiggle/test-helpers/src/lib.rs index fa36bee5de85..ff2b2eda0d42 100644 --- a/crates/wiggle/test-helpers/src/lib.rs +++ b/crates/wiggle/test-helpers/src/lib.rs @@ -49,6 +49,9 @@ struct HostBuffer { cell: UnsafeCell<[u8; 4096]>, } +unsafe impl Send for HostBuffer {} +unsafe impl Sync for HostBuffer {} + pub struct HostMemory { buffer: HostBuffer, bc: BorrowChecker, diff --git a/crates/wiggle/tests/atoms.rs b/crates/wiggle/tests/atoms.rs index 8cc2acaa4092..04ab6f5029d7 100644 --- a/crates/wiggle/tests/atoms.rs +++ b/crates/wiggle/tests/atoms.rs @@ -9,11 +9,14 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> atoms::Atoms for WasiCtx<'a> { - fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } - fn double_int_return_float(&self, an_int: u32) -> Result { + fn double_int_return_float( + &mut self, + an_int: u32, + ) -> Result { Ok((an_int as f32) * 2.0) } } @@ -28,10 +31,10 @@ struct IntFloatExercise { impl IntFloatExercise { pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let e = atoms::int_float_args(&ctx, &host_memory, self.an_int as i32, self.an_float); + let e = atoms::int_float_args(&mut ctx, &host_memory, self.an_int as i32, self.an_float); assert_eq!(e, Ok(types::Errno::Ok as i32), "int_float_args error"); } @@ -57,11 +60,11 @@ struct DoubleIntExercise { impl DoubleIntExercise { pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let e = atoms::double_int_return_float( - &ctx, + &mut ctx, &host_memory, self.input as i32, self.return_loc.ptr as i32, diff --git a/crates/wiggle/tests/atoms_async.rs b/crates/wiggle/tests/atoms_async.rs index 7d41d3a273e9..bfb7f2dadd73 100644 --- a/crates/wiggle/tests/atoms_async.rs +++ b/crates/wiggle/tests/atoms_async.rs @@ -14,12 +14,12 @@ impl_errno!(types::Errno); #[wiggle::async_trait] impl<'a> atoms::Atoms for WasiCtx<'a> { - async fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + async fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } async fn double_int_return_float( - &self, + &mut self, an_int: u32, ) -> Result { Ok((an_int as f32) * 2.0) @@ -36,11 +36,11 @@ struct IntFloatExercise { impl IntFloatExercise { pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let e = run(atoms::int_float_args( - &ctx, + &mut ctx, &host_memory, self.an_int as i32, self.an_float, @@ -70,11 +70,11 @@ struct DoubleIntExercise { impl DoubleIntExercise { pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let e = run(atoms::double_int_return_float( - &ctx, + &mut ctx, &host_memory, self.input as i32, self.return_loc.ptr as i32, diff --git a/crates/wiggle/tests/errors.rs b/crates/wiggle/tests/errors.rs index 98cd30edf71e..41cf9fd14f85 100644 --- a/crates/wiggle/tests/errors.rs +++ b/crates/wiggle/tests/errors.rs @@ -31,7 +31,7 @@ mod convert_just_errno { /// When the `errors` mapping in witx is non-empty, we need to impl the /// types::UserErrorConversion trait that wiggle generates from that mapping. impl<'a> types::UserErrorConversion for WasiCtx<'a> { - fn errno_from_rich_error(&self, e: RichError) -> Result { + fn errno_from_rich_error(&mut self, e: RichError) -> Result { // WasiCtx can collect a Vec log so we can test this. We're // logging the Display impl that `thiserror::Error` provides us. self.log.borrow_mut().push(e.to_string()); @@ -44,7 +44,7 @@ mod convert_just_errno { } impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> { - fn foo(&self, strike: u32) -> Result<(), RichError> { + fn foo(&mut self, strike: u32) -> Result<(), RichError> { // We use the argument to this function to exercise all of the // possible error cases we could hit here match strike { @@ -57,12 +57,12 @@ mod convert_just_errno { #[test] fn one_error_conversion_test() { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Exercise each of the branches in `foo`. // Start with the success case: - let r0 = one_error_conversion::foo(&ctx, &host_memory, 0); + let r0 = one_error_conversion::foo(&mut ctx, &host_memory, 0); assert_eq!( r0, Ok(types::Errno::Ok as i32), @@ -71,7 +71,7 @@ mod convert_just_errno { assert!(ctx.log.borrow().is_empty(), "No error log for strike=0"); // First error case: - let r1 = one_error_conversion::foo(&ctx, &host_memory, 1); + let r1 = one_error_conversion::foo(&mut ctx, &host_memory, 1); assert_eq!( r1, Ok(types::Errno::PicketLine as i32), @@ -84,7 +84,7 @@ mod convert_just_errno { ); // Second error case: - let r2 = one_error_conversion::foo(&ctx, &host_memory, 2); + let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2); assert_eq!( r2, Ok(types::Errno::InvalidArg as i32), @@ -140,11 +140,11 @@ mod convert_multiple_error_types { // each member of the `errors` mapping. // Bodies elided. impl<'a> types::UserErrorConversion for WasiCtx<'a> { - fn errno_from_rich_error(&self, _e: RichError) -> Result { + fn errno_from_rich_error(&mut self, _e: RichError) -> Result { unimplemented!() } fn errno2_from_another_rich_error( - &self, + &mut self, _e: AnotherRichError, ) -> Result { unimplemented!() @@ -153,13 +153,13 @@ mod convert_multiple_error_types { // And here's the witx module trait impl, bodies elided impl<'a> two_error_conversions::TwoErrorConversions for WasiCtx<'a> { - fn foo(&self, _: u32) -> Result<(), RichError> { + fn foo(&mut self, _: u32) -> Result<(), RichError> { unimplemented!() } - fn bar(&self, _: u32) -> Result<(), AnotherRichError> { + fn bar(&mut self, _: u32) -> Result<(), AnotherRichError> { unimplemented!() } - fn baz(&self, _: u32) -> wiggle::Trap { + fn baz(&mut self, _: u32) -> wiggle::Trap { unimplemented!() } } diff --git a/crates/wiggle/tests/flags.rs b/crates/wiggle/tests/flags.rs index e29cfdeab752..56c52c4277d4 100644 --- a/crates/wiggle/tests/flags.rs +++ b/crates/wiggle/tests/flags.rs @@ -11,7 +11,7 @@ impl_errno!(types::Errno); impl<'a> flags::Flags for WasiCtx<'a> { fn configure_car( - &self, + &mut self, old_config: types::CarConfig, other_config_ptr: &GuestPtr, ) -> Result { @@ -62,7 +62,7 @@ impl ConfigureCarExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Populate input ptr @@ -72,7 +72,7 @@ impl ConfigureCarExercise { .expect("deref ptr mut to CarConfig"); let res = flags::configure_car( - &ctx, + &mut ctx, &host_memory, self.old_config.bits() as i32, self.other_config_by_ptr.ptr as i32, diff --git a/crates/wiggle/tests/handles.rs b/crates/wiggle/tests/handles.rs index 6dce915286bf..150d1f09b2f9 100644 --- a/crates/wiggle/tests/handles.rs +++ b/crates/wiggle/tests/handles.rs @@ -11,10 +11,10 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> handle_examples::HandleExamples for WasiCtx<'a> { - fn fd_create(&self) -> Result { + fn fd_create(&mut self) -> Result { Ok(types::Fd::from(FD_VAL)) } - fn fd_consume(&self, fd: types::Fd) -> Result<(), types::Errno> { + fn fd_consume(&mut self, fd: types::Fd) -> Result<(), types::Errno> { println!("FD_CONSUME {}", fd); if fd == types::Fd::from(FD_VAL) { Ok(()) @@ -31,10 +31,10 @@ struct HandleExercise { impl HandleExercise { pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let e = handle_examples::fd_create(&ctx, &host_memory, self.return_loc.ptr as i32); + let e = handle_examples::fd_create(&mut ctx, &host_memory, self.return_loc.ptr as i32); assert_eq!(e, Ok(types::Errno::Ok as i32), "fd_create error"); @@ -45,11 +45,11 @@ impl HandleExercise { assert_eq!(h_got, 123, "fd_create return val"); - let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32); + let e = handle_examples::fd_consume(&mut ctx, &host_memory, h_got as i32); assert_eq!(e, Ok(types::Errno::Ok as i32), "fd_consume error"); - let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32 + 1); + let e = handle_examples::fd_consume(&mut ctx, &host_memory, h_got as i32 + 1); assert_eq!( e, diff --git a/crates/wiggle/tests/ints.rs b/crates/wiggle/tests/ints.rs index eb54b02f4e6f..bc5a980ee3d8 100644 --- a/crates/wiggle/tests/ints.rs +++ b/crates/wiggle/tests/ints.rs @@ -10,7 +10,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> ints::Ints for WasiCtx<'a> { - fn cookie_cutter(&self, init_cookie: types::Cookie) -> Result { + fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result { let res = if init_cookie == types::COOKIE_START { types::Bool::True } else { @@ -43,11 +43,11 @@ impl CookieCutterExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let res = ints::cookie_cutter( - &ctx, + &mut ctx, &host_memory, self.cookie as i64, self.return_ptr_loc.ptr as i32, diff --git a/crates/wiggle/tests/keywords.rs b/crates/wiggle/tests/keywords.rs index fb93d7b3b362..eda062b81977 100644 --- a/crates/wiggle/tests/keywords.rs +++ b/crates/wiggle/tests/keywords.rs @@ -33,7 +33,7 @@ mod module_trait_fn_and_arg_test { }); impl<'a> self_::Self_ for WasiCtx<'a> { #[allow(unused_variables)] - fn fn_(&self, use_: u32, virtual_: u32) { + fn fn_(&mut self, use_: u32, virtual_: u32) { unimplemented!(); } } diff --git a/crates/wiggle/tests/lists.rs b/crates/wiggle/tests/lists.rs index e797b5f0ca89..39649fc9b141 100644 --- a/crates/wiggle/tests/lists.rs +++ b/crates/wiggle/tests/lists.rs @@ -10,7 +10,7 @@ impl_errno!(types::Errno); impl<'a> lists::Lists for WasiCtx<'a> { fn reduce_excuses( - &self, + &mut self, excuses: &types::ConstExcuseArray, ) -> Result { let last = &excuses @@ -23,7 +23,7 @@ impl<'a> lists::Lists for WasiCtx<'a> { Ok(last.read().expect("dereferencing ptr should succeed")) } - fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { + fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { for excuse in excuses.iter() { let ptr_to_excuse = excuse .expect("valid ptr to ptr") @@ -74,7 +74,7 @@ impl ReduceExcusesExcercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Populate memory with pointers to generated Excuse values @@ -97,7 +97,7 @@ impl ReduceExcusesExcercise { } let res = lists::reduce_excuses( - &ctx, + &mut ctx, &host_memory, self.array_ptr_loc.ptr as i32, self.excuse_ptr_locs.len() as i32, @@ -162,7 +162,7 @@ impl PopulateExcusesExcercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Populate array with valid pointers to Excuse type in memory @@ -177,7 +177,7 @@ impl PopulateExcusesExcercise { } let res = lists::populate_excuses( - &ctx, + &mut ctx, &host_memory, self.array_ptr_loc.ptr as i32, self.elements.len() as i32, @@ -210,7 +210,7 @@ proptest! { impl<'a> array_traversal::ArrayTraversal for WasiCtx<'a> { fn sum_of_element( - &self, + &mut self, elements: &GuestPtr<[types::PairInts]>, index: u32, ) -> Result { @@ -219,7 +219,7 @@ impl<'a> array_traversal::ArrayTraversal for WasiCtx<'a> { Ok(pair.first.wrapping_add(pair.second)) } fn sum_of_elements( - &self, + &mut self, elements: &GuestPtr<[types::PairInts]>, start: u32, end: u32, @@ -288,7 +288,7 @@ impl SumElementsExercise { .boxed() } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Populate array @@ -301,7 +301,7 @@ impl SumElementsExercise { } let res = array_traversal::sum_of_element( - &ctx, + &mut ctx, &host_memory, self.element_loc.ptr as i32, self.elements.len() as i32, @@ -320,7 +320,7 @@ impl SumElementsExercise { // Off the end of the array: let res = array_traversal::sum_of_element( - &ctx, + &mut ctx, &host_memory, self.element_loc.ptr as i32, self.elements.len() as i32, @@ -334,7 +334,7 @@ impl SumElementsExercise { ); let res = array_traversal::sum_of_elements( - &ctx, + &mut ctx, &host_memory, self.element_loc.ptr as i32, self.elements.len() as i32, @@ -373,7 +373,7 @@ impl SumElementsExercise { // Index an array off the end of the array: let res = array_traversal::sum_of_elements( - &ctx, + &mut ctx, &host_memory, self.element_loc.ptr as i32, self.elements.len() as i32, diff --git a/crates/wiggle/tests/pointers.rs b/crates/wiggle/tests/pointers.rs index 7988733074ec..ef0ae6801706 100644 --- a/crates/wiggle/tests/pointers.rs +++ b/crates/wiggle/tests/pointers.rs @@ -10,7 +10,7 @@ impl_errno!(types::Errno); impl<'a> pointers::Pointers for WasiCtx<'a> { fn pointers_and_enums<'b>( - &self, + &mut self, input1: types::Excuse, input2_ptr: &GuestPtr<'b, types::Excuse>, input3_ptr: &GuestPtr<'b, types::Excuse>, @@ -125,7 +125,7 @@ impl PointersAndEnumsExercise { .boxed() } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); host_memory @@ -149,7 +149,7 @@ impl PointersAndEnumsExercise { .expect("input4 ptr ref_mut"); let e = pointers::pointers_and_enums( - &ctx, + &mut ctx, &host_memory, self.input1 as i32, self.input2_loc.ptr as i32, diff --git a/crates/wiggle/tests/records.rs b/crates/wiggle/tests/records.rs index 3c61e621a3d4..d6f96248080c 100644 --- a/crates/wiggle/tests/records.rs +++ b/crates/wiggle/tests/records.rs @@ -9,11 +9,11 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> records::Records for WasiCtx<'a> { - fn sum_of_pair(&self, an_pair: &types::PairInts) -> Result { + fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { Ok(an_pair.first as i64 + an_pair.second as i64) } - fn sum_of_pair_of_ptrs(&self, an_pair: &types::PairIntPtrs) -> Result { + fn sum_of_pair_of_ptrs(&mut self, an_pair: &types::PairIntPtrs) -> Result { let first = an_pair .first .read() @@ -25,7 +25,7 @@ impl<'a> records::Records for WasiCtx<'a> { Ok(first as i64 + second as i64) } - fn sum_of_int_and_ptr(&self, an_pair: &types::PairIntAndPtr) -> Result { + fn sum_of_int_and_ptr(&mut self, an_pair: &types::PairIntAndPtr) -> Result { let first = an_pair .first .read() @@ -34,7 +34,7 @@ impl<'a> records::Records for WasiCtx<'a> { Ok(first as i64 + second) } - fn return_pair_ints(&self) -> Result { + fn return_pair_ints(&mut self) -> Result { Ok(types::PairInts { first: 10, second: 20, @@ -42,7 +42,7 @@ impl<'a> records::Records for WasiCtx<'a> { } fn return_pair_of_ptrs<'b>( - &self, + &mut self, first: &GuestPtr<'b, i32>, second: &GuestPtr<'b, i32>, ) -> Result, types::Errno> { @@ -52,7 +52,10 @@ impl<'a> records::Records for WasiCtx<'a> { }) } - fn sum_array<'b>(&self, record_of_list: &types::RecordOfList<'b>) -> Result { + fn sum_array<'b>( + &mut self, + record_of_list: &types::RecordOfList<'b>, + ) -> Result { // my kingdom for try blocks fn aux(record_of_list: &types::RecordOfList) -> Result { let mut s = 0; @@ -99,7 +102,7 @@ impl SumOfPairExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); host_memory @@ -111,7 +114,7 @@ impl SumOfPairExercise { .write(self.input.second) .expect("input ref_mut"); let sum_err = records::sum_of_pair( - &ctx, + &mut ctx, &host_memory, self.input_loc.ptr as i32, self.return_loc.ptr as i32, @@ -187,7 +190,7 @@ impl SumPairPtrsExercise { .boxed() } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); host_memory @@ -209,7 +212,7 @@ impl SumPairPtrsExercise { .expect("input_struct ref"); let res = records::sum_of_pair_of_ptrs( - &ctx, + &mut ctx, &host_memory, self.input_struct_loc.ptr as i32, self.return_loc.ptr as i32, @@ -275,7 +278,7 @@ impl SumIntAndPtrExercise { .boxed() } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); host_memory @@ -292,7 +295,7 @@ impl SumIntAndPtrExercise { .expect("input_struct ref"); let res = records::sum_of_int_and_ptr( - &ctx, + &mut ctx, &host_memory, self.input_struct_loc.ptr as i32, self.return_loc.ptr as i32, @@ -332,10 +335,10 @@ impl ReturnPairInts { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let err = records::return_pair_ints(&ctx, &host_memory, self.return_loc.ptr as i32); + let err = records::return_pair_ints(&mut ctx, &host_memory, self.return_loc.ptr as i32); assert_eq!(err, Ok(types::Errno::Ok as i32), "return struct errno"); @@ -397,7 +400,7 @@ impl ReturnPairPtrsExercise { .boxed() } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); host_memory @@ -410,7 +413,7 @@ impl ReturnPairPtrsExercise { .expect("input_second ref"); let res = records::return_pair_of_ptrs( - &ctx, + &mut ctx, &host_memory, self.input_first_loc.ptr as i32, self.input_second_loc.ptr as i32, @@ -498,7 +501,7 @@ impl SumArrayExercise { .boxed() } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Write inputs to memory as an array @@ -522,7 +525,7 @@ impl SumArrayExercise { // Call wiggle-generated func let res = records::sum_array( - &ctx, + &mut ctx, &host_memory, self.input_struct_loc.ptr as i32, self.output_loc.ptr as i32, diff --git a/crates/wiggle/tests/strings.rs b/crates/wiggle/tests/strings.rs index e6eab2a9e2ff..3476924cad2e 100644 --- a/crates/wiggle/tests/strings.rs +++ b/crates/wiggle/tests/strings.rs @@ -9,14 +9,14 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> strings::Strings for WasiCtx<'a> { - fn hello_string(&self, a_string: &GuestPtr) -> Result { + fn hello_string(&mut self, a_string: &GuestPtr) -> Result { let s = a_string.as_str().expect("should be valid string"); println!("a_string='{}'", &*s); Ok(s.len() as u32) } fn multi_string( - &self, + &mut self, a: &GuestPtr, b: &GuestPtr, c: &GuestPtr, @@ -69,7 +69,7 @@ impl HelloStringExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Populate string in guest's memory @@ -81,7 +81,7 @@ impl HelloStringExercise { } let res = strings::hello_string( - &ctx, + &mut ctx, &host_memory, self.string_ptr_loc.ptr as i32, self.test_word.len() as i32, @@ -181,7 +181,7 @@ impl MultiStringExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let write_string = |val: &str, loc: MemArea| { @@ -198,7 +198,7 @@ impl MultiStringExercise { write_string(&self.c, self.sc_ptr_loc); let res = strings::multi_string( - &ctx, + &mut ctx, &host_memory, self.sa_ptr_loc.ptr as i32, self.a.len() as i32, @@ -260,7 +260,7 @@ impl OverlappingStringExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let write_string = |val: &str, loc: MemArea| { @@ -276,7 +276,7 @@ impl OverlappingStringExercise { let a_len = self.a.as_bytes().len() as i32; let res = strings::multi_string( - &ctx, + &mut ctx, &host_memory, self.sa_ptr_loc.ptr as i32, a_len, diff --git a/crates/wiggle/tests/variant.rs b/crates/wiggle/tests/variant.rs index d3679381069a..ea0f898b0dfc 100644 --- a/crates/wiggle/tests/variant.rs +++ b/crates/wiggle/tests/variant.rs @@ -31,7 +31,7 @@ fn mult_zero_nan(a: f32, b: u32) -> f32 { } impl<'a> variant_example::VariantExample for WasiCtx<'a> { - fn get_tag(&self, u: &types::Reason) -> Result { + fn get_tag(&mut self, u: &types::Reason) -> Result { println!("GET TAG: {:?}", u); match u { types::Reason::DogAte { .. } => Ok(types::Excuse::DogAte), @@ -39,7 +39,11 @@ impl<'a> variant_example::VariantExample for WasiCtx<'a> { types::Reason::Sleeping { .. } => Ok(types::Excuse::Sleeping), } } - fn reason_mult(&self, u: &types::ReasonMut<'_>, multiply_by: u32) -> Result<(), types::Errno> { + fn reason_mult( + &mut self, + u: &types::ReasonMut<'_>, + multiply_by: u32, + ) -> Result<(), types::Errno> { match u { types::ReasonMut::DogAte(fptr) => { let val = fptr.read().expect("valid pointer"); @@ -104,7 +108,7 @@ impl GetTagExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let discriminant = reason_tag(&self.input) as u8; @@ -126,7 +130,7 @@ impl GetTagExercise { types::Reason::Sleeping => {} // Do nothing } let e = variant_example::get_tag( - &ctx, + &mut ctx, &host_memory, self.input_loc.ptr as i32, self.return_loc.ptr as i32, @@ -181,7 +185,7 @@ impl ReasonMultExercise { } pub fn test(&self) { - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); let discriminant = reason_tag(&self.input) as u8; @@ -210,7 +214,7 @@ impl ReasonMultExercise { types::Reason::Sleeping => {} // Do nothing } let e = variant_example::reason_mult( - &ctx, + &mut ctx, &host_memory, self.input_loc.ptr as i32, self.multiply_by as i32, diff --git a/crates/wiggle/tests/wasi.rs b/crates/wiggle/tests/wasi.rs index d95f00844903..4c1043bb2d6b 100644 --- a/crates/wiggle/tests/wasi.rs +++ b/crates/wiggle/tests/wasi.rs @@ -32,32 +32,32 @@ impl GuestErrorType for types::Errno { } impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { - fn args_get(&self, _argv: &GuestPtr>, _argv_buf: &GuestPtr) -> Result<()> { + fn args_get(&mut self, _argv: &GuestPtr>, _argv_buf: &GuestPtr) -> Result<()> { unimplemented!("args_get") } - fn args_sizes_get(&self) -> Result<(types::Size, types::Size)> { + fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { unimplemented!("args_sizes_get") } fn environ_get( - &self, + &mut self, _environ: &GuestPtr>, _environ_buf: &GuestPtr, ) -> Result<()> { unimplemented!("environ_get") } - fn environ_sizes_get(&self) -> Result<(types::Size, types::Size)> { + fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { unimplemented!("environ_sizes_get") } - fn clock_res_get(&self, _id: types::Clockid) -> Result { + fn clock_res_get(&mut self, _id: types::Clockid) -> Result { unimplemented!("clock_res_get") } fn clock_time_get( - &self, + &mut self, _id: types::Clockid, _precision: types::Timestamp, ) -> Result { @@ -65,7 +65,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn fd_advise( - &self, + &mut self, _fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -75,7 +75,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn fd_allocate( - &self, + &mut self, _fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -83,24 +83,24 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_allocate") } - fn fd_close(&self, _fd: types::Fd) -> Result<()> { + fn fd_close(&mut self, _fd: types::Fd) -> Result<()> { unimplemented!("fd_close") } - fn fd_datasync(&self, _fd: types::Fd) -> Result<()> { + fn fd_datasync(&mut self, _fd: types::Fd) -> Result<()> { unimplemented!("fd_datasync") } - fn fd_fdstat_get(&self, _fd: types::Fd) -> Result { + fn fd_fdstat_get(&mut self, _fd: types::Fd) -> Result { unimplemented!("fd_fdstat_get") } - fn fd_fdstat_set_flags(&self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> { + fn fd_fdstat_set_flags(&mut self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> { unimplemented!("fd_fdstat_set_flags") } fn fd_fdstat_set_rights( - &self, + &mut self, _fd: types::Fd, _fs_rights_base: types::Rights, _fs_rights_inherting: types::Rights, @@ -108,16 +108,16 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_fdstat_set_rights") } - fn fd_filestat_get(&self, _fd: types::Fd) -> Result { + fn fd_filestat_get(&mut self, _fd: types::Fd) -> Result { unimplemented!("fd_filestat_get") } - fn fd_filestat_set_size(&self, _fd: types::Fd, _size: types::Filesize) -> Result<()> { + fn fd_filestat_set_size(&mut self, _fd: types::Fd, _size: types::Filesize) -> Result<()> { unimplemented!("fd_filestat_set_size") } fn fd_filestat_set_times( - &self, + &mut self, _fd: types::Fd, _atim: types::Timestamp, _mtim: types::Timestamp, @@ -127,7 +127,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn fd_pread( - &self, + &mut self, _fd: types::Fd, iovs: &types::IovecArray<'_>, _offset: types::Filesize, @@ -158,12 +158,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_pread") } - fn fd_prestat_get(&self, _fd: types::Fd) -> Result { + fn fd_prestat_get(&mut self, _fd: types::Fd) -> Result { unimplemented!("fd_prestat_get") } fn fd_prestat_dir_name( - &self, + &mut self, _fd: types::Fd, _path: &GuestPtr, _path_len: types::Size, @@ -172,7 +172,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn fd_pwrite( - &self, + &mut self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>, _offset: types::Filesize, @@ -180,12 +180,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_pwrite") } - fn fd_read(&self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result { + fn fd_read(&mut self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result { unimplemented!("fd_read") } fn fd_readdir( - &self, + &mut self, _fd: types::Fd, _buf: &GuestPtr, _buf_len: types::Size, @@ -194,12 +194,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_readdir") } - fn fd_renumber(&self, _fd: types::Fd, _to: types::Fd) -> Result<()> { + fn fd_renumber(&mut self, _fd: types::Fd, _to: types::Fd) -> Result<()> { unimplemented!("fd_renumber") } fn fd_seek( - &self, + &mut self, _fd: types::Fd, _offset: types::Filedelta, _whence: types::Whence, @@ -207,24 +207,24 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_seek") } - fn fd_sync(&self, _fd: types::Fd) -> Result<()> { + fn fd_sync(&mut self, _fd: types::Fd) -> Result<()> { unimplemented!("fd_sync") } - fn fd_tell(&self, _fd: types::Fd) -> Result { + fn fd_tell(&mut self, _fd: types::Fd) -> Result { unimplemented!("fd_tell") } - fn fd_write(&self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result { + fn fd_write(&mut self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result { unimplemented!("fd_write") } - fn path_create_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { + fn path_create_directory(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { unimplemented!("path_create_directory") } fn path_filestat_get( - &self, + &mut self, _fd: types::Fd, _flags: types::Lookupflags, _path: &GuestPtr<'_, str>, @@ -233,7 +233,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn path_filestat_set_times( - &self, + &mut self, _fd: types::Fd, _flags: types::Lookupflags, _path: &GuestPtr<'_, str>, @@ -245,7 +245,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn path_link( - &self, + &mut self, _old_fd: types::Fd, _old_flags: types::Lookupflags, _old_path: &GuestPtr<'_, str>, @@ -256,7 +256,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn path_open( - &self, + &mut self, _fd: types::Fd, _dirflags: types::Lookupflags, _path: &GuestPtr<'_, str>, @@ -269,7 +269,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn path_readlink( - &self, + &mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>, _buf: &GuestPtr, @@ -278,12 +278,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("path_readlink") } - fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { + fn path_remove_directory(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { unimplemented!("path_remove_directory") } fn path_rename( - &self, + &mut self, _fd: types::Fd, _old_path: &GuestPtr<'_, str>, _new_fd: types::Fd, @@ -293,7 +293,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn path_symlink( - &self, + &mut self, _old_path: &GuestPtr<'_, str>, _fd: types::Fd, _new_path: &GuestPtr<'_, str>, @@ -301,12 +301,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("path_symlink") } - fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { + fn path_unlink_file(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { unimplemented!("path_unlink_file") } fn poll_oneoff( - &self, + &mut self, _in_: &GuestPtr, _out: &GuestPtr, _nsubscriptions: types::Size, @@ -314,24 +314,24 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("poll_oneoff") } - fn proc_exit(&self, _rval: types::Exitcode) -> wiggle::Trap { + fn proc_exit(&mut self, _rval: types::Exitcode) -> wiggle::Trap { unimplemented!("proc_exit") } - fn proc_raise(&self, _sig: types::Signal) -> Result<()> { + fn proc_raise(&mut self, _sig: types::Signal) -> Result<()> { unimplemented!("proc_raise") } - fn sched_yield(&self) -> Result<()> { + fn sched_yield(&mut self) -> Result<()> { unimplemented!("sched_yield") } - fn random_get(&self, _buf: &GuestPtr, _buf_len: types::Size) -> Result<()> { + fn random_get(&mut self, _buf: &GuestPtr, _buf_len: types::Size) -> Result<()> { unimplemented!("random_get") } fn sock_recv( - &self, + &mut self, _fd: types::Fd, _ri_data: &types::IovecArray<'_>, _ri_flags: types::Riflags, @@ -340,7 +340,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { } fn sock_send( - &self, + &mut self, _fd: types::Fd, _si_data: &types::CiovecArray<'_>, _si_flags: types::Siflags, @@ -348,7 +348,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("sock_send") } - fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { + fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { unimplemented!("sock_shutdown") } } diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 01f8a8db4821..a2ce947b5806 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -72,35 +72,13 @@ fn generate_module( ctx_type: &syn::Type, async_conf: &AsyncConf, ) -> TokenStream2 { - let fields = module.funcs().map(|f| { - let name_ident = names.func(&f.name); - quote! { pub #name_ident: wasmtime::Func } - }); - let get_exports = module.funcs().map(|f| { - let func_name = f.name.as_str(); - let name_ident = names.func(&f.name); - quote! { #func_name => Some(&self.#name_ident) } - }); - let ctor_fields = module.funcs().map(|f| names.func(&f.name)); - - let module_name = module.name.as_str(); - - let linker_add = module.funcs().map(|f| { - let func_name = f.name.as_str(); - let name_ident = names.func(&f.name); - quote! { - linker.define(#module_name, #func_name, self.#name_ident.clone())?; - } - }); - let target_path = &target_conf.path; let module_id = names.module(&module.name); let target_module = quote! { #target_path::#module_id }; - let mut fns = Vec::new(); - let mut ctor_externs = Vec::new(); let mut host_funcs = Vec::new(); + let mut any_async = false; for f in module.funcs() { let asyncness = async_conf.is_async(module.name.as_str(), f.name.as_str()); match asyncness { @@ -110,134 +88,66 @@ fn generate_module( cfg!(feature = "async"), "generating async wasmtime Funcs requires cargo feature \"async\"" ); + any_async = true; } _ => {} } - generate_func( - &module_id, - &f, - names, - &target_module, - ctx_type, - asyncness, - &mut fns, - &mut ctor_externs, - &mut host_funcs, - ); + generate_func(&f, names, &target_module, asyncness, &mut host_funcs); } - let type_name = module_conf.name.clone(); - let type_docs = module_conf - .docs - .as_ref() - .map(|docs| quote!( #[doc = #docs] )) - .unwrap_or_default(); - let constructor_docs = format!( - "Creates a new [`{}`] instance. - -External values are allocated into the `store` provided and -configuration of the instance itself should be all -contained in the `cx` parameter.", - module_conf.name.to_string() - ); + let send_bound = if any_async { + quote! { + Send } + } else { + quote! {} + }; - let config_adder_definitions = host_funcs.iter().map(|(func_name, body)| { - let adder_func = format_ident!("add_{}_to_config", names.func(&func_name)); + let linker_add_definitions = host_funcs.iter().map(|(func_name, body)| { + let adder_func = format_ident!("add_{}_to_linker", names.func(&func_name)); let docs = format!( - "Add the host function for `{}` to a config under a given module and field name.", + "Add the host function for `{}` to a linker under a given module and field name.", func_name.as_str() ); quote! { #[doc = #docs] - pub fn #adder_func(config: &mut wasmtime::Config, module: &str, field: &str) { + pub fn #adder_func(linker: &mut wasmtime::Linker, module: &str, field: &str) + -> anyhow::Result<()> + where + T: std::borrow::BorrowMut<#ctx_type> #send_bound + { #body } } }); - let config_adder_invocations = host_funcs.iter().map(|(func_name, _body)| { - let adder_func = format_ident!("add_{}_to_config", names.func(&func_name)); + let linker_add_invocations = host_funcs.iter().map(|(func_name, _body)| { + let adder_func = format_ident!("add_{}_to_linker", names.func(&func_name)); let module = module.name.as_str(); let field = func_name.as_str(); quote! { - Self::#adder_func(config, #module, #field); + #adder_func(linker, #module, #field)?; } }); + let type_name = module_conf.name.clone(); + let add_to_linker = format_ident!("add_{}_to_linker", type_name); quote! { - #type_docs - pub struct #type_name { - #(#fields,)* + /// Adds all instance items to the specified `Linker`. + pub fn #add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> + where + T: std::borrow::BorrowMut<#ctx_type> #send_bound + { + #(#linker_add_invocations)* + Ok(()) } - impl #type_name { - #[doc = #constructor_docs] - pub fn new(store: &wasmtime::Store, ctx: std::rc::Rc>) -> Self { - #(#ctor_externs)* - - Self { - #(#ctor_fields,)* - } - } - - - /// Looks up a field called `name` in this structure, returning it - /// if found. - /// - /// This is often useful when instantiating a `wasmtime` instance - /// where name resolution often happens with strings. - pub fn get_export(&self, name: &str) -> Option<&wasmtime::Func> { - match name { - #(#get_exports,)* - _ => None, - } - } - - /// Adds all instance items to the specified `Linker`. - pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> { - #(#linker_add)* - Ok(()) - } - - /// Adds the host functions to the given [`wasmtime::Config`]. - /// - /// Host functions added to the config expect [`set_context`] to be called. - /// - /// Host functions will trap if the context is not set in the calling [`wasmtime::Store`]. - pub fn add_to_config(config: &mut wasmtime::Config) { - #(#config_adder_invocations)* - } - - #(#config_adder_definitions)* - - /// Sets the context in the given store. - /// - /// Context must be set in the store when using [`add_to_config`] and prior to any - /// host function being called. - /// - /// If the context is already set in the store, the given context is returned as an error. - pub fn set_context(store: &wasmtime::Store, ctx: #ctx_type) -> Result<(), #ctx_type> { - store.set(std::rc::Rc::new(std::cell::RefCell::new(ctx))).map_err(|ctx| { - match std::rc::Rc::try_unwrap(ctx) { - Ok(ctx) => ctx.into_inner(), - Err(_) => unreachable!(), - } - }) - } - - #(#fns)* - } + #(#linker_add_definitions)* } } fn generate_func( - module_ident: &Ident, func: &witx::InterfaceFunc, names: &Names, target_module: &TokenStream2, - ctx_type: &syn::Type, asyncness: Asyncness, - fns: &mut Vec, - ctors: &mut Vec, host_funcs: &mut Vec<(witx::Id, TokenStream2)>, ) { let rt = names.runtime_mod(); @@ -264,11 +174,6 @@ fn generate_func( _ => unimplemented!(), }; - let async_ = if asyncness.is_sync() { - quote!() - } else { - quote!(async) - }; let await_ = if asyncness.is_sync() { quote!() } else { @@ -276,120 +181,65 @@ fn generate_func( }; let runtime = names.runtime_mod(); - let fn_ident = format_ident!("{}_{}", module_ident, name_ident); - fns.push(quote! { - #async_ fn #fn_ident(caller: &wasmtime::Caller<'_>, ctx: &mut #ctx_type #(, #arg_decls)*) -> Result<#ret_ty, wasmtime::Trap> { - unsafe { - let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, - _ => { - return Err(wasmtime::Trap::new("missing required memory export")); - } - }; - let mem = #runtime::WasmtimeGuestMemory::new(mem); - match #target_module::#name_ident(ctx, &mem #(, #arg_names)*) #await_ { - Ok(r) => Ok(r.into()), - Err(wasmtime_wiggle::Trap::String(err)) => Err(wasmtime::Trap::new(err)), - Err(wasmtime_wiggle::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), - } + let body = quote! { + let mem = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + return Err(wasmtime::Trap::new("missing required memory export")); } + }; + let (ctx, mem) = unsafe { + // TODO: doc this + let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); + (caller.data_mut().borrow_mut(), #runtime::WasmtimeGuestMemory::new(mem)) + }; + match #target_module::#name_ident(ctx, &mem #(, #arg_names)*) #await_ { + Ok(r) => Ok(<#ret_ty>::from(r)), + Err(wasmtime_wiggle::Trap::String(err)) => Err(wasmtime::Trap::new(err)), + Err(wasmtime_wiggle::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), } - }); - - match asyncness { - Asyncness::Async => { - let wrapper = format_ident!("wrap{}_async", params.len()); - ctors.push(quote! { - let #name_ident = wasmtime::Func::#wrapper( - store, - ctx.clone(), - move |caller: wasmtime::Caller<'_>, my_ctx: &std::rc::Rc> #(,#arg_decls)*| - -> Box>> { - Box::new(async move { Self::#fn_ident(&caller, &mut my_ctx.borrow_mut() #(, #arg_names)*).await }) - } - ); - }); - } - Asyncness::Blocking => { - // Emit a synchronous function. Self::#fn_ident returns a Future, so we need to - // use a dummy executor to let any synchronous code inside there execute correctly. If - // the future ends up Pending, this func will Trap. - ctors.push(quote! { - let my_ctx = ctx.clone(); - let #name_ident = wasmtime::Func::wrap( - store, - move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { - #rt::run_in_dummy_executor(Self::#fn_ident(&caller, &mut my_ctx.borrow_mut() #(, #arg_names)*)) - } - ); - }); - } - Asyncness::Sync => { - ctors.push(quote! { - let my_ctx = ctx.clone(); - let #name_ident = wasmtime::Func::wrap( - store, - move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { - Self::#fn_ident(&caller, &mut my_ctx.borrow_mut() #(, #arg_names)*) - } - ); - }); - } - } + }; let host_wrapper = match asyncness { Asyncness::Async => { - let wrapper = format_ident!("wrap{}_host_func_async", params.len()); + let wrapper = format_ident!("func_wrap{}_async", params.len()); quote! { - config.#wrapper( + linker.#wrapper( module, field, - move |caller #(,#arg_decls)*| - -> Box>> { - Box::new(async move { - let ctx = caller.store() - .get::>>() - .ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?; - let result = Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*).await; - result - }) - } - ); + move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| { + Box::new(async move { #body }) + }, + )?; + Ok(()) } } Asyncness::Blocking => { - // Emit a synchronous host function. Self::#fn_ident returns a Future, so we need to - // use a dummy executor to let any synchronous code inside there execute correctly. If - // the future ends up Pending, this func will Trap. quote! { - config.wrap_host_func( + linker.func_wrap( module, field, - move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { - let ctx = caller - .store() - .get::>>() - .ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?; - #rt::run_in_dummy_executor(Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*)) + move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { + let result = async { #body }; + #rt::run_in_dummy_executor(result) }, - ); + )?; + Ok(()) } } + Asyncness::Sync => { quote! { - config.wrap_host_func( + linker.func_wrap( module, field, - move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { - let ctx = caller - .store() - .get::>>() - .ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?; - Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*) + move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { + #body }, - ); + )?; + Ok(()) } } }; diff --git a/crates/wiggle/wasmtime/src/lib.rs b/crates/wiggle/wasmtime/src/lib.rs index dc43683c142f..23dff142c73d 100644 --- a/crates/wiggle/wasmtime/src/lib.rs +++ b/crates/wiggle/wasmtime/src/lib.rs @@ -5,13 +5,13 @@ use wiggle_borrow::BorrowChecker; /// Lightweight `wasmtime::Memory` wrapper so we can implement the /// `wiggle::GuestMemory` trait on it. -pub struct WasmtimeGuestMemory { - mem: wasmtime::Memory, +pub struct WasmtimeGuestMemory<'a> { + mem: &'a mut [u8], bc: BorrowChecker, } -impl WasmtimeGuestMemory { - pub fn new(mem: wasmtime::Memory) -> Self { +impl<'a> WasmtimeGuestMemory<'a> { + pub fn new(mem: &'a mut [u8]) -> Self { Self { mem, // Wiggle does not expose any methods for functions to re-enter @@ -28,9 +28,9 @@ impl WasmtimeGuestMemory { } } -unsafe impl GuestMemory for WasmtimeGuestMemory { +unsafe impl GuestMemory for WasmtimeGuestMemory<'_> { fn base(&self) -> (*mut u8, u32) { - (self.mem.data_ptr(), self.mem.data_size() as _) + (self.mem.as_ptr() as *mut u8, self.mem.len() as u32) } fn has_outstanding_borrows(&self) -> bool { self.bc.has_outstanding_borrows() diff --git a/examples/externref.rs b/examples/externref.rs index f49f3bb6f186..794e5fbb7bf1 100644 --- a/examples/externref.rs +++ b/examples/externref.rs @@ -10,14 +10,13 @@ fn main() -> Result<()> { let mut config = Config::new(); config.wasm_reference_types(true); let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); println!("Compiling module..."); let module = Module::from_file(&engine, "examples/externref.wat")?; println!("Instantiating module..."); - let imports = []; - let instance = Instance::new(&store, &module, &imports)?; + let instance = Instance::new(&mut store, &module, &[])?; println!("Creating new `externref`..."); let externref = ExternRef::new("Hello, World!"); @@ -28,20 +27,25 @@ fn main() -> Result<()> { ); println!("Touching `externref` table..."); - let table = instance.get_table("table").unwrap(); - table.set(3, Some(externref.clone()).into())?; - let elem = table.get(3).unwrap().unwrap_externref().unwrap(); + let table = instance.get_table(&mut store, "table").unwrap(); + table.set(&mut store, 3, Some(externref.clone()).into())?; + let elem = table + .get(&mut store, 3) + .unwrap() // assert in bounds + .unwrap_externref() // assert it's an externref table + .unwrap(); // assert the externref isn't null assert!(elem.ptr_eq(&externref)); println!("Touching `externref` global..."); - let global = instance.get_global("global").unwrap(); - global.set(Some(externref.clone()).into())?; - let global_val = global.get().unwrap_externref().unwrap(); + let global = instance.get_global(&mut store, "global").unwrap(); + global.set(&mut store, Some(externref.clone()).into())?; + let global_val = global.get(&mut store).unwrap_externref().unwrap(); assert!(global_val.ptr_eq(&externref)); println!("Calling `externref` func..."); - let func = instance.get_typed_func::, Option>("func")?; - let ret = func.call(Some(externref.clone()))?; + let func = + instance.get_typed_func::, Option, _>(&mut store, "func")?; + let ret = func.call(&mut store, Some(externref.clone()))?; assert!(ret.is_some()); assert!(ret.unwrap().ptr_eq(&externref)); diff --git a/examples/fib-debug/main.rs b/examples/fib-debug/main.rs index 920cef8907b1..66216081a68d 100644 --- a/examples/fib-debug/main.rs +++ b/examples/fib-debug/main.rs @@ -16,12 +16,12 @@ fn main() -> Result<()> { // also ensure that we generate debuginfo so this executable can be // debugged in GDB. let engine = Engine::new(Config::new().debug_info(true))?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let module = Module::from_file(&engine, "target/wasm32-unknown-unknown/debug/fib.wasm")?; - let instance = Instance::new(&store, &module, &[])?; + let instance = Instance::new(&mut store, &module, &[])?; // Invoke `fib` export - let fib = instance.get_typed_func::("fib")?; - println!("fib(6) = {}", fib.call(6)?); + let fib = instance.get_typed_func::(&mut store, "fib")?; + println!("fib(6) = {}", fib.call(&mut store, 6)?); Ok(()) } diff --git a/examples/fuel.rs b/examples/fuel.rs index 00181a67e1aa..f47a7a16d4dd 100644 --- a/examples/fuel.rs +++ b/examples/fuel.rs @@ -9,16 +9,16 @@ fn main() -> Result<()> { let mut config = Config::new(); config.consume_fuel(true); let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); store.add_fuel(10_000)?; let module = Module::from_file(store.engine(), "examples/fuel.wat")?; - let instance = Instance::new(&store, &module, &[])?; + let instance = Instance::new(&mut store, &module, &[])?; // Invoke `fibonacci` export with higher and higher numbers until we exhaust our fuel. - let fibonacci = instance.get_typed_func::("fibonacci")?; + let fibonacci = instance.get_typed_func::(&mut store, "fibonacci")?; for n in 1.. { let fuel_before = store.fuel_consumed().unwrap(); - let output = match fibonacci.call(n) { + let output = match fibonacci.call(&mut store, n) { Ok(v) => v, Err(_) => { println!("Exhausted fuel computing fib({})", n); diff --git a/examples/gcd.rs b/examples/gcd.rs index 1cc13d37813d..a748c6d82752 100644 --- a/examples/gcd.rs +++ b/examples/gcd.rs @@ -10,13 +10,13 @@ fn main() -> Result<()> { // Load our WebAssembly (parsed WAT in our case), and then load it into a // `Module` which is attached to a `Store` cache. After we've got that we // can instantiate it. - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::from_file(store.engine(), "examples/gcd.wat")?; - let instance = Instance::new(&store, &module, &[])?; + let instance = Instance::new(&mut store, &module, &[])?; // Invoke `gcd` export - let gcd = instance.get_typed_func::<(i32, i32), i32>("gcd")?; + let gcd = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "gcd")?; - println!("gcd(6, 27) = {}", gcd.call((6, 27))?); + println!("gcd(6, 27) = {}", gcd.call(&mut store, (6, 27))?); Ok(()) } diff --git a/examples/hello.rs b/examples/hello.rs index 7936fa82d11e..62ae9fa352cf 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -6,23 +6,42 @@ use anyhow::Result; use wasmtime::*; -fn main() -> Result<()> { - // Configure the initial compilation environment, creating the global - // `Store` structure. Note that you can also tweak configuration settings - // with a `Config` and an `Engine` if desired. - println!("Initializing..."); - let store = Store::default(); +struct MyState { + name: String, + count: usize, +} - // Compile the wasm binary into an in-memory instance of a `Module`. +fn main() -> Result<()> { + // First the wasm module needs to be compiled. This is done with a global + // "compilation environment" within an `Engine`. Note that engines can be + // further configured through `Config` if desired instead of using the + // default like this is here. println!("Compiling module..."); - let module = Module::from_file(store.engine(), "examples/hello.wat")?; + let engine = Engine::default(); + let module = Module::from_file(&engine, "examples/hello.wat")?; + + // After a module is compiled we create a `Store` which will contain + // instantiated modules and other items like host functions. A Store + // contains an arbitrary piece of host information, and we use `MyState` + // here. + println!("Initializing..."); + let mut store = Store::new( + &engine, + MyState { + name: "hello, world!".to_string(), + count: 0, + }, + ); - // Here we handle the imports of the module, which in this case is our - // `HelloCallback` type and its associated implementation of `Callback. + // Our wasm module we'll be instantiating requires one imported function. + // the function takes no parameters and returns no results. We create a host + // implementation of that function here, and the `caller` parameter here is + // used to get access to our original `MyState` value. println!("Creating callback..."); - let hello_func = Func::wrap(&store, || { + let hello_func = Func::wrap(&mut store, |mut caller: Caller<'_, MyState>| { println!("Calling back..."); - println!("> Hello World!"); + println!("> {}", caller.data().name); + caller.data_mut().count += 1; }); // Once we've got that all set up we can then move to the instantiation @@ -30,15 +49,15 @@ fn main() -> Result<()> { // Note that this is where the wasm `start` function, if any, would run. println!("Instantiating module..."); let imports = [hello_func.into()]; - let instance = Instance::new(&store, &module, &imports)?; + let instance = Instance::new(&mut store, &module, &imports)?; // Next we poke around a bit to extract the `run` function from the module. println!("Extracting export..."); - let run = instance.get_typed_func::<(), ()>("run")?; + let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?; // And last but not least we can call it! println!("Calling export..."); - run.call(())?; + run.call(&mut store, ())?; println!("Done."); Ok(()) diff --git a/examples/interrupt.rs b/examples/interrupt.rs index 3f12afe09693..95179adbd3e0 100644 --- a/examples/interrupt.rs +++ b/examples/interrupt.rs @@ -10,13 +10,13 @@ fn main() -> Result<()> { // Enable interruptable code via `Config` and then create an interrupt // handle which we'll use later to interrupt running code. let engine = Engine::new(Config::new().interruptable(true))?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let interrupt_handle = store.interrupt_handle()?; // Compile and instantiate a small example with an infinite loop. let module = Module::from_file(&engine, "examples/interrupt.wat")?; - let instance = Instance::new(&store, &module, &[])?; - let run = instance.get_typed_func::<(), ()>("run")?; + let instance = Instance::new(&mut store, &module, &[])?; + let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?; // Spin up a thread to send us an interrupt in a second std::thread::spawn(move || { @@ -26,7 +26,7 @@ fn main() -> Result<()> { }); println!("Entering infinite loop ..."); - let trap = run.call(()).unwrap_err(); + let trap = run.call(&mut store, ()).unwrap_err(); println!("trap received..."); assert!(trap.to_string().contains("wasm trap: interrupt")); diff --git a/examples/linking.rs b/examples/linking.rs index 824bb1341086..279d5fa663ab 100644 --- a/examples/linking.rs +++ b/examples/linking.rs @@ -4,36 +4,35 @@ use anyhow::Result; use wasmtime::*; -use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder}; +use wasmtime_wasi::sync::WasiCtxBuilder; fn main() -> Result<()> { let engine = Engine::default(); - let store = Store::new(&engine); // First set up our linker which is going to be linking modules together. We // want our linker to have wasi available, so we set that up here as well. - let mut linker = Linker::new(&store); - let wasi = Wasi::new( - &store, - WasiCtxBuilder::new() - .inherit_stdio() - .inherit_args()? - .build()?, - ); - wasi.add_to_linker(&mut linker)?; + let mut linker = Linker::new(&engine); + wasmtime_wasi::add_to_linker(&mut linker)?; // Load and compile our two modules let linking1 = Module::from_file(&engine, "examples/linking1.wat")?; let linking2 = Module::from_file(&engine, "examples/linking2.wat")?; + // Configure WASI and insert it into a `Store` + let wasi = WasiCtxBuilder::new() + .inherit_stdio() + .inherit_args()? + .build()?; + let mut store = Store::new(&engine, wasi); + // Instantiate our first module which only uses WASI, then register that // instance with the linker since the next linking will use it. - let linking2 = linker.instantiate(&linking2)?; - linker.instance("linking2", &linking2)?; + let linking2 = linker.instantiate(&mut store, &linking2)?; + linker.instance(&mut store, "linking2", linking2)?; // And with that we can perform the final link and the execute the module. - let linking1 = linker.instantiate(&linking1)?; - let run = linking1.get_typed_func::<(), ()>("run")?; - run.call(())?; + let linking1 = linker.instantiate(&mut store, &linking1)?; + let run = linking1.get_typed_func::<(), (), _>(&mut store, "run")?; + run.call(&mut store, ())?; Ok(()) } diff --git a/examples/memory.rs b/examples/memory.rs index e47c249e5fc9..6c980ae983e0 100644 --- a/examples/memory.rs +++ b/examples/memory.rs @@ -10,75 +10,65 @@ use anyhow::Result; use wasmtime::*; fn main() -> Result<()> { - // Create our `Store` context and then compile a module and create an + // Create our `store_fn` context and then compile a module and create an // instance from the compiled module all in one go. - let wasmtime_store = Store::default(); - let module = Module::from_file(wasmtime_store.engine(), "examples/memory.wat")?; - let instance = Instance::new(&wasmtime_store, &module, &[])?; + let mut store: Store<()> = Store::default(); + let module = Module::from_file(store.engine(), "examples/memory.wat")?; + let instance = Instance::new(&mut store, &module, &[])?; - // Load up our exports from the instance + // load_fn up our exports from the instance let memory = instance - .get_memory("memory") + .get_memory(&mut store, "memory") .ok_or(anyhow::format_err!("failed to find `memory` export"))?; - let size = instance.get_typed_func::<(), i32>("size")?; - let load = instance.get_typed_func::("load")?; - let store = instance.get_typed_func::<(i32, i32), ()>("store")?; + let size = instance.get_typed_func::<(), i32, _>(&mut store, "size")?; + let load_fn = instance.get_typed_func::(&mut store, "load")?; + let store_fn = instance.get_typed_func::<(i32, i32), (), _>(&mut store, "store")?; - // Note that these memory reads are *unsafe* due to unknown knowledge about - // aliasing with wasm memory. For more information about the safety - // guarantees here and how to use `Memory` safely, see the API - // documentation. println!("Checking memory..."); - assert_eq!(memory.size(), 2); - assert_eq!(memory.data_size(), 0x20000); - unsafe { - assert_eq!(memory.data_unchecked_mut()[0], 0); - assert_eq!(memory.data_unchecked_mut()[0x1000], 1); - assert_eq!(memory.data_unchecked_mut()[0x1003], 4); - } + assert_eq!(memory.size(&store), 2); + assert_eq!(memory.data_size(&store), 0x20000); + assert_eq!(memory.data_mut(&mut store)[0], 0); + assert_eq!(memory.data_mut(&mut store)[0x1000], 1); + assert_eq!(memory.data_mut(&mut store)[0x1003], 4); - assert_eq!(size.call(())?, 2); - assert_eq!(load.call(0)?, 0); - assert_eq!(load.call(0x1000)?, 1); - assert_eq!(load.call(0x1003)?, 4); - assert_eq!(load.call(0x1ffff)?, 0); - assert!(load.call(0x20000).is_err()); // out of bounds trap + assert_eq!(size.call(&mut store, ())?, 2); + assert_eq!(load_fn.call(&mut store, 0)?, 0); + assert_eq!(load_fn.call(&mut store, 0x1000)?, 1); + assert_eq!(load_fn.call(&mut store, 0x1003)?, 4); + assert_eq!(load_fn.call(&mut store, 0x1ffff)?, 0); + assert!(load_fn.call(&mut store, 0x20000).is_err()); // out of bounds trap println!("Mutating memory..."); - unsafe { - memory.data_unchecked_mut()[0x1003] = 5; - } + memory.data_mut(&mut store)[0x1003] = 5; - store.call((0x1002, 6))?; - assert!(store.call((0x20000, 0)).is_err()); // out of bounds trap + store_fn.call(&mut store, (0x1002, 6))?; + assert!(store_fn.call(&mut store, (0x20000, 0)).is_err()); // out of bounds trap - unsafe { - assert_eq!(memory.data_unchecked_mut()[0x1002], 6); - assert_eq!(memory.data_unchecked_mut()[0x1003], 5); - } - assert_eq!(load.call(0x1002)?, 6); - assert_eq!(load.call(0x1003)?, 5); + assert_eq!(memory.data(&store)[0x1002], 6); + assert_eq!(memory.data(&store)[0x1003], 5); + assert_eq!(load_fn.call(&mut store, 0x1002)?, 6); + assert_eq!(load_fn.call(&mut store, 0x1003)?, 5); // Grow memory. println!("Growing memory..."); - memory.grow(1)?; - assert_eq!(memory.size(), 3); - assert_eq!(memory.data_size(), 0x30000); + memory.grow(&mut store, 1)?; + assert_eq!(memory.size(&store), 3); + assert_eq!(memory.data_size(&store), 0x30000); - assert_eq!(load.call(0x20000)?, 0); - store.call((0x20000, 0))?; - assert!(load.call(0x30000).is_err()); - assert!(store.call((0x30000, 0)).is_err()); + assert_eq!(load_fn.call(&mut store, 0x20000)?, 0); + store_fn.call(&mut store, (0x20000, 0))?; + assert!(load_fn.call(&mut store, 0x30000).is_err()); + assert!(store_fn.call(&mut store, (0x30000, 0)).is_err()); - assert!(memory.grow(1).is_err()); - assert!(memory.grow(0).is_ok()); + assert!(memory.grow(&mut store, 1).is_err()); + assert!(memory.grow(&mut store, 0).is_ok()); println!("Creating stand-alone memory..."); let memorytype = MemoryType::new(Limits::new(5, Some(5))); - let memory2 = Memory::new(&wasmtime_store, memorytype)?; - assert_eq!(memory2.size(), 5); - assert!(memory2.grow(1).is_err()); - assert!(memory2.grow(0).is_ok()); + let memory2 = Memory::new(&mut store, memorytype)?; + assert_eq!(memory2.size(&store), 5); + assert!(memory2.grow(&mut store, 1).is_err()); + assert!(memory2.grow(&mut store, 0).is_ok()); Ok(()) } diff --git a/examples/multi.rs b/examples/multi.rs index 6b224c8e8cb8..df36671cebd3 100644 --- a/examples/multi.rs +++ b/examples/multi.rs @@ -15,38 +15,30 @@ fn main() -> Result<()> { println!("Initializing..."); let engine = Engine::default(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); // Compile. println!("Compiling module..."); let module = Module::from_file(&engine, "examples/multi.wat")?; - // Create external print functions. + // Create a host function which takes multiple parameters and returns + // multiple results. println!("Creating callback..."); - let callback_type = FuncType::new( - [ValType::I32, ValType::I64].iter().cloned(), - [ValType::I64, ValType::I32].iter().cloned(), - ); - let callback_func = Func::new(&store, callback_type, |_, args, results| { - println!("Calling back..."); - println!("> {} {}", args[0].unwrap_i32(), args[1].unwrap_i64()); - - results[0] = Val::I64(args[1].unwrap_i64() + 1); - results[1] = Val::I32(args[0].unwrap_i32() + 1); - Ok(()) + let callback_func = Func::wrap(&mut store, |a: i32, b: i64| -> (i64, i32) { + (b + 1, a + 1) }); // Instantiate. println!("Instantiating module..."); - let instance = Instance::new(&store, &module, &[callback_func.into()])?; + let instance = Instance::new(&mut store, &module, &[callback_func.into()])?; // Extract exports. println!("Extracting export..."); - let g = instance.get_typed_func::<(i32, i64), (i64, i32)>("g")?; + let g = instance.get_typed_func::<(i32, i64), (i64, i32), _>(&mut store, "g")?; // Call `$g`. println!("Calling export \"g\"..."); - let (a, b) = g.call((1, 3))?; + let (a, b) = g.call(&mut store, (1, 3))?; println!("Printing result..."); println!("> {} {}", a, b); @@ -60,9 +52,10 @@ fn main() -> Result<()> { .get_typed_func::< (i64, i64, i64, i64, i64, i64, i64, i64, i64, i64), (i64, i64, i64, i64, i64, i64, i64, i64, i64, i64), + _, > - ("round_trip_many")?; - let results = round_trip_many.call((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))?; + (&mut store, "round_trip_many")?; + let results = round_trip_many.call(&mut store, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9))?; println!("Printing result..."); println!("> {:?}", results); @@ -71,6 +64,9 @@ fn main() -> Result<()> { Ok(()) } +// Note that this example is not supported in the off-by-default feature of the +// old x86 compiler backend for Cranelift. Wasmtime's default configuration +// supports this example, however. #[cfg(feature = "old-x86-backend")] fn main() -> Result<()> { Ok(()) diff --git a/examples/serialize.rs b/examples/serialize.rs index 70a875c3bf20..22a281698b60 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -27,7 +27,7 @@ fn deserialize(buffer: &[u8]) -> Result<()> { // `Store` structure. Note that you can also tweak configuration settings // with a `Config` and an `Engine` if desired. println!("Initializing..."); - let store = Store::default(); + let mut store: Store<()> = Store::default(); // Compile the wasm binary into an in-memory instance of a `Module`. Note // that this is `unsafe` because it is our responsibility for guaranteeing @@ -39,7 +39,7 @@ fn deserialize(buffer: &[u8]) -> Result<()> { // Here we handle the imports of the module, which in this case is our // `HelloCallback` type and its associated implementation of `Callback. println!("Creating callback..."); - let hello_func = Func::wrap(&store, || { + let hello_func = Func::wrap(&mut store, || { println!("Calling back..."); println!("> Hello World!"); }); @@ -49,15 +49,15 @@ fn deserialize(buffer: &[u8]) -> Result<()> { // Note that this is where the wasm `start` function, if any, would run. println!("Instantiating module..."); let imports = [hello_func.into()]; - let instance = Instance::new(&store, &module, &imports)?; + let instance = Instance::new(&mut store, &module, &imports)?; // Next we poke around a bit to extract the `run` function from the module. println!("Extracting export..."); - let run = instance.get_typed_func::<(), ()>("run")?; + let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?; // And last but not least we can call it! println!("Calling export..."); - run.call(())?; + run.call(&mut store, ())?; println!("Done."); Ok(()) diff --git a/examples/threads.rs b/examples/threads.rs index b51e8fe41bc7..9586022a5ee4 100644 --- a/examples/threads.rs +++ b/examples/threads.rs @@ -1,6 +1,10 @@ +//! This program is an example of how Wasmtime can be used with multithreaded +//! runtimes and how various types and structures can be shared across threads. + // You can execute this example with `cargo run --example threads` use anyhow::Result; +use std::sync::Arc; use std::thread; use std::time; use wasmtime::*; @@ -8,57 +12,59 @@ use wasmtime::*; const N_THREADS: i32 = 10; const N_REPS: i32 = 3; -fn run(engine: &Engine, module: Module, id: i32) -> Result<()> { - let store = Store::new(&engine); - - // Create external print functions. - println!("Creating callback..."); - let callback_func = Func::wrap(&store, |arg: i32| { - println!("> Thread {} is running", arg); - }); - - let id_type = GlobalType::new(ValType::I32, Mutability::Const); - let id_global = Global::new(&store, id_type, Val::I32(id))?; +fn main() -> Result<()> { + println!("Initializing..."); - // Instantiate. - println!("Instantiating module..."); - let instance = Instance::new(&store, &module, &[callback_func.into(), id_global.into()])?; + // Initialize global per-process state. This state will be shared amonst all + // threads. Notably this includes the compiled module as well as a `Linker`, + // which contains all our host functions we want to define. + let engine = Engine::default(); + let module = Module::from_file(&engine, "examples/threads.wat")?; + let mut linker = Linker::new(&engine); + linker.func_wrap("global", "hello", || { + println!("> Hello from {:?}", thread::current().id()); + })?; + let linker = Arc::new(linker); // "finalize" the linker - // Extract exports. - println!("Extracting export..."); - let g = instance.get_typed_func::<(), ()>("run")?; + // Share this global state amongst a set of threads, each of which will + // create stores and execute instances. + let children = (0..N_THREADS) + .map(|_| { + let engine = engine.clone(); + let module = module.clone(); + let linker = linker.clone(); + thread::spawn(move || { + run(&engine, &module, &linker).expect("Success"); + }) + }) + .collect::>(); - for _ in 0..N_REPS { - thread::sleep(time::Duration::from_millis(100)); - // Call `$run`. - drop(g.call(())?); + for child in children { + child.join().unwrap(); } Ok(()) } -fn main() -> Result<()> { - println!("Initializing..."); - let engine = Engine::default(); - - // Compile. - println!("Compiling module..."); - let module = Module::from_file(&engine, "examples/threads.wat")?; +fn run(engine: &Engine, module: &Module, linker: &Linker<()>) -> Result<()> { + // Each sub-thread we have starting out by instantiating the `module` + // provided into a fresh `Store`. + println!("Instantiating module..."); + let mut store = Store::new(&engine, ()); + let instance = linker.instantiate(&mut store, module)?; + let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?; - let mut children = Vec::new(); - for id in 0..N_THREADS { - let engine = engine.clone(); - let module = module.clone(); - children.push(thread::spawn(move || { - run(&engine, module, id).expect("Success"); - })); + println!("Executing..."); + for _ in 0..N_REPS { + run.call(&mut store, ())?; + thread::sleep(time::Duration::from_millis(100)); } - for (i, child) in children.into_iter().enumerate() { - if let Err(_) = child.join() { - println!("Thread #{} errors", i); - } - } + // Also note that that a `Store` can also move between threads: + println!("> Moving {:?} to a new thread", thread::current().id()); + let child = thread::spawn(move || run.call(&mut store, ())); + + child.join().unwrap()?; Ok(()) } diff --git a/examples/threads.wat b/examples/threads.wat index 228f99f2b564..95e7d6f7bffb 100644 --- a/examples/threads.wat +++ b/examples/threads.wat @@ -1,5 +1,4 @@ (module - (func $message (import "" "hello") (param i32)) - (global $id (import "" "id") i32) - (func (export "run") (call $message (global.get $id))) + (func $hello (import "global" "hello")) + (func (export "run") (call $hello)) ) diff --git a/examples/tokio/main.rs b/examples/tokio/main.rs index 491fe754db80..99ebdd662deb 100644 --- a/examples/tokio/main.rs +++ b/examples/tokio/main.rs @@ -1,11 +1,11 @@ -use anyhow::{anyhow, Error}; -use std::future::Future; +use anyhow::Error; +use std::sync::Arc; use tokio::time::Duration; use wasmtime::{Config, Engine, Linker, Module, Store}; // For this example we want to use the async version of wasmtime_wasi. // Notably, this version of wasi uses a scheduler that will async yield // when sleeping in `poll_oneoff`. -use wasmtime_wasi::tokio::{Wasi, WasiCtxBuilder}; +use wasmtime_wasi::{tokio::WasiCtxBuilder, WasiCtx}; #[tokio::main] async fn main() -> Result<(), Error> { @@ -42,6 +42,7 @@ async fn main() -> Result<(), Error> { struct Environment { engine: Engine, module: Module, + linker: Arc>, } impl Environment { @@ -52,13 +53,21 @@ impl Environment { config.async_support(true); config.consume_fuel(true); - // Install the host functions for `Wasi`. - Wasi::add_to_config(&mut config); - let engine = Engine::new(&config)?; let module = Module::from_file(&engine, "target/wasm32-wasi/debug/tokio-wasi.wasm")?; - Ok(Self { engine, module }) + // A `Linker` is shared in the environment amongst all stores, and this + // linker is used to instantiate the `module` above. This example only + // adds WASI functions to the linker, notably the async versions built + // on tokio. + let mut linker = Linker::new(&engine); + wasmtime_wasi::tokio::add_to_linker(&mut linker)?; + + Ok(Self { + engine, + module, + linker: Arc::new(linker), + }) } } @@ -76,87 +85,29 @@ impl Inputs { } } -fn run_wasm(inputs: Inputs) -> impl Future> { - use std::pin::Pin; - use std::task::{Context, Poll}; - // IMPORTANT: The current wasmtime API is very challenging to use safely - // on an async runtime. This RFC describes a redesign of the API that will - // resolve these safety issues: - // https://github.com/alexcrichton/rfcs-2/blob/new-api/accepted/new-api.md - - // This is a "marker type future" which simply wraps some other future and - // the only purpose it serves is to forward the implementation of `Future` - // as well as have `unsafe impl Send` for itself, regardless of the - // underlying type. - // - // Note that the qctual safety of this relies on the fact that the inputs - // here are `Send`, the outputs (just () in this case) are `Send`, and the - // future itself is safe tu resume on other threads. - // - // For an in-depth discussion of the safety of moving Wasmtime's `Store` - // between threads, see - // https://docs.wasmtime.dev/examples-rust-multithreading.html. - struct UnsafeSend(T); - - // Note the `where` cause specifically ensures the output of the future to - // be `Send` is required. We specifically dont require `T` to be `Send` - // since that's the whole point of this function, but we require that - // everything used to construct `T` is `Send` below. - unsafe impl Send for UnsafeSend - where - T: Future, - T::Output: Send, - { - } - impl Future for UnsafeSend { - type Output = T::Output; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Note that this `unsafe` is unrelated to `Send`, it only has to do with "pin - // projection" and should be safe since it's all we do with the `Pin`. - unsafe { self.map_unchecked_mut(|p| &mut p.0).poll(cx) } - } - } - - // This is a crucial assertion that needs to be here. The compiler - // typically checks this for us, but do to our `UnsafeSend` type the - // compiler isn't automatically checking this. The assertion here must - // assert that all arguments to this function are indeed `Send` because - // we're closing over them and sending them to other threads. It's only - // everything *internal* to the computation of this function which doesn't - // have to be `Send`. - fn assert_send(_t: &T) {} - assert_send(&inputs); - - // Wrap up the `_run_wasm` function, which is *not* `Send`, but is safe to - // resume on other threads. - UnsafeSend(_run_wasm(inputs)) -} - -async fn _run_wasm(inputs: Inputs) -> Result<(), Error> { - let store = Store::new(&inputs.env.engine); +async fn run_wasm(inputs: Inputs) -> Result<(), Error> { + let wasi = WasiCtxBuilder::new() + // Let wasi print to this process's stdout. + .inherit_stdout() + // Set an environment variable so the wasm knows its name. + .env("NAME", &inputs.name)? + .build()?; + let mut store = Store::new(&inputs.env.engine, wasi); // WebAssembly execution will be paused for an async yield every time it // consumes 10000 fuel. Fuel will be refilled u32::MAX times. store.out_of_fuel_async_yield(u32::MAX, 10000); - Wasi::set_context( - &store, - WasiCtxBuilder::new() - // Let wasi print to this process's stdout. - .inherit_stdout() - // Set an environment variable so the wasm knows its name. - .env("NAME", &inputs.name)? - .build()?, - ) - .map_err(|_| anyhow!("setting wasi context"))?; - - let linker = Linker::new(&store); - - // Instantiate - let instance = linker.instantiate_async(&inputs.env.module).await?; + // Instantiate into our own unique store using the shared linker, afterwards + // acquiring the `_start` function for the module and executing it. + let instance = inputs + .env + .linker + .instantiate_async(&mut store, &inputs.env.module) + .await?; instance - .get_typed_func::<(), ()>("_start")? - .call_async(()) + .get_typed_func::<(), (), _>(&mut store, "_start")? + .call_async(&mut store, ()) .await?; Ok(()) diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index 0c3f077d9307..5d5092b2bc31 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -5,38 +5,30 @@ use anyhow::Result; use wasmtime::*; -use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder}; +use wasmtime_wasi::sync::WasiCtxBuilder; fn main() -> Result<()> { - tracing_subscriber::FmtSubscriber::builder() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .with_ansi(true) - .init(); - // Define the WASI functions globally on the `Config`. - let mut config = Config::default(); - Wasi::add_to_config(&mut config); - - let store = Store::new(&Engine::new(&config)?); - - // Set the WASI context in the store; all instances in the store share this context. - // `WasiCtxBuilder` provides a number of ways to configure what the target program - // will have access to. - assert!(Wasi::set_context( - &store, - WasiCtxBuilder::new() - .inherit_stdio() - .inherit_args()? - .build()? - ) - .is_ok()); - - let mut linker = Linker::new(&store); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + wasmtime_wasi::add_to_linker(&mut linker)?; + + // Create a WASI context and put it in a Store; all instances in the store + // share this context. `WasiCtxBuilder` provides a number of ways to + // configure what the target program will have access to. + let wasi = WasiCtxBuilder::new() + .inherit_stdio() + .inherit_args()? + .build()?; + let mut store = Store::new(&engine, wasi); // Instantiate our module with the imports we've created, and run it. - let module = Module::from_file(store.engine(), "target/wasm32-wasi/debug/wasi.wasm")?; - linker.module("", &module)?; - linker.get_default("")?.typed::<(), ()>()?.call(())?; + let module = Module::from_file(&engine, "target/wasm32-wasi/debug/wasi.wasm")?; + linker.module(&mut store, "", &module)?; + linker + .get_default(&mut store, "")? + .typed::<(), (), _>(&store)? + .call(&mut store, ())?; Ok(()) } diff --git a/src/commands/compile.rs b/src/commands/compile.rs index 9d601ab7a03b..23088d68e1d3 100644 --- a/src/commands/compile.rs +++ b/src/commands/compile.rs @@ -128,10 +128,10 @@ mod test { let engine = Engine::default(); let contents = std::fs::read(output_path)?; let module = unsafe { Module::deserialize(&engine, contents)? }; - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let f = instance.get_typed_func::("f")?; - assert_eq!(f.call(1234).unwrap(), 1234); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let f = instance.get_typed_func::(&mut store, "f")?; + assert_eq!(f.call(&mut store, 1234).unwrap(), 1234); Ok(()) } diff --git a/src/commands/run.rs b/src/commands/run.rs index eeec81b2b70d..15bfe1f95a11 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -2,6 +2,7 @@ use crate::{CommonOptions, WasiModules}; use anyhow::{bail, Context as _, Result}; +use std::borrow::{Borrow, BorrowMut}; use std::thread; use std::time::Duration; use std::{ @@ -11,16 +12,13 @@ use std::{ }; use structopt::{clap::AppSettings, StructOpt}; use wasmtime::{Engine, Func, Linker, Module, Store, Trap, Val, ValType}; -use wasmtime_wasi::sync::{Dir, Wasi, WasiCtxBuilder}; +use wasmtime_wasi::sync::{Dir, WasiCtxBuilder}; #[cfg(feature = "wasi-nn")] -use wasmtime_wasi_nn::{WasiNn, WasiNnCtx}; +use wasmtime_wasi_nn::WasiNnCtx; #[cfg(feature = "wasi-crypto")] -use wasmtime_wasi_crypto::{ - WasiCryptoAsymmetricCommon, WasiCryptoCommon, WasiCryptoCtx, WasiCryptoSignatures, - WasiCryptoSymmetric, -}; +use wasmtime_wasi_crypto::WasiCryptoCtx; fn parse_module(s: &OsStr) -> Result { // Do not accept wasmtime subcommand names as the module name @@ -136,14 +134,15 @@ impl RunCommand { config.interruptable(true); } let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, Host::default()); // Make wasi available by default. let preopen_dirs = self.compute_preopen_dirs()?; let argv = self.compute_argv(); - let mut linker = Linker::new(&store); + let mut linker = Linker::new(&engine); populate_with_wasi( + &mut store, &mut linker, preopen_dirs, &argv, @@ -157,7 +156,7 @@ impl RunCommand { let module = Module::from_file(&engine, path)?; // Add the module's functions to the linker. - linker.module(name, &module).context(format!( + linker.module(&mut store, name, &module).context(format!( "failed to process preload `{}` at `{}`", name, path.display() @@ -166,7 +165,7 @@ impl RunCommand { // Load the main wasm module. match self - .load_main_module(&mut linker) + .load_main_module(&mut store, &mut linker) .with_context(|| format!("failed to run main module `{}`", self.module.display())) { Ok(()) => (), @@ -250,9 +249,9 @@ impl RunCommand { result } - fn load_main_module(&self, linker: &mut Linker) -> Result<()> { + fn load_main_module(&self, store: &mut Store, linker: &mut Linker) -> Result<()> { if let Some(timeout) = self.wasm_timeout { - let handle = linker.store().interrupt_handle()?; + let handle = store.interrupt_handle()?; thread::spawn(move || { thread::sleep(timeout); handle.interrupt(); @@ -261,30 +260,38 @@ impl RunCommand { // Read the wasm module binary either as `*.wat` or a raw binary. // Use "" as a default module name. - let module = Module::from_file(linker.store().engine(), &self.module)?; + let module = Module::from_file(linker.engine(), &self.module)?; linker - .module("", &module) + .module(&mut *store, "", &module) .context(format!("failed to instantiate {:?}", self.module))?; // If a function to invoke was given, invoke it. if let Some(name) = self.invoke.as_ref() { - self.invoke_export(linker, name) + self.invoke_export(store, linker, name) } else { - let func = linker.get_default("")?; - self.invoke_func(func, None) + let func = linker.get_default(&mut *store, "")?; + self.invoke_func(store, func, None) } } - fn invoke_export(&self, linker: &Linker, name: &str) -> Result<()> { - let func = match linker.get_one_by_name("", Some(name))?.into_func() { + fn invoke_export( + &self, + store: &mut Store, + linker: &Linker, + name: &str, + ) -> Result<()> { + let func = match linker + .get_one_by_name(&mut *store, "", Some(name))? + .into_func() + { Some(func) => func, None => bail!("export of `{}` wasn't a function", name), }; - self.invoke_func(func, Some(name)) + self.invoke_func(store, func, Some(name)) } - fn invoke_func(&self, func: Func, name: Option<&str>) -> Result<()> { - let ty = func.ty(); + fn invoke_func(&self, store: &mut Store, func: Func, name: Option<&str>) -> Result<()> { + let ty = func.ty(&store); if ty.params().len() > 0 { eprintln!( "warning: using `--invoke` with a function that takes arguments \ @@ -318,7 +325,7 @@ impl RunCommand { // Invoke the function and then afterwards print all the results that came // out, if there are any. - let results = func.call(&values).with_context(|| { + let results = func.call(store, &values).with_context(|| { if let Some(name) = name { format!("failed to invoke `{}`", name) } else { @@ -348,24 +355,74 @@ impl RunCommand { } } +#[derive(Default)] +struct Host { + wasi: Option, + #[cfg(feature = "wasi-nn")] + wasi_nn: Option, + #[cfg(feature = "wasi-crypto")] + wasi_crypto: Option, +} + +impl Borrow for Host { + fn borrow(&self) -> &wasmtime_wasi::WasiCtx { + self.wasi.as_ref().unwrap() + } +} + +impl BorrowMut for Host { + fn borrow_mut(&mut self) -> &mut wasmtime_wasi::WasiCtx { + self.wasi.as_mut().unwrap() + } +} + +#[cfg(feature = "wasi-nn")] +impl Borrow for Host { + fn borrow(&self) -> &WasiNnCtx { + self.wasi_nn.as_ref().unwrap() + } +} + +#[cfg(feature = "wasi-nn")] +impl BorrowMut for Host { + fn borrow_mut(&mut self) -> &mut WasiNnCtx { + self.wasi_nn.as_mut().unwrap() + } +} + +#[cfg(feature = "wasi-crypto")] +impl Borrow for Host { + fn borrow(&self) -> &WasiCryptoCtx { + self.wasi_crypto.as_ref().unwrap() + } +} + +#[cfg(feature = "wasi-crypto")] +impl BorrowMut for Host { + fn borrow_mut(&mut self) -> &mut WasiCryptoCtx { + self.wasi_crypto.as_mut().unwrap() + } +} + /// Populates the given `Linker` with WASI APIs. fn populate_with_wasi( - linker: &mut Linker, + store: &mut Store, + linker: &mut Linker, preopen_dirs: Vec<(String, Dir)>, argv: &[String], vars: &[(String, String)], wasi_modules: &WasiModules, ) -> Result<()> { - // Add the current snapshot to the linker. - let mut builder = WasiCtxBuilder::new(); - builder = builder.inherit_stdio().args(argv)?.envs(vars)?; + if wasi_modules.wasi_common { + wasmtime_wasi::add_to_linker(linker)?; - for (name, dir) in preopen_dirs.into_iter() { - builder = builder.preopened_dir(dir, name)?; - } + let mut builder = WasiCtxBuilder::new(); + builder = builder.inherit_stdio().args(argv)?.envs(vars)?; - if wasi_modules.wasi_common { - Wasi::new(linker.store(), builder.build()?).add_to_linker(linker)?; + for (name, dir) in preopen_dirs.into_iter() { + builder = builder.preopened_dir(dir, name)?; + } + store.data_mut().wasi = Some(builder.build()?); } if wasi_modules.wasi_nn { @@ -375,10 +432,8 @@ fn populate_with_wasi( } #[cfg(feature = "wasi-nn")] { - use std::cell::RefCell; - use std::rc::Rc; - let wasi_nn = WasiNn::new(linker.store(), Rc::new(RefCell::new(WasiNnCtx::new()?))); - wasi_nn.add_to_linker(linker)?; + wasmtime_wasi_nn::add_wasi_nn_to_linker(linker)?; + store.data_mut().wasi_nn = Some(WasiNnCtx::new()?); } } @@ -389,14 +444,8 @@ fn populate_with_wasi( } #[cfg(feature = "wasi-crypto")] { - use std::cell::RefCell; - use std::rc::Rc; - let cx_crypto = Rc::new(RefCell::new(WasiCryptoCtx::new())); - WasiCryptoCommon::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?; - WasiCryptoAsymmetricCommon::new(linker.store(), cx_crypto.clone()) - .add_to_linker(linker)?; - WasiCryptoSignatures::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?; - WasiCryptoSymmetric::new(linker.store(), cx_crypto).add_to_linker(linker)?; + wasmtime_wasi_crypto::add_to_linker(linker)?; + store.data_mut().wasi_crypto = Some(WasiCryptoCtx::new()); } } diff --git a/src/commands/wast.rs b/src/commands/wast.rs index 08ad8e0a2ab2..5baf7e1b0d6a 100644 --- a/src/commands/wast.rs +++ b/src/commands/wast.rs @@ -36,7 +36,7 @@ impl WastCommand { self.common.init_logging(); let config = self.common.config(None)?; - let store = Store::new(&Engine::new(&config)?); + let store = Store::new(&Engine::new(&config)?, ()); let mut wast_context = WastContext::new(store); wast_context diff --git a/tests/all/async_functions.rs b/tests/all/async_functions.rs index d065fd4f3cfb..c9a1a1deda0b 100644 --- a/tests/all/async_functions.rs +++ b/tests/all/async_functions.rs @@ -1,114 +1,103 @@ -use std::cell::Cell; -use std::cell::RefCell; +use anyhow::Result; use std::future::Future; use std::pin::Pin; -use std::rc::Rc; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use wasmtime::*; -fn async_store() -> Store { - Store::new(&Engine::new(Config::new().async_support(true)).unwrap()) +fn async_store() -> Store<()> { + Store::new(&Engine::new(Config::new().async_support(true)).unwrap(), ()) } -fn run_smoke_test(func: &Func) { - run(func.call_async(&[])).unwrap(); - run(func.call_async(&[])).unwrap(); - let future1 = func.call_async(&[]); - let future2 = func.call_async(&[]); - run(future2).unwrap(); - run(future1).unwrap(); +fn run_smoke_test(store: &mut Store<()>, func: Func) { + run(func.call_async(&mut *store, &[])).unwrap(); + run(func.call_async(&mut *store, &[])).unwrap(); } -fn run_smoke_typed_test(func: &Func) { - let func = func.typed::<(), ()>().unwrap(); - run(func.call_async(())).unwrap(); - run(func.call_async(())).unwrap(); - let future1 = func.call_async(()); - let future2 = func.call_async(()); - run(future2).unwrap(); - run(future1).unwrap(); +fn run_smoke_typed_test(store: &mut Store<()>, func: Func) { + let func = func.typed::<(), (), _>(&store).unwrap(); + run(func.call_async(&mut *store, ())).unwrap(); + run(func.call_async(&mut *store, ())).unwrap(); } #[test] fn smoke() { - let store = async_store(); + let mut store = async_store(); let func = Func::new_async( - &store, + &mut store, FuncType::new(None, None), - (), - move |_caller, _state, _params, _results| Box::new(async { Ok(()) }), + move |_caller, _params, _results| Box::new(async { Ok(()) }), ); - run_smoke_test(&func); - run_smoke_typed_test(&func); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); - let func = Func::wrap0_async(&store, (), move |_caller: Caller<'_>, _state| { - Box::new(async { Ok(()) }) - }); - run_smoke_test(&func); - run_smoke_typed_test(&func); + let func = Func::wrap0_async(&mut store, move |_caller| Box::new(async { Ok(()) })); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); } #[test] -fn smoke_host_func() { - let mut config = Config::new(); - config.async_support(true); - config.define_host_func_async( +fn smoke_host_func() -> Result<()> { + let mut store = async_store(); + let mut linker = Linker::new(store.engine()); + + linker.func_new_async( "", "first", FuncType::new(None, None), move |_caller, _params, _results| Box::new(async { Ok(()) }), - ); - config.wrap0_host_func_async("", "second", move |_caller: Caller<'_>| { - Box::new(async { Ok(()) }) - }); + )?; - let store = Store::new(&Engine::new(&config).unwrap()); + linker.func_wrap0_async("", "second", move |_caller| Box::new(async { Ok(()) }))?; - let func = store - .get_host_func("", "first") - .expect("expected host function"); - run_smoke_test(&func); - run_smoke_typed_test(&func); + let func = linker + .get_one_by_name(&mut store, "", Some("first"))? + .into_func() + .unwrap(); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); + + let func = linker + .get_one_by_name(&mut store, "", Some("second"))? + .into_func() + .unwrap(); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); - let func = store - .get_host_func("", "second") - .expect("expected host function"); - run_smoke_test(&func); - run_smoke_typed_test(&func); + Ok(()) } #[test] fn smoke_with_suspension() { - let store = async_store(); + let mut store = async_store(); let func = Func::new_async( - &store, + &mut store, FuncType::new(None, None), - (), - move |_caller, _state, _params, _results| { + move |_caller, _params, _results| { Box::new(async { PendingOnce::default().await; Ok(()) }) }, ); - run_smoke_test(&func); - run_smoke_typed_test(&func); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); - let func = Func::wrap0_async(&store, (), move |_caller: Caller<'_>, _state| { + let func = Func::wrap0_async(&mut store, move |_caller| { Box::new(async { PendingOnce::default().await; Ok(()) }) }); - run_smoke_test(&func); - run_smoke_typed_test(&func); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); } #[test] -fn smoke_host_func_with_suspension() { - let mut config = Config::new(); - config.async_support(true); - config.define_host_func_async( +fn smoke_host_func_with_suspension() -> Result<()> { + let mut store = async_store(); + let mut linker = Linker::new(store.engine()); + + linker.func_new_async( "", "first", FuncType::new(None, None), @@ -118,56 +107,55 @@ fn smoke_host_func_with_suspension() { Ok(()) }) }, - ); - config.wrap0_host_func_async("", "second", move |_caller: Caller<'_>| { + )?; + + linker.func_wrap0_async("", "second", move |_caller| { Box::new(async { PendingOnce::default().await; Ok(()) }) - }); + })?; - let store = Store::new(&Engine::new(&config).unwrap()); + let func = linker + .get_one_by_name(&mut store, "", Some("first"))? + .into_func() + .unwrap(); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); - let func = store - .get_host_func("", "first") - .expect("expected host function"); - run_smoke_test(&func); - run_smoke_typed_test(&func); + let func = linker + .get_one_by_name(&mut store, "", Some("second"))? + .into_func() + .unwrap(); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); - let func = store - .get_host_func("", "second") - .expect("expected host function"); - run_smoke_test(&func); - run_smoke_typed_test(&func); + Ok(()) } #[test] fn recursive_call() { - let store = async_store(); - let async_wasm_func = Rc::new(Func::new_async( - &store, + let mut store = async_store(); + let async_wasm_func = Func::new_async( + &mut store, FuncType::new(None, None), - (), - |_caller, _state, _params, _results| { + |_caller, _params, _results| { Box::new(async { PendingOnce::default().await; Ok(()) }) }, - )); - let weak = Rc::downgrade(&async_wasm_func); + ); // Create an imported function which recursively invokes another wasm // function asynchronously, although this one is just our own host function // which suffices for this test. let func2 = Func::new_async( - &store, + &mut store, FuncType::new(None, None), - (), - move |_caller, _state, _params, _results| { - let async_wasm_func = weak.upgrade().unwrap(); + move |mut caller, _params, _results| { Box::new(async move { - async_wasm_func.call_async(&[]).await?; + async_wasm_func.call_async(&mut caller, &[]).await?; Ok(()) }) }, @@ -190,16 +178,16 @@ fn recursive_call() { .unwrap(); run(async { - let instance = Instance::new_async(&store, &module, &[func2.into()]).await?; - let func = instance.get_func("").unwrap(); - func.call_async(&[]).await + let instance = Instance::new_async(&mut store, &module, &[func2.into()]).await?; + let func = instance.get_func(&mut store, "").unwrap(); + func.call_async(&mut store, &[]).await }) .unwrap(); } #[test] fn suspend_while_suspending() { - let store = async_store(); + let mut store = async_store(); // Create a synchronous function which calls our asynchronous function and // runs it locally. This shouldn't generally happen but we know everything @@ -208,19 +196,16 @@ fn suspend_while_suspending() { // The purpose of this test is intended to stress various cases in how // we manage pointers in ways that are not necessarily common but are still // possible in safe code. - let async_thunk = Rc::new(Func::new_async( - &store, + let async_thunk = Func::new_async( + &mut store, FuncType::new(None, None), - (), - |_caller, _state, _params, _results| Box::new(async { Ok(()) }), - )); - let weak = Rc::downgrade(&async_thunk); + |_caller, _params, _results| Box::new(async { Ok(()) }), + ); let sync_call_async_thunk = Func::new( - &store, + &mut store, FuncType::new(None, None), - move |_caller, _params, _results| { - let async_thunk = weak.upgrade().unwrap(); - run(async_thunk.call_async(&[]))?; + move |mut caller, _params, _results| { + run(async_thunk.call_async(&mut caller, &[]))?; Ok(()) }, ); @@ -228,10 +213,9 @@ fn suspend_while_suspending() { // A small async function that simply awaits once to pump the loops and // then finishes. let async_import = Func::new_async( - &store, + &mut store, FuncType::new(None, None), - (), - move |_caller, _state, _params, _results| { + move |_caller, _params, _results| { Box::new(async move { PendingOnce::default().await; Ok(()) @@ -255,31 +239,28 @@ fn suspend_while_suspending() { .unwrap(); run(async { let instance = Instance::new_async( - &store, + &mut store, &module, &[sync_call_async_thunk.into(), async_import.into()], ) .await?; - let func = instance.get_func("").unwrap(); - func.call_async(&[]).await + let func = instance.get_func(&mut store, "").unwrap(); + func.call_async(&mut store, &[]).await }) .unwrap(); } #[test] fn cancel_during_run() { - let store = async_store(); - let state = Rc::new(Cell::new(0)); - let state2 = state.clone(); + let mut store = Store::new(&Engine::new(Config::new().async_support(true)).unwrap(), 0); let async_thunk = Func::new_async( - &store, + &mut store, FuncType::new(None, None), - (), - move |_caller, _state, _params, _results| { - assert_eq!(state2.get(), 0); - state2.set(1); - let dtor = SetOnDrop(state2.clone()); + move |mut caller, _params, _results| { + assert_eq!(*caller.data(), 0); + *caller.data_mut() = 1; + let dtor = SetOnDrop(caller); Box::new(async move { drop(&dtor); PendingOnce::default().await; @@ -288,12 +269,11 @@ fn cancel_during_run() { }, ); // Shouldn't have called anything yet... - assert_eq!(state.get(), 0); + assert_eq!(*store.data(), 0); // Create our future, but as per async conventions this still doesn't // actually do anything. No wasm or host function has been called yet. - let mut future = Pin::from(Box::new(async_thunk.call_async(&[]))); - assert_eq!(state.get(), 0); + let mut future = Pin::from(Box::new(async_thunk.call_async(&mut store, &[]))); // Push the future forward one tick, which actually runs the host code in // our async func. Our future is designed to be pending once, however. @@ -301,19 +281,18 @@ fn cancel_during_run() { .as_mut() .poll(&mut Context::from_waker(&dummy_waker())); assert!(poll.is_pending()); - assert_eq!(state.get(), 1); // Now that our future is running (on a separate, now-suspended fiber), drop // the future and that should deallocate all the Rust bits as well. drop(future); - assert_eq!(state.get(), 2); + assert_eq!(*store.data(), 2); - struct SetOnDrop(Rc>); + struct SetOnDrop<'a>(Caller<'a, usize>); - impl Drop for SetOnDrop { + impl Drop for SetOnDrop<'_> { fn drop(&mut self) { - assert_eq!(self.0.get(), 1); - self.0.set(2); + assert_eq!(*self.0.data(), 1); + *self.0.data_mut() = 2; } } } @@ -374,7 +353,7 @@ fn dummy_waker() -> Waker { #[test] fn iloop_with_fuel() { let engine = Engine::new(Config::new().async_support(true).consume_fuel(true)).unwrap(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); store.out_of_fuel_async_yield(1_000, 10); let module = Module::new( &engine, @@ -386,7 +365,7 @@ fn iloop_with_fuel() { ", ) .unwrap(); - let instance = Instance::new_async(&store, &module, &[]); + let instance = Instance::new_async(&mut store, &module, &[]); let mut f = Pin::from(Box::new(instance)); let waker = dummy_waker(); let mut cx = Context::from_waker(&waker); @@ -408,7 +387,7 @@ fn iloop_with_fuel() { #[test] fn fuel_eventually_finishes() { let engine = Engine::new(Config::new().async_support(true).consume_fuel(true)).unwrap(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); store.out_of_fuel_async_yield(u32::max_value(), 10); let module = Module::new( &engine, @@ -430,7 +409,7 @@ fn fuel_eventually_finishes() { ", ) .unwrap(); - let instance = Instance::new_async(&store, &module, &[]); + let instance = Instance::new_async(&mut store, &module, &[]); run(instance).unwrap(); } @@ -452,20 +431,19 @@ fn async_with_pooling_stacks() { }); let engine = Engine::new(&config).unwrap(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let func = Func::new_async( - &store, + &mut store, FuncType::new(None, None), - (), - move |_caller, _state, _params, _results| Box::new(async { Ok(()) }), + move |_caller, _params, _results| Box::new(async { Ok(()) }), ); - run_smoke_test(&func); - run_smoke_typed_test(&func); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); } #[test] -fn async_host_func_with_pooling_stacks() { +fn async_host_func_with_pooling_stacks() -> Result<()> { let mut config = Config::new(); config.async_support(true); config.allocation_strategy(InstanceAllocationStrategy::Pooling { @@ -481,32 +459,26 @@ fn async_host_func_with_pooling_stacks() { }, }); - config.define_host_func_async( + let mut store = Store::new(&Engine::new(&config)?, ()); + let mut linker = Linker::new(store.engine()); + linker.func_new_async( "", "", FuncType::new(None, None), move |_caller, _params, _results| Box::new(async { Ok(()) }), - ); + )?; - let store = Store::new(&Engine::new(&config).unwrap()); - let func = store.get_host_func("", "").expect("expected host function"); - - run_smoke_test(&func); - run_smoke_typed_test(&func); + let func = linker + .get_one_by_name(&mut store, "", Some(""))? + .into_func() + .unwrap(); + run_smoke_test(&mut store, func); + run_smoke_typed_test(&mut store, func); + Ok(()) } -fn execute_across_threads(future: F) { - struct UnsafeSend(T); - unsafe impl Send for UnsafeSend {} - - impl Future for UnsafeSend { - type Output = T::Output; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { self.map_unchecked_mut(|p| &mut p.0).poll(cx) } - } - } - - let mut future = Pin::from(Box::new(UnsafeSend(future))); +fn execute_across_threads(future: F) { + let mut future = Pin::from(Box::new(future)); let poll = future .as_mut() .poll(&mut Context::from_waker(&dummy_waker())); @@ -527,7 +499,7 @@ fn resume_separate_thread() { // This test will poll the following future on two threads. Simulating a // trap requires accessing TLS info, so that should be preserved correctly. execute_across_threads(async { - let store = async_store(); + let mut store = async_store(); let module = Module::new( store.engine(), " @@ -538,13 +510,13 @@ fn resume_separate_thread() { ", ) .unwrap(); - let func = Func::wrap0_async(&store, (), |_, _| { + let func = Func::wrap0_async(&mut store, |_| { Box::new(async { PendingOnce::default().await; Err::<(), _>(wasmtime::Trap::new("test")) }) }); - let result = Instance::new_async(&store, &module, &[func.into()]).await; + let result = Instance::new_async(&mut store, &module, &[func.into()]).await; assert!(result.is_err()); }); } @@ -555,7 +527,7 @@ fn resume_separate_thread2() { // signal requires looking up TLS information to determine whether it's a // trap to handle or not, so that must be preserved correctly across threads. execute_across_threads(async { - let store = async_store(); + let mut store = async_store(); let module = Module::new( store.engine(), " @@ -569,10 +541,10 @@ fn resume_separate_thread2() { ", ) .unwrap(); - let func = Func::wrap0_async(&store, (), |_, _| { + let func = Func::wrap0_async(&mut store, |_| { Box::new(async { PendingOnce::default().await }) }); - let result = Instance::new_async(&store, &module, &[func.into()]).await; + let result = Instance::new_async(&mut store, &module, &[func.into()]).await; assert!(result.is_err()); }); } @@ -587,30 +559,28 @@ fn resume_separate_thread3() { // "enter into wasm" semantics since it's just calling a trampoline. In this // situation we'll set up the TLS info so it's in place while the body of // the function executes... - let store = Store::default(); - let storage = Rc::new(RefCell::new(None)); - let storage2 = storage.clone(); - let f = Func::wrap(&store, move || { + let mut store = Store::new(&Engine::default(), None); + let f = Func::wrap(&mut store, move |mut caller: Caller<'_, _>| { // ... and the execution of this host-defined function (while the TLS // info is initialized), will set up a recursive call into wasm. This // recursive call will be done asynchronously so we can suspend it // halfway through. let f = async { - let store = async_store(); + let mut store = async_store(); let module = Module::new( store.engine(), " - (module - (import \"\" \"\" (func)) - (start 0) - ) - ", + (module + (import \"\" \"\" (func)) + (start 0) + ) + ", ) .unwrap(); - let func = Func::wrap0_async(&store, (), |_, _| { + let func = Func::wrap0_async(&mut store, |_| { Box::new(async { PendingOnce::default().await }) }); - drop(Instance::new_async(&store, &module, &[func.into()]).await); + drop(Instance::new_async(&mut store, &module, &[func.into()]).await); unreachable!() }; let mut future = Pin::from(Box::new(f)); @@ -626,12 +596,12 @@ fn resume_separate_thread3() { // here then we would reenter the future's suspended stack to clean it // up, which would do more alterations of TLS information we're not // testing here. - *storage2.borrow_mut() = Some(future); + *caller.data_mut() = Some(future); // ... all in all this function will need access to the original TLS // information to raise the trap. This TLS information should be // restored even though the asynchronous execution is suspended. Err::<(), _>(wasmtime::Trap::new("")) }); - assert!(f.call(&[]).is_err()); + assert!(f.call(&mut store, &[]).is_err()); } diff --git a/tests/all/custom_signal_handler.rs b/tests/all/custom_signal_handler.rs index 2d4243123eff..680bd50540c4 100644 --- a/tests/all/custom_signal_handler.rs +++ b/tests/all/custom_signal_handler.rs @@ -1,8 +1,8 @@ #[cfg(target_os = "linux")] mod tests { use anyhow::Result; - use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; use wasmtime::unix::StoreExt; use wasmtime::*; @@ -41,16 +41,18 @@ mod tests { ) "#; - fn invoke_export(instance: &Instance, func_name: &str) -> Result { - let ret = instance.get_typed_func::<(), i32>(func_name)?.call(())?; + fn invoke_export(store: &mut Store<()>, instance: Instance, func_name: &str) -> Result { + let ret = instance + .get_typed_func::<(), i32, _>(&mut *store, func_name)? + .call(store, ())?; Ok(ret) } // Locate "memory" export, get base address and size and set memory protection to PROT_NONE - fn set_up_memory(instance: &Instance) -> (*mut u8, usize) { - let mem_export = instance.get_memory("memory").unwrap(); - let base = mem_export.data_ptr(); - let length = mem_export.data_size(); + fn set_up_memory(store: &mut Store<()>, instance: Instance) -> (usize, usize) { + let mem_export = instance.get_memory(&mut *store, "memory").unwrap(); + let base = mem_export.data_ptr(&store); + let length = mem_export.data_size(&store); // So we can later trigger SIGSEGV by performing a read unsafe { @@ -59,11 +61,11 @@ mod tests { println!("memory: base={:?}, length={}", base, length); - (base, length) + (base as usize, length) } fn handle_sigsegv( - base: *mut u8, + base: usize, length: usize, signum: libc::c_int, siginfo: *const libc::siginfo_t, @@ -90,15 +92,15 @@ mod tests { } } - fn make_externs(store: &Store, module: &Module) -> Vec { + fn make_externs(store: &mut Store<()>, module: &Module) -> Vec { module .imports() .map(|import| { assert_eq!(Some("hostcall_read"), import.name()); - let func = Func::wrap(&store, { - move |caller: Caller<'_>| { + let func = Func::wrap(&mut *store, { + move |mut caller: Caller<'_, _>| { let mem = caller.get_export("memory").unwrap().into_memory().unwrap(); - let memory = unsafe { mem.data_unchecked_mut() }; + let memory = mem.data(&caller); use std::convert::TryInto; i32::from_le_bytes(memory[0..4].try_into().unwrap()) } @@ -112,20 +114,21 @@ mod tests { // hostcall can be handled. #[test] fn test_custom_signal_handler_single_instance_hostcall() -> Result<()> { - let engine = Engine::new(&Config::default())?; - let store = Store::new(&engine); + let engine = Engine::default(); + let mut store = Store::new(&engine, ()); let module = Module::new(&engine, WAT1)?; - let instance = Instance::new(&store, &module, &make_externs(&store, &module))?; + let externs = make_externs(&mut store, &module); + let instance = Instance::new(&mut store, &module, &externs)?; - let (base, length) = set_up_memory(&instance); + let (base, length) = set_up_memory(&mut store, instance); unsafe { store.set_signal_handler(move |signum, siginfo, _| { handle_sigsegv(base, length, signum, siginfo) }); } println!("calling hostcall_read..."); - let result = invoke_export(&instance, "hostcall_read").unwrap(); + let result = invoke_export(&mut store, instance, "hostcall_read").unwrap(); assert_eq!(123, result); Ok(()) } @@ -133,12 +136,13 @@ mod tests { #[test] fn test_custom_signal_handler_single_instance() -> Result<()> { let engine = Engine::new(&Config::default())?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let module = Module::new(&engine, WAT1)?; - let instance = Instance::new(&store, &module, &make_externs(&store, &module))?; + let externs = make_externs(&mut store, &module); + let instance = Instance::new(&mut store, &module, &externs)?; - let (base, length) = set_up_memory(&instance); + let (base, length) = set_up_memory(&mut store, instance); unsafe { store.set_signal_handler(move |signum, siginfo, _| { handle_sigsegv(base, length, signum, siginfo) @@ -148,13 +152,13 @@ mod tests { // these invoke wasmtime_call_trampoline from action.rs { println!("calling read..."); - let result = invoke_export(&instance, "read").expect("read succeeded"); + let result = invoke_export(&mut store, instance, "read").expect("read succeeded"); assert_eq!(123, result); } { println!("calling read_out_of_bounds..."); - let trap = invoke_export(&instance, "read_out_of_bounds") + let trap = invoke_export(&mut store, instance, "read_out_of_bounds") .unwrap_err() .downcast::()?; assert!( @@ -167,17 +171,19 @@ mod tests { // these invoke wasmtime_call_trampoline from callable.rs { - let read_func = instance.get_typed_func::<(), i32>("read")?; + let read_func = instance.get_typed_func::<(), i32, _>(&mut store, "read")?; println!("calling read..."); - let result = read_func.call(()).expect("expected function not to trap"); + let result = read_func + .call(&mut store, ()) + .expect("expected function not to trap"); assert_eq!(123i32, result); } { let read_out_of_bounds_func = - instance.get_typed_func::<(), i32>("read_out_of_bounds")?; + instance.get_typed_func::<(), i32, _>(&mut store, "read_out_of_bounds")?; println!("calling read_out_of_bounds..."); - let trap = read_out_of_bounds_func.call(()).unwrap_err(); + let trap = read_out_of_bounds_func.call(&mut store, ()).unwrap_err(); assert!(trap .to_string() .contains("wasm trap: out of bounds memory access")); @@ -187,17 +193,18 @@ mod tests { #[test] fn test_custom_signal_handler_multiple_instances() -> Result<()> { - let engine = Engine::new(&Config::default())?; - let store = Store::new(&engine); + let engine = Engine::default(); + let mut store = Store::new(&engine, ()); let module = Module::new(&engine, WAT1)?; // Set up multiple instances - let instance1 = Instance::new(&store, &module, &make_externs(&store, &module))?; - let instance1_handler_triggered = Rc::new(AtomicBool::new(false)); + let externs = make_externs(&mut store, &module); + let instance1 = Instance::new(&mut store, &module, &externs)?; + let instance1_handler_triggered = Arc::new(AtomicBool::new(false)); unsafe { - let (base1, length1) = set_up_memory(&instance1); + let (base1, length1) = set_up_memory(&mut store, instance1); store.set_signal_handler({ let instance1_handler_triggered = instance1_handler_triggered.clone(); @@ -222,11 +229,12 @@ mod tests { // First instance1 { - let mut exports1 = instance1.exports(); + let mut exports1 = instance1.exports(&mut store); assert!(exports1.next().is_some()); + drop(exports1); println!("calling instance1.read..."); - let result = invoke_export(&instance1, "read").expect("read succeeded"); + let result = invoke_export(&mut store, instance1, "read").expect("read succeeded"); assert_eq!(123, result); assert_eq!( instance1_handler_triggered.load(Ordering::SeqCst), @@ -235,12 +243,13 @@ mod tests { ); } - let instance2 = Instance::new(&store, &module, &make_externs(&store, &module)) - .expect("failed to instantiate module"); - let instance2_handler_triggered = Rc::new(AtomicBool::new(false)); + let externs = make_externs(&mut store, &module); + let instance2 = + Instance::new(&mut store, &module, &externs).expect("failed to instantiate module"); + let instance2_handler_triggered = Arc::new(AtomicBool::new(false)); unsafe { - let (base2, length2) = set_up_memory(&instance2); + let (base2, length2) = set_up_memory(&mut store, instance2); store.set_signal_handler({ let instance2_handler_triggered = instance2_handler_triggered.clone(); @@ -263,11 +272,12 @@ mod tests { // And then instance2 { - let mut exports2 = instance2.exports(); + let mut exports2 = instance2.exports(&mut store); assert!(exports2.next().is_some()); + drop(exports2); println!("calling instance2.read..."); - let result = invoke_export(&instance2, "read").expect("read succeeded"); + let result = invoke_export(&mut store, instance2, "read").expect("read succeeded"); assert_eq!(123, result); assert_eq!( instance2_handler_triggered.load(Ordering::SeqCst), @@ -280,13 +290,14 @@ mod tests { #[test] fn test_custom_signal_handler_instance_calling_another_instance() -> Result<()> { - let engine = Engine::new(&Config::default())?; - let store = Store::new(&engine); + let engine = Engine::default(); + let mut store = Store::new(&engine, ()); // instance1 which defines 'read' let module1 = Module::new(&engine, WAT1)?; - let instance1 = Instance::new(&store, &module1, &make_externs(&store, &module1))?; - let (base1, length1) = set_up_memory(&instance1); + let externs = make_externs(&mut store, &module1); + let instance1 = Instance::new(&mut store, &module1, &externs)?; + let (base1, length1) = set_up_memory(&mut store, instance1); unsafe { store.set_signal_handler(move |signum, siginfo, _| { println!("instance1"); @@ -294,12 +305,13 @@ mod tests { }); } - let mut instance1_exports = instance1.exports(); - let instance1_read = instance1_exports.next().unwrap(); + let mut instance1_exports = instance1.exports(&mut store); + let instance1_read = instance1_exports.next().unwrap().clone().into_extern(); + drop(instance1_exports); // instance2 which calls 'instance1.read' let module2 = Module::new(&engine, WAT2)?; - let instance2 = Instance::new(&store, &module2, &[instance1_read.into_extern()])?; + let instance2 = Instance::new(&mut store, &module2, &[instance1_read])?; // since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle // SIGSEGV originating from within the memory of instance1 unsafe { @@ -309,7 +321,7 @@ mod tests { } println!("calling instance2.run"); - let result = invoke_export(&instance2, "run")?; + let result = invoke_export(&mut store, instance2, "run")?; assert_eq!(123, result); Ok(()) } diff --git a/tests/all/externals.rs b/tests/all/externals.rs index 0e26166f0715..dede68bf83a1 100644 --- a/tests/all/externals.rs +++ b/tests/all/externals.rs @@ -2,166 +2,172 @@ use wasmtime::*; #[test] fn bad_globals() { + let mut store = Store::<()>::default(); let ty = GlobalType::new(ValType::I32, Mutability::Var); - assert!(Global::new(&Store::default(), ty.clone(), Val::I64(0)).is_err()); - assert!(Global::new(&Store::default(), ty.clone(), Val::F32(0)).is_err()); - assert!(Global::new(&Store::default(), ty.clone(), Val::F64(0)).is_err()); + assert!(Global::new(&mut store, ty.clone(), Val::I64(0)).is_err()); + assert!(Global::new(&mut store, ty.clone(), Val::F32(0)).is_err()); + assert!(Global::new(&mut store, ty.clone(), Val::F64(0)).is_err()); let ty = GlobalType::new(ValType::I32, Mutability::Const); - let g = Global::new(&Store::default(), ty.clone(), Val::I32(0)).unwrap(); - assert!(g.set(Val::I32(1)).is_err()); + let g = Global::new(&mut store, ty.clone(), Val::I32(0)).unwrap(); + assert!(g.set(&mut store, Val::I32(1)).is_err()); let ty = GlobalType::new(ValType::I32, Mutability::Var); - let g = Global::new(&Store::default(), ty.clone(), Val::I32(0)).unwrap(); - assert!(g.set(Val::I64(0)).is_err()); + let g = Global::new(&mut store, ty.clone(), Val::I32(0)).unwrap(); + assert!(g.set(&mut store, Val::I64(0)).is_err()); } #[test] fn bad_tables() { + let mut store = Store::<()>::default(); + // i32 not supported yet let ty = TableType::new(ValType::I32, Limits::new(0, Some(1))); - assert!(Table::new(&Store::default(), ty.clone(), Val::I32(0)).is_err()); + assert!(Table::new(&mut store, ty.clone(), Val::I32(0)).is_err()); // mismatched initializer let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1))); - assert!(Table::new(&Store::default(), ty.clone(), Val::I32(0)).is_err()); + assert!(Table::new(&mut store, ty.clone(), Val::I32(0)).is_err()); // get out of bounds let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1))); - let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap(); - assert!(t.get(0).is_none()); - assert!(t.get(u32::max_value()).is_none()); + let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap(); + assert!(t.get(&mut store, 0).is_none()); + assert!(t.get(&mut store, u32::max_value()).is_none()); // set out of bounds or wrong type let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1))); - let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap(); - assert!(t.set(0, Val::I32(0)).is_err()); - assert!(t.set(0, Val::FuncRef(None)).is_ok()); - assert!(t.set(1, Val::FuncRef(None)).is_err()); + let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap(); + assert!(t.set(&mut store, 0, Val::I32(0)).is_err()); + assert!(t.set(&mut store, 0, Val::FuncRef(None)).is_ok()); + assert!(t.set(&mut store, 1, Val::FuncRef(None)).is_err()); // grow beyond max let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1))); - let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap(); - assert!(t.grow(0, Val::FuncRef(None)).is_ok()); - assert!(t.grow(1, Val::FuncRef(None)).is_err()); - assert_eq!(t.size(), 1); + let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap(); + assert!(t.grow(&mut store, 0, Val::FuncRef(None)).is_ok()); + assert!(t.grow(&mut store, 1, Val::FuncRef(None)).is_err()); + assert_eq!(t.size(&store), 1); // grow wrong type let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(2))); - let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap(); - assert!(t.grow(1, Val::I32(0)).is_err()); - assert_eq!(t.size(), 1); + let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap(); + assert!(t.grow(&mut store, 1, Val::I32(0)).is_err()); + assert_eq!(t.size(&store), 1); } -#[test] -fn cross_store() -> anyhow::Result<()> { - let mut cfg = Config::new(); - cfg.wasm_reference_types(true); - let engine = Engine::new(&cfg)?; - let store1 = Store::new(&engine); - let store2 = Store::new(&engine); - - // ============ Cross-store instantiation ============== - - let func = Func::wrap(&store2, || {}); - let ty = GlobalType::new(ValType::I32, Mutability::Const); - let global = Global::new(&store2, ty, Val::I32(0))?; - let ty = MemoryType::new(Limits::new(1, None)); - let memory = Memory::new(&store2, ty)?; - let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); - let table = Table::new(&store2, ty, Val::FuncRef(None))?; - - let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?; - assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err()); - - let need_global = Module::new(&engine, r#"(module (import "" "" (global i32)))"#)?; - assert!(Instance::new(&store1, &need_global, &[global.into()]).is_err()); - - let need_table = Module::new(&engine, r#"(module (import "" "" (table 1 funcref)))"#)?; - assert!(Instance::new(&store1, &need_table, &[table.into()]).is_err()); - - let need_memory = Module::new(&engine, r#"(module (import "" "" (memory 1)))"#)?; - assert!(Instance::new(&store1, &need_memory, &[memory.into()]).is_err()); - - // ============ Cross-store globals ============== - - let store1val = Val::FuncRef(Some(Func::wrap(&store1, || {}))); - let store2val = Val::FuncRef(Some(Func::wrap(&store2, || {}))); - - let ty = GlobalType::new(ValType::FuncRef, Mutability::Var); - assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err()); - if let Ok(g) = Global::new(&store2, ty.clone(), store2val.clone()) { - assert!(g.set(store1val.clone()).is_err()); - } - - // ============ Cross-store tables ============== - - let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); - assert!(Table::new(&store2, ty.clone(), store1val.clone()).is_err()); - let t1 = Table::new(&store2, ty.clone(), store2val.clone())?; - assert!(t1.set(0, store1val.clone()).is_err()); - assert!(t1.grow(0, store1val.clone()).is_err()); - assert!(t1.fill(0, store1val.clone(), 1).is_err()); - let t2 = Table::new(&store1, ty.clone(), store1val.clone())?; - assert!(Table::copy(&t1, 0, &t2, 0, 0).is_err()); - - // ============ Cross-store funcs ============== - - let module = Module::new(&engine, r#"(module (func (export "f") (param funcref)))"#)?; - let s1_inst = Instance::new(&store1, &module, &[])?; - let s2_inst = Instance::new(&store2, &module, &[])?; - let s1_f = s1_inst.get_func("f").unwrap(); - let s2_f = s2_inst.get_func("f").unwrap(); - - assert!(s1_f.call(&[Val::FuncRef(None)]).is_ok()); - assert!(s2_f.call(&[Val::FuncRef(None)]).is_ok()); - assert!(s1_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_ok()); - assert!(s1_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_err()); - assert!(s2_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_err()); - assert!(s2_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_ok()); - - let s1_f_t = s1_f.typed::, ()>()?; - let s2_f_t = s2_f.typed::, ()>()?; - - assert!(s1_f_t.call(None).is_ok()); - assert!(s2_f_t.call(None).is_ok()); - assert!(s1_f_t.call(Some(s1_f.clone())).is_ok()); - assert!(s1_f_t.call(Some(s2_f.clone())).is_err()); - assert!(s2_f_t.call(Some(s1_f.clone())).is_err()); - assert!(s2_f_t.call(Some(s2_f.clone())).is_ok()); - - Ok(()) -} +// #[test] +// fn cross_store() -> anyhow::Result<()> { +// let mut cfg = Config::new(); +// cfg.wasm_reference_types(true); +// let engine = Engine::new(&cfg)?; +// let store1 = Store::new(&engine); +// let store2 = Store::new(&engine); + +// // ============ Cross-store instantiation ============== + +// let func = Func::wrap(&store2, || {}); +// let ty = GlobalType::new(ValType::I32, Mutability::Const); +// let global = Global::new(&store2, ty, Val::I32(0))?; +// let ty = MemoryType::new(Limits::new(1, None)); +// let memory = Memory::new(&store2, ty)?; +// let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); +// let table = Table::new(&store2, ty, Val::FuncRef(None))?; + +// let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?; +// assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err()); + +// let need_global = Module::new(&engine, r#"(module (import "" "" (global i32)))"#)?; +// assert!(Instance::new(&store1, &need_global, &[global.into()]).is_err()); + +// let need_table = Module::new(&engine, r#"(module (import "" "" (table 1 funcref)))"#)?; +// assert!(Instance::new(&store1, &need_table, &[table.into()]).is_err()); + +// let need_memory = Module::new(&engine, r#"(module (import "" "" (memory 1)))"#)?; +// assert!(Instance::new(&store1, &need_memory, &[memory.into()]).is_err()); + +// // ============ Cross-store globals ============== + +// let store1val = Val::FuncRef(Some(Func::wrap(&store1, || {}))); +// let store2val = Val::FuncRef(Some(Func::wrap(&store2, || {}))); + +// let ty = GlobalType::new(ValType::FuncRef, Mutability::Var); +// assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err()); +// if let Ok(g) = Global::new(&store2, ty.clone(), store2val.clone()) { +// assert!(g.set(store1val.clone()).is_err()); +// } + +// // ============ Cross-store tables ============== + +// let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); +// assert!(Table::new(&store2, ty.clone(), store1val.clone()).is_err()); +// let t1 = Table::new(&store2, ty.clone(), store2val.clone())?; +// assert!(t1.set(0, store1val.clone()).is_err()); +// assert!(t1.grow(0, store1val.clone()).is_err()); +// assert!(t1.fill(0, store1val.clone(), 1).is_err()); +// let t2 = Table::new(&store1, ty.clone(), store1val.clone())?; +// assert!(Table::copy(&t1, 0, &t2, 0, 0).is_err()); + +// // ============ Cross-store funcs ============== + +// let module = Module::new(&engine, r#"(module (func (export "f") (param funcref)))"#)?; +// let s1_inst = Instance::new(&store1, &module, &[])?; +// let s2_inst = Instance::new(&store2, &module, &[])?; +// let s1_f = s1_inst.get_func("f").unwrap(); +// let s2_f = s2_inst.get_func("f").unwrap(); + +// assert!(s1_f.call(&[Val::FuncRef(None)]).is_ok()); +// assert!(s2_f.call(&[Val::FuncRef(None)]).is_ok()); +// assert!(s1_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_ok()); +// assert!(s1_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_err()); +// assert!(s2_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_err()); +// assert!(s2_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_ok()); + +// let s1_f_t = s1_f.typed::, ()>()?; +// let s2_f_t = s2_f.typed::, ()>()?; + +// assert!(s1_f_t.call(None).is_ok()); +// assert!(s2_f_t.call(None).is_ok()); +// assert!(s1_f_t.call(Some(s1_f.clone())).is_ok()); +// assert!(s1_f_t.call(Some(s2_f.clone())).is_err()); +// assert!(s2_f_t.call(Some(s1_f.clone())).is_err()); +// assert!(s2_f_t.call(Some(s2_f.clone())).is_ok()); + +// Ok(()) +// } #[test] fn get_set_externref_globals_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); // Initialize with a null externref. let global = Global::new( - &store, + &mut store, GlobalType::new(ValType::ExternRef, Mutability::Var), Val::ExternRef(None), )?; - assert!(global.get().unwrap_externref().is_none()); + assert!(global.get(&mut store).unwrap_externref().is_none()); - global.set(Val::ExternRef(Some(ExternRef::new("hello".to_string()))))?; - let r = global.get().unwrap_externref().unwrap(); + global.set( + &mut store, + Val::ExternRef(Some(ExternRef::new("hello".to_string()))), + )?; + let r = global.get(&mut store).unwrap_externref().unwrap(); assert!(r.data().is::()); assert_eq!(r.data().downcast_ref::().unwrap(), "hello"); // Initialize with a non-null externref. let global = Global::new( - &store, + &mut store, GlobalType::new(ValType::ExternRef, Mutability::Const), Val::ExternRef(Some(ExternRef::new(42_i32))), )?; - let r = global.get().unwrap_externref().unwrap(); + let r = global.get(&mut store).unwrap_externref().unwrap(); assert!(r.data().is::()); assert_eq!(r.data().downcast_ref::().copied().unwrap(), 42); @@ -173,32 +179,32 @@ fn get_set_funcref_globals_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); - let f = Func::wrap(&store, || {}); + let f = Func::wrap(&mut store, || {}); // Initialize with a null funcref. let global = Global::new( - &store, + &mut store, GlobalType::new(ValType::FuncRef, Mutability::Var), Val::FuncRef(None), )?; - assert!(global.get().unwrap_funcref().is_none()); + assert!(global.get(&mut store).unwrap_funcref().is_none()); - global.set(Val::FuncRef(Some(f.clone())))?; - let f2 = global.get().unwrap_funcref().cloned().unwrap(); - assert_eq!(f.ty(), f2.ty()); + global.set(&mut store, Val::FuncRef(Some(f.clone())))?; + let f2 = global.get(&mut store).unwrap_funcref().cloned().unwrap(); + assert_eq!(f.ty(&store), f2.ty(&store)); // Initialize with a non-null funcref. let global = Global::new( - &store, + &mut store, GlobalType::new(ValType::FuncRef, Mutability::Var), Val::FuncRef(Some(f.clone())), )?; - let f2 = global.get().unwrap_funcref().cloned().unwrap(); - assert_eq!(f.ty(), f2.ty()); + let f2 = global.get(&mut store).unwrap_funcref().cloned().unwrap(); + assert_eq!(f.ty(&store), f2.ty(&store)); Ok(()) } @@ -208,18 +214,15 @@ fn create_get_set_funcref_tables_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let table_ty = TableType::new(ValType::FuncRef, Limits::at_least(10)); - let table = Table::new( - &store, - table_ty, - Val::FuncRef(Some(Func::wrap(&store, || {}))), - )?; + let init = Val::FuncRef(Some(Func::wrap(&mut store, || {}))); + let table = Table::new(&mut store, table_ty, init)?; - assert!(table.get(5).unwrap().unwrap_funcref().is_some()); - table.set(5, Val::FuncRef(None))?; - assert!(table.get(5).unwrap().unwrap_funcref().is_none()); + assert!(table.get(&mut store, 5).unwrap().unwrap_funcref().is_some()); + table.set(&mut store, 5, Val::FuncRef(None))?; + assert!(table.get(&mut store, 5).unwrap().unwrap_funcref().is_none()); Ok(()) } @@ -229,22 +232,23 @@ fn fill_funcref_tables_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let table_ty = TableType::new(ValType::FuncRef, Limits::at_least(10)); - let table = Table::new(&store, table_ty, Val::FuncRef(None))?; + let table = Table::new(&mut store, table_ty, Val::FuncRef(None))?; for i in 0..10 { - assert!(table.get(i).unwrap().unwrap_funcref().is_none()); + assert!(table.get(&mut store, i).unwrap().unwrap_funcref().is_none()); } - table.fill(2, Val::FuncRef(Some(Func::wrap(&store, || {}))), 4)?; + let fill = Val::FuncRef(Some(Func::wrap(&mut store, || {}))); + table.fill(&mut store, 2, fill, 4)?; for i in (0..2).chain(7..10) { - assert!(table.get(i).unwrap().unwrap_funcref().is_none()); + assert!(table.get(&mut store, i).unwrap().unwrap_funcref().is_none()); } for i in 2..6 { - assert!(table.get(i).unwrap().unwrap_funcref().is_some()); + assert!(table.get(&mut store, i).unwrap().unwrap_funcref().is_some()); } Ok(()) @@ -255,14 +259,14 @@ fn grow_funcref_tables_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let table_ty = TableType::new(ValType::FuncRef, Limits::at_least(10)); - let table = Table::new(&store, table_ty, Val::FuncRef(None))?; + let table = Table::new(&mut store, table_ty, Val::FuncRef(None))?; - assert_eq!(table.size(), 10); - table.grow(3, Val::FuncRef(None))?; - assert_eq!(table.size(), 13); + assert_eq!(table.size(&store), 10); + table.grow(&mut store, 3, Val::FuncRef(None))?; + assert_eq!(table.size(&store), 13); Ok(()) } @@ -272,18 +276,18 @@ fn create_get_set_externref_tables_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let table_ty = TableType::new(ValType::ExternRef, Limits::at_least(10)); let table = Table::new( - &store, + &mut store, table_ty, Val::ExternRef(Some(ExternRef::new(42_usize))), )?; assert_eq!( *table - .get(5) + .get(&mut store, 5) .unwrap() .unwrap_externref() .unwrap() @@ -292,8 +296,12 @@ fn create_get_set_externref_tables_via_api() -> anyhow::Result<()> { .unwrap(), 42 ); - table.set(5, Val::ExternRef(None))?; - assert!(table.get(5).unwrap().unwrap_externref().is_none()); + table.set(&mut store, 5, Val::ExternRef(None))?; + assert!(table + .get(&mut store, 5) + .unwrap() + .unwrap_externref() + .is_none()); Ok(()) } @@ -303,24 +311,37 @@ fn fill_externref_tables_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let table_ty = TableType::new(ValType::ExternRef, Limits::at_least(10)); - let table = Table::new(&store, table_ty, Val::ExternRef(None))?; + let table = Table::new(&mut store, table_ty, Val::ExternRef(None))?; for i in 0..10 { - assert!(table.get(i).unwrap().unwrap_externref().is_none()); + assert!(table + .get(&mut store, i) + .unwrap() + .unwrap_externref() + .is_none()); } - table.fill(2, Val::ExternRef(Some(ExternRef::new(42_usize))), 4)?; + table.fill( + &mut store, + 2, + Val::ExternRef(Some(ExternRef::new(42_usize))), + 4, + )?; for i in (0..2).chain(7..10) { - assert!(table.get(i).unwrap().unwrap_externref().is_none()); + assert!(table + .get(&mut store, i) + .unwrap() + .unwrap_externref() + .is_none()); } for i in 2..6 { assert_eq!( *table - .get(i) + .get(&mut store, i) .unwrap() .unwrap_externref() .unwrap() @@ -339,14 +360,14 @@ fn grow_externref_tables_via_api() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); let engine = Engine::new(&cfg)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let table_ty = TableType::new(ValType::ExternRef, Limits::at_least(10)); - let table = Table::new(&store, table_ty, Val::ExternRef(None))?; + let table = Table::new(&mut store, table_ty, Val::ExternRef(None))?; - assert_eq!(table.size(), 10); - table.grow(3, Val::ExternRef(None))?; - assert_eq!(table.size(), 13); + assert_eq!(table.size(&store), 10); + table.grow(&mut store, 3, Val::ExternRef(None))?; + assert_eq!(table.size(&store), 13); Ok(()) } @@ -354,16 +375,17 @@ fn grow_externref_tables_via_api() -> anyhow::Result<()> { #[test] fn read_write_memory_via_api() { let cfg = Config::new(); - let store = Store::new(&Engine::new(&cfg).unwrap()); + let mut store = Store::new(&Engine::new(&cfg).unwrap(), ()); let ty = MemoryType::new(Limits::new(1, None)); - let mem = Memory::new(&store, ty).unwrap(); - mem.grow(1).unwrap(); + let mem = Memory::new(&mut store, ty).unwrap(); + mem.grow(&mut store, 1).unwrap(); let value = b"hello wasm"; - mem.write(mem.data_size() - value.len(), value).unwrap(); + let size = mem.data_size(&store); + mem.write(&mut store, size - value.len(), value).unwrap(); let mut buffer = [0u8; 10]; - mem.read(mem.data_size() - buffer.len(), &mut buffer) + mem.read(&store, mem.data_size(&store) - buffer.len(), &mut buffer) .unwrap(); assert_eq!(value, &buffer); @@ -371,10 +393,11 @@ fn read_write_memory_via_api() { // Out of bounds write. - let res = mem.write(mem.data_size() - value.len() + 1, value); + let size = mem.data_size(&store); + let res = mem.write(&mut store, size - value.len() + 1, value); assert!(res.is_err()); assert_ne!( - unsafe { mem.data_unchecked()[mem.data_size() - value.len() + 1] }, + mem.data(&store)[mem.data_size(&store) - value.len() + 1], value[0], "no data is written", ); @@ -382,15 +405,19 @@ fn read_write_memory_via_api() { // Out of bounds read. buffer[0] = 0x42; - let res = mem.read(mem.data_size() - buffer.len() + 1, &mut buffer); + let res = mem.read( + &store, + mem.data_size(&store) - buffer.len() + 1, + &mut buffer, + ); assert!(res.is_err()); assert_eq!(buffer[0], 0x42, "no data is read"); // Read offset overflow. - let res = mem.read(usize::MAX, &mut buffer); + let res = mem.read(&store, usize::MAX, &mut buffer); assert!(res.is_err()); // Write offset overflow. - let res = mem.write(usize::MAX, &mut buffer); + let res = mem.write(&mut store, usize::MAX, &mut buffer); assert!(res.is_err()); } diff --git a/tests/all/fuel.rs b/tests/all/fuel.rs index 4ecf6112b20e..8478e542ef1c 100644 --- a/tests/all/fuel.rs +++ b/tests/all/fuel.rs @@ -50,9 +50,9 @@ fn fuel_consumed(wasm: &[u8]) -> u64 { config.consume_fuel(true); let engine = Engine::new(&config).unwrap(); let module = Module::new(&engine, wasm).unwrap(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); store.add_fuel(u64::max_value()).unwrap(); - drop(Instance::new(&store, &module, &[])); + drop(Instance::new(&mut store, &module, &[])); store.fuel_consumed().unwrap() } @@ -112,9 +112,9 @@ fn iloop() { config.consume_fuel(true); let engine = Engine::new(&config).unwrap(); let module = Module::new(&engine, wat).unwrap(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); store.add_fuel(10_000).unwrap(); - let error = Instance::new(&store, &module, &[]).err().unwrap(); + let error = Instance::new(&mut store, &module, &[]).err().unwrap(); assert!( error.to_string().contains("all fuel consumed"), "bad error: {}", diff --git a/tests/all/func.rs b/tests/all/func.rs index 67e4f9ddc26a..b4f025e1908a 100644 --- a/tests/all/func.rs +++ b/tests/all/func.rs @@ -1,30 +1,31 @@ use anyhow::Result; -use std::cell::Cell; -use std::rc::Rc; -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; +use std::sync::Arc; use wasmtime::*; #[test] fn func_constructors() { - let store = Store::default(); - Func::wrap(&store, || {}); - Func::wrap(&store, |_: i32| {}); - Func::wrap(&store, |_: i32, _: i64| {}); - Func::wrap(&store, |_: f32, _: f64| {}); - Func::wrap(&store, || -> i32 { 0 }); - Func::wrap(&store, || -> i64 { 0 }); - Func::wrap(&store, || -> f32 { 0.0 }); - Func::wrap(&store, || -> f64 { 0.0 }); - Func::wrap(&store, || -> Option { None }); - Func::wrap(&store, || -> Option { None }); - - Func::wrap(&store, || -> Result<(), Trap> { loop {} }); - Func::wrap(&store, || -> Result { loop {} }); - Func::wrap(&store, || -> Result { loop {} }); - Func::wrap(&store, || -> Result { loop {} }); - Func::wrap(&store, || -> Result { loop {} }); - Func::wrap(&store, || -> Result, Trap> { loop {} }); - Func::wrap(&store, || -> Result, Trap> { loop {} }); + let mut store = Store::<()>::default(); + Func::wrap(&mut store, || {}); + Func::wrap(&mut store, |_: i32| {}); + Func::wrap(&mut store, |_: i32, _: i64| {}); + Func::wrap(&mut store, |_: f32, _: f64| {}); + Func::wrap(&mut store, || -> i32 { 0 }); + Func::wrap(&mut store, || -> i64 { 0 }); + Func::wrap(&mut store, || -> f32 { 0.0 }); + Func::wrap(&mut store, || -> f64 { 0.0 }); + Func::wrap(&mut store, || -> Option { None }); + Func::wrap(&mut store, || -> Option { None }); + + Func::wrap(&mut store, || -> Result<(), Trap> { loop {} }); + Func::wrap(&mut store, || -> Result { loop {} }); + Func::wrap(&mut store, || -> Result { loop {} }); + Func::wrap(&mut store, || -> Result { loop {} }); + Func::wrap(&mut store, || -> Result { loop {} }); + Func::wrap(&mut store, || -> Result, Trap> { + loop {} + }); + Func::wrap(&mut store, || -> Result, Trap> { loop {} }); } #[test] @@ -39,10 +40,10 @@ fn dtor_runs() { } } - let store = Store::default(); + let mut store = Store::<()>::default(); let a = A; assert_eq!(HITS.load(SeqCst), 0); - Func::wrap(&store, move || { + Func::wrap(&mut store, move || { drop(&a); }); drop(store); @@ -61,54 +62,52 @@ fn dtor_delayed() -> Result<()> { } } - let store = Store::default(); + let mut store = Store::<()>::default(); let a = A; - let func = Func::wrap(&store, move || drop(&a)); + let func = Func::wrap(&mut store, move || drop(&a)); assert_eq!(HITS.load(SeqCst), 0); let wasm = wat::parse_str(r#"(import "" "" (func))"#)?; let module = Module::new(store.engine(), &wasm)?; - let instance = Instance::new(&store, &module, &[func.into()])?; + let _instance = Instance::new(&mut store, &module, &[func.into()])?; assert_eq!(HITS.load(SeqCst), 0); - drop((instance, module, store)); + drop(store); assert_eq!(HITS.load(SeqCst), 1); Ok(()) } #[test] fn signatures_match() { - let store = Store::default(); + let mut store = Store::<()>::default(); - let f = Func::wrap(&store, || {}); - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.param_arity(), 0); - assert_eq!(f.ty().results().collect::>(), &[]); - assert_eq!(f.result_arity(), 0); + let f = Func::wrap(&mut store, || {}); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[]); - let f = Func::wrap(&store, || -> i32 { loop {} }); - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::I32]); + let f = Func::wrap(&mut store, || -> i32 { loop {} }); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::I32]); - let f = Func::wrap(&store, || -> i64 { loop {} }); - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::I64]); + let f = Func::wrap(&mut store, || -> i64 { loop {} }); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::I64]); - let f = Func::wrap(&store, || -> f32 { loop {} }); - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::F32]); + let f = Func::wrap(&mut store, || -> f32 { loop {} }); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F32]); - let f = Func::wrap(&store, || -> f64 { loop {} }); - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::F64]); + let f = Func::wrap(&mut store, || -> f64 { loop {} }); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F64]); let f = Func::wrap( - &store, + &mut store, |_: f32, _: f64, _: i32, _: i64, _: i32, _: Option, _: Option| -> f64 { loop {} }, ); assert_eq!( - f.ty().params().collect::>(), + f.ty(&store).params().collect::>(), &[ ValType::F32, ValType::F64, @@ -119,7 +118,7 @@ fn signatures_match() { ValType::FuncRef, ] ); - assert_eq!(f.ty().results().collect::>(), &[ValType::F64]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F64]); } #[test] @@ -156,61 +155,74 @@ fn import_works() -> Result<()> { let mut config = Config::new(); config.wasm_reference_types(true); let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let module = Module::new(&engine, &wasm)?; - let instance = Instance::new( - &store, - &module, + let imports = [ + Func::wrap(&mut store, || { + assert_eq!(HITS.fetch_add(1, SeqCst), 0); + }) + .into(), + Func::wrap(&mut store, |x: i32| -> i32 { + assert_eq!(x, 0); + assert_eq!(HITS.fetch_add(1, SeqCst), 1); + 1 + }) + .into(), + Func::wrap(&mut store, |x: i32, y: i64| { + assert_eq!(x, 2); + assert_eq!(y, 3); + assert_eq!(HITS.fetch_add(1, SeqCst), 2); + }) + .into(), + Func::wrap( + &mut store, + |mut caller: Caller<'_, ()>, + a: i32, + b: i64, + c: i32, + d: f32, + e: f64, + f: Option, + g: Option| { + assert_eq!(a, 100); + assert_eq!(b, 200); + assert_eq!(c, 300); + assert_eq!(d, 400.0); + assert_eq!(e, 500.0); + assert_eq!( + f.as_ref().unwrap().data().downcast_ref::().unwrap(), + "hello" + ); + assert_eq!( + g.as_ref().unwrap().call(&mut caller, &[]).unwrap()[0].unwrap_i32(), + 42 + ); + assert_eq!(HITS.fetch_add(1, SeqCst), 3); + }, + ) + .into(), + ]; + let instance = Instance::new(&mut store, &module, &imports)?; + let run = instance.get_func(&mut store, "run").unwrap(); + let funcref = Val::FuncRef(Some(Func::wrap(&mut store, || -> i32 { 42 }))); + run.call( + &mut store, &[ - Func::wrap(&store, || { - assert_eq!(HITS.fetch_add(1, SeqCst), 0); - }) - .into(), - Func::wrap(&store, |x: i32| -> i32 { - assert_eq!(x, 0); - assert_eq!(HITS.fetch_add(1, SeqCst), 1); - 1 - }) - .into(), - Func::wrap(&store, |x: i32, y: i64| { - assert_eq!(x, 2); - assert_eq!(y, 3); - assert_eq!(HITS.fetch_add(1, SeqCst), 2); - }) - .into(), - Func::wrap( - &store, - |a: i32, b: i64, c: i32, d: f32, e: f64, f: Option, g: Option| { - assert_eq!(a, 100); - assert_eq!(b, 200); - assert_eq!(c, 300); - assert_eq!(d, 400.0); - assert_eq!(e, 500.0); - assert_eq!( - f.as_ref().unwrap().data().downcast_ref::().unwrap(), - "hello" - ); - assert_eq!(g.as_ref().unwrap().call(&[]).unwrap()[0].unwrap_i32(), 42); - assert_eq!(HITS.fetch_add(1, SeqCst), 3); - }, - ) - .into(), + Val::ExternRef(Some(ExternRef::new("hello".to_string()))), + funcref, ], )?; - let run = instance.get_func("run").unwrap(); - run.call(&[ - Val::ExternRef(Some(ExternRef::new("hello".to_string()))), - Val::FuncRef(Some(Func::wrap(&store, || -> i32 { 42 }))), - ])?; assert_eq!(HITS.load(SeqCst), 4); Ok(()) } #[test] fn trap_smoke() -> Result<()> { - let store = Store::default(); - let f = Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("test")) }); - let err = f.call(&[]).unwrap_err().downcast::()?; + let mut store = Store::<()>::default(); + let f = Func::wrap(&mut store, || -> Result<(), Trap> { + Err(Trap::new("test")) + }); + let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; assert!(err.to_string().contains("test")); assert!(err.i32_exit_status().is_none()); Ok(()) @@ -224,80 +236,77 @@ fn trap_import() -> Result<()> { (start 0) "#, )?; - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new(store.engine(), &wasm)?; - let trap = Instance::new( - &store, - &module, - &[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()], - ) - .err() - .unwrap() - .downcast::()?; + let import = Func::wrap(&mut store, || -> Result<(), Trap> { Err(Trap::new("foo")) }); + let trap = Instance::new(&mut store, &module, &[import.into()]) + .err() + .unwrap() + .downcast::()?; assert!(trap.to_string().contains("foo")); Ok(()) } #[test] fn get_from_wrapper() { - let store = Store::default(); - let f = Func::wrap(&store, || {}); - assert!(f.typed::<(), ()>().is_ok()); - assert!(f.typed::<(), i32>().is_err()); - assert!(f.typed::<(), ()>().is_ok()); - assert!(f.typed::().is_err()); - assert!(f.typed::().is_err()); - assert!(f.typed::<(i32, i32), ()>().is_err()); - assert!(f.typed::<(i32, i32), i32>().is_err()); - - let f = Func::wrap(&store, || -> i32 { loop {} }); - assert!(f.typed::<(), i32>().is_ok()); - let f = Func::wrap(&store, || -> f32 { loop {} }); - assert!(f.typed::<(), f32>().is_ok()); - let f = Func::wrap(&store, || -> f64 { loop {} }); - assert!(f.typed::<(), f64>().is_ok()); - let f = Func::wrap(&store, || -> Option { loop {} }); - assert!(f.typed::<(), Option>().is_ok()); - let f = Func::wrap(&store, || -> Option { loop {} }); - assert!(f.typed::<(), Option>().is_ok()); - - let f = Func::wrap(&store, |_: i32| {}); - assert!(f.typed::().is_ok()); - assert!(f.typed::().is_err()); - assert!(f.typed::().is_err()); - assert!(f.typed::().is_err()); - let f = Func::wrap(&store, |_: i64| {}); - assert!(f.typed::().is_ok()); - let f = Func::wrap(&store, |_: f32| {}); - assert!(f.typed::().is_ok()); - let f = Func::wrap(&store, |_: f64| {}); - assert!(f.typed::().is_ok()); - let f = Func::wrap(&store, |_: Option| {}); - assert!(f.typed::, ()>().is_ok()); - let f = Func::wrap(&store, |_: Option| {}); - assert!(f.typed::, ()>().is_ok()); + let mut store = Store::<()>::default(); + let f = Func::wrap(&mut store, || {}); + assert!(f.typed::<(), (), _>(&store).is_ok()); + assert!(f.typed::<(), i32, _>(&store).is_err()); + assert!(f.typed::<(), (), _>(&store).is_ok()); + assert!(f.typed::(&store).is_err()); + assert!(f.typed::(&store).is_err()); + assert!(f.typed::<(i32, i32), (), _>(&store).is_err()); + assert!(f.typed::<(i32, i32), i32, _>(&store).is_err()); + + let f = Func::wrap(&mut store, || -> i32 { loop {} }); + assert!(f.typed::<(), i32, _>(&store).is_ok()); + let f = Func::wrap(&mut store, || -> f32 { loop {} }); + assert!(f.typed::<(), f32, _>(&store).is_ok()); + let f = Func::wrap(&mut store, || -> f64 { loop {} }); + assert!(f.typed::<(), f64, _>(&store).is_ok()); + let f = Func::wrap(&mut store, || -> Option { loop {} }); + assert!(f.typed::<(), Option, _>(&store).is_ok()); + let f = Func::wrap(&mut store, || -> Option { loop {} }); + assert!(f.typed::<(), Option, _>(&store).is_ok()); + + let f = Func::wrap(&mut store, |_: i32| {}); + assert!(f.typed::(&store).is_ok()); + assert!(f.typed::(&store).is_err()); + assert!(f.typed::(&store).is_err()); + assert!(f.typed::(&store).is_err()); + let f = Func::wrap(&mut store, |_: i64| {}); + assert!(f.typed::(&store).is_ok()); + let f = Func::wrap(&mut store, |_: f32| {}); + assert!(f.typed::(&store).is_ok()); + let f = Func::wrap(&mut store, |_: f64| {}); + assert!(f.typed::(&store).is_ok()); + let f = Func::wrap(&mut store, |_: Option| {}); + assert!(f.typed::, (), _>(&store).is_ok()); + let f = Func::wrap(&mut store, |_: Option| {}); + assert!(f.typed::, (), _>(&store).is_ok()); } #[test] fn get_from_signature() { - let store = Store::default(); + let mut store = Store::<()>::default(); let ty = FuncType::new(None, None); - let f = Func::new(&store, ty, |_, _, _| panic!()); - assert!(f.typed::<(), ()>().is_ok()); - assert!(f.typed::<(), i32>().is_err()); - assert!(f.typed::().is_err()); + let f = Func::new(&mut store, ty, |_, _, _| panic!()); + assert!(f.typed::<(), (), _>(&store).is_ok()); + assert!(f.typed::<(), i32, _>(&store).is_err()); + assert!(f.typed::(&store).is_err()); let ty = FuncType::new(Some(ValType::I32), Some(ValType::F64)); - let f = Func::new(&store, ty, |_, _, _| panic!()); - assert!(f.typed::<(), ()>().is_err()); - assert!(f.typed::<(), i32>().is_err()); - assert!(f.typed::().is_err()); - assert!(f.typed::().is_ok()); + let f = Func::new(&mut store, ty, |_, _, _| panic!()); + assert!(f.typed::<(), (), _>(&store).is_err()); + assert!(f.typed::<(), i32, _>(&store).is_err()); + assert!(f.typed::(&store).is_err()); + assert!(f.typed::(&store).is_ok()); } #[test] fn get_from_module() -> anyhow::Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new( store.engine(), r#" @@ -310,72 +319,75 @@ fn get_from_module() -> anyhow::Result<()> { "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let f0 = instance.get_func("f0").unwrap(); - assert!(f0.typed::<(), ()>().is_ok()); - assert!(f0.typed::<(), i32>().is_err()); - let f1 = instance.get_func("f1").unwrap(); - assert!(f1.typed::<(), ()>().is_err()); - assert!(f1.typed::().is_ok()); - assert!(f1.typed::().is_err()); - let f2 = instance.get_func("f2").unwrap(); - assert!(f2.typed::<(), ()>().is_err()); - assert!(f2.typed::<(), i32>().is_ok()); - assert!(f2.typed::().is_err()); - assert!(f2.typed::().is_err()); + let instance = Instance::new(&mut store, &module, &[])?; + let f0 = instance.get_func(&mut store, "f0").unwrap(); + assert!(f0.typed::<(), (), _>(&store).is_ok()); + assert!(f0.typed::<(), i32, _>(&store).is_err()); + let f1 = instance.get_func(&mut store, "f1").unwrap(); + assert!(f1.typed::<(), (), _>(&store).is_err()); + assert!(f1.typed::(&store).is_ok()); + assert!(f1.typed::(&store).is_err()); + let f2 = instance.get_func(&mut store, "f2").unwrap(); + assert!(f2.typed::<(), (), _>(&store).is_err()); + assert!(f2.typed::<(), i32, _>(&store).is_ok()); + assert!(f2.typed::(&store).is_err()); + assert!(f2.typed::(&store).is_err()); Ok(()) } #[test] fn call_wrapped_func() -> Result<()> { - let store = Store::default(); - let f = Func::wrap(&store, |a: i32, b: i64, c: f32, d: f64| { + let mut store = Store::<()>::default(); + let f = Func::wrap(&mut store, |a: i32, b: i64, c: f32, d: f64| { assert_eq!(a, 1); assert_eq!(b, 2); assert_eq!(c, 3.0); assert_eq!(d, 4.0); }); - f.call(&[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()])?; - f.typed::<(i32, i64, f32, f64), ()>()? - .call((1, 2, 3.0, 4.0))?; + f.call( + &mut store, + &[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()], + )?; + f.typed::<(i32, i64, f32, f64), (), _>(&store)? + .call(&mut store, (1, 2, 3.0, 4.0))?; - let f = Func::wrap(&store, || 1i32); - let results = f.call(&[])?; + let f = Func::wrap(&mut store, || 1i32); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_i32(), 1); - assert_eq!(f.typed::<(), i32>()?.call(())?, 1); + assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1); - let f = Func::wrap(&store, || 2i64); - let results = f.call(&[])?; + let f = Func::wrap(&mut store, || 2i64); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_i64(), 2); - assert_eq!(f.typed::<(), i64>()?.call(())?, 2); + assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2); - let f = Func::wrap(&store, || 3.0f32); - let results = f.call(&[])?; + let f = Func::wrap(&mut store, || 3.0f32); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_f32(), 3.0); - assert_eq!(f.typed::<(), f32>()?.call(())?, 3.0); + assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0); - let f = Func::wrap(&store, || 4.0f64); - let results = f.call(&[])?; + let f = Func::wrap(&mut store, || 4.0f64); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_f64(), 4.0); - assert_eq!(f.typed::<(), f64>()?.call(())?, 4.0); + assert_eq!(f.typed::<(), f64, _>(&store)?.call(&mut store, ())?, 4.0); Ok(()) } #[test] fn caller_memory() -> anyhow::Result<()> { - let store = Store::default(); - let f = Func::wrap(&store, |c: Caller<'_>| { + let mut store = Store::<()>::default(); + let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| { assert!(c.get_export("x").is_none()); assert!(c.get_export("y").is_none()); assert!(c.get_export("z").is_none()); }); - f.call(&[])?; + f.call(&mut store, &[])?; - let f = Func::wrap(&store, |c: Caller<'_>| { + let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| { assert!(c.get_export("x").is_none()); }); let module = Module::new( @@ -388,9 +400,9 @@ fn caller_memory() -> anyhow::Result<()> { "#, )?; - Instance::new(&store, &module, &[f.into()])?; + Instance::new(&mut store, &module, &[f.into()])?; - let f = Func::wrap(&store, |c: Caller<'_>| { + let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| { assert!(c.get_export("memory").is_some()); }); let module = Module::new( @@ -404,9 +416,9 @@ fn caller_memory() -> anyhow::Result<()> { "#, )?; - Instance::new(&store, &module, &[f.into()])?; + Instance::new(&mut store, &module, &[f.into()])?; - let f = Func::wrap(&store, |c: Caller<'_>| { + let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| { assert!(c.get_export("m").is_some()); assert!(c.get_export("f").is_some()); assert!(c.get_export("g").is_none()); @@ -426,16 +438,16 @@ fn caller_memory() -> anyhow::Result<()> { "#, )?; - Instance::new(&store, &module, &[f.into()])?; + Instance::new(&mut store, &module, &[f.into()])?; Ok(()) } #[test] fn func_write_nothing() -> anyhow::Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let ty = FuncType::new(None, Some(ValType::I32)); - let f = Func::new(&store, ty, |_, _, _| Ok(())); - let err = f.call(&[]).unwrap_err().downcast::()?; + let f = Func::new(&mut store, ty, |_, _, _| Ok(())); + let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; assert!(err .to_string() .contains("function attempted to return an incompatible value")); @@ -461,16 +473,16 @@ fn return_cross_store_value() -> anyhow::Result<()> { let engine = Engine::new(&config)?; let module = Module::new(&engine, &wasm)?; - let store1 = Store::new(&engine); - let store2 = Store::new(&engine); + let mut store1 = Store::new(&engine, ()); + let mut store2 = Store::new(&engine, ()); - let store2_func = Func::wrap(&store2, || {}); - let return_cross_store_func = Func::wrap(&store1, move || Some(store2_func.clone())); + let store2_func = Func::wrap(&mut store2, || {}); + let return_cross_store_func = Func::wrap(&mut store1, move || Some(store2_func.clone())); - let instance = Instance::new(&store1, &module, &[return_cross_store_func.into()])?; + let instance = Instance::new(&mut store1, &module, &[return_cross_store_func.into()])?; - let run = instance.get_func("run").unwrap(); - let result = run.call(&[]); + let run = instance.get_func(&mut store1, "run").unwrap(); + let result = run.call(&mut store1, &[]); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("cross-`Store`")); @@ -486,21 +498,21 @@ fn pass_cross_store_arg() -> anyhow::Result<()> { config.wasm_reference_types(true); let engine = Engine::new(&config)?; - let store1 = Store::new(&engine); - let store2 = Store::new(&engine); + let mut store1 = Store::new(&engine, ()); + let mut store2 = Store::new(&engine, ()); - let store1_func = Func::wrap(&store1, |_: Option| {}); - let store2_func = Func::wrap(&store2, || {}); + let store1_func = Func::wrap(&mut store1, |_: Option| {}); + let store2_func = Func::wrap(&mut store2, || {}); // Using regular `.call` fails with cross-Store arguments. assert!(store1_func - .call(&[Val::FuncRef(Some(store2_func.clone()))]) + .call(&mut store1, &[Val::FuncRef(Some(store2_func.clone()))]) .is_err()); // And using `.get` followed by a function call also fails with cross-Store // arguments. - let f = store1_func.typed::, ()>()?; - let result = f.call(Some(store2_func)); + let f = store1_func.typed::, (), _>(&store1)?; + let result = f.call(&mut store1, Some(store2_func)); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("cross-`Store`")); @@ -509,10 +521,12 @@ fn pass_cross_store_arg() -> anyhow::Result<()> { #[test] fn externref_signature_no_reference_types() -> anyhow::Result<()> { - let store = Store::default(); - Func::wrap(&store, |_: Option| {}); + let mut config = Config::new(); + config.wasm_reference_types(false); + let mut store = Store::new(&Engine::new(&config)?, ()); + Func::wrap(&mut store, |_: Option| {}); Func::new( - &store, + &mut store, FuncType::new( [ValType::FuncRef, ValType::ExternRef].iter().cloned(), [ValType::FuncRef, ValType::ExternRef].iter().cloned(), @@ -524,35 +538,34 @@ fn externref_signature_no_reference_types() -> anyhow::Result<()> { #[test] fn trampolines_always_valid() -> anyhow::Result<()> { - let func = { - // Compile two modules up front - let store = Store::default(); - let module1 = Module::new(store.engine(), "(module (import \"\" \"\" (func)))")?; - let module2 = Module::new(store.engine(), "(module (func (export \"\")))")?; - // Start instantiating the first module, but this will fail. - // Historically this registered the module's trampolines with `Store` - // before the failure, but then after the failure the `Store` didn't - // hold onto the trampoline. - drop(Instance::new(&store, &module1, &[])); - drop(module1); - - // Then instantiate another module which has the same function type (no - // parameters or results) which tries to use the trampoline defined in - // the previous module. Then we extract the function and, in another - // scope where everything is dropped, we call the func. - let i = Instance::new(&store, &module2, &[])?; - i.get_func("").unwrap() - }; + // Compile two modules up front + let mut store = Store::<()>::default(); + let module1 = Module::new(store.engine(), "(module (import \"\" \"\" (func)))")?; + let module2 = Module::new(store.engine(), "(module (func (export \"\")))")?; + // Start instantiating the first module, but this will fail. + // Historically this registered the module's trampolines with `Store` + // before the failure, but then after the failure the `Store` didn't + // hold onto the trampoline. + drop(Instance::new(&mut store, &module1, &[])); + drop(module1); + + // Then instantiate another module which has the same function type (no + // parameters or results) which tries to use the trampoline defined in + // the previous module. Then we extract the function and, after we drop the + // module's reference, we call the func. + let i = Instance::new(&mut store, &module2, &[])?; + let func = i.get_func(&mut store, "").unwrap(); + drop(module2); // ... and no segfaults! right? right? ... - func.call(&[])?; + func.call(&mut store, &[])?; Ok(()) } #[test] #[cfg(not(feature = "old-x86-backend"))] fn typed_multiple_results() -> anyhow::Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new( store.engine(), r#" @@ -567,16 +580,21 @@ fn typed_multiple_results() -> anyhow::Result<()> { "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let f0 = instance.get_func("f0").unwrap(); - assert!(f0.typed::<(), ()>().is_err()); - assert!(f0.typed::<(), (i32, f32)>().is_err()); - assert!(f0.typed::<(), i32>().is_err()); - assert_eq!(f0.typed::<(), (i32, i64)>()?.call(())?, (0, 1)); - - let f1 = instance.get_func("f1").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let f0 = instance.get_func(&mut store, "f0").unwrap(); + assert!(f0.typed::<(), (), _>(&store).is_err()); + assert!(f0.typed::<(), (i32, f32), _>(&store).is_err()); + assert!(f0.typed::<(), i32, _>(&store).is_err()); assert_eq!( - f1.typed::<(i32, i32, i32), (f32, f64)>()?.call((1, 2, 3))?, + f0.typed::<(), (i32, i64), _>(&store)? + .call(&mut store, ())?, + (0, 1) + ); + + let f1 = instance.get_func(&mut store, "f1").unwrap(); + assert_eq!( + f1.typed::<(i32, i32, i32), (f32, f64), _>(&store)? + .call(&mut store, (1, 2, 3))?, (2., 3.) ); Ok(()) @@ -584,108 +602,117 @@ fn typed_multiple_results() -> anyhow::Result<()> { #[test] fn trap_doesnt_leak() -> anyhow::Result<()> { - struct Canary(Rc>); + #[derive(Default)] + struct Canary(Arc); impl Drop for Canary { fn drop(&mut self) { - self.0.set(true); + self.0.store(true, SeqCst); } } - let store = Store::default(); + let mut store = Store::<()>::default(); // test that `Func::wrap` is correct - let canary1 = Canary(Rc::new(Cell::new(false))); + let canary1 = Canary::default(); let dtor1_run = canary1.0.clone(); - let f1 = Func::wrap(&store, move || -> Result<(), Trap> { + let f1 = Func::wrap(&mut store, move || -> Result<(), Trap> { drop(&canary1); Err(Trap::new("")) }); - assert!(f1.typed::<(), ()>()?.call(()).is_err()); - assert!(f1.call(&[]).is_err()); + assert!(f1.typed::<(), (), _>(&store)?.call(&mut store, ()).is_err()); + assert!(f1.call(&mut store, &[]).is_err()); // test that `Func::new` is correct - let canary2 = Canary(Rc::new(Cell::new(false))); + let canary2 = Canary::default(); let dtor2_run = canary2.0.clone(); - let f2 = Func::new(&store, FuncType::new(None, None), move |_, _, _| { + let f2 = Func::new(&mut store, FuncType::new(None, None), move |_, _, _| { drop(&canary2); Err(Trap::new("")) }); - assert!(f2.typed::<(), ()>()?.call(()).is_err()); - assert!(f2.call(&[]).is_err()); + assert!(f2.typed::<(), (), _>(&store)?.call(&mut store, ()).is_err()); + assert!(f2.call(&mut store, &[]).is_err()); // drop everything and ensure dtors are run - drop((store, f1, f2)); - assert!(dtor1_run.get()); - assert!(dtor2_run.get()); + drop(store); + assert!(dtor1_run.load(SeqCst)); + assert!(dtor2_run.load(SeqCst)); Ok(()) } #[test] #[cfg(not(feature = "old-x86-backend"))] fn wrap_multiple_results() -> anyhow::Result<()> { - fn test(store: &Store, t: T) -> anyhow::Result<()> + fn test(store: &mut Store<()>, t: T) -> anyhow::Result<()> where - T: WasmRet + WasmResults + PartialEq + Copy + std::fmt::Debug + EqualToValues + 'static, + T: WasmRet + + WasmResults + + PartialEq + + Copy + + std::fmt::Debug + + EqualToValues + + 'static + + Send + + Sync, { - let f = Func::wrap(store, move || t); - assert_eq!(f.typed::<(), T>()?.call(())?, t); - assert!(t.eq_values(&f.call(&[])?)); + let f = Func::wrap(&mut *store, move || t); + assert_eq!(f.typed::<(), T, _>(&store,)?.call(&mut *store, ())?, t); + assert!(t.eq_values(&f.call(&mut *store, &[])?)); let module = Module::new(store.engine(), &T::gen_wasm())?; - let instance = Instance::new(store, &module, &[f.into()])?; - let f = instance.get_func("foo").unwrap(); + let instance = Instance::new(&mut *store, &module, &[f.into()])?; + let f = instance.get_func(&mut *store, "foo").unwrap(); - assert_eq!(f.typed::<(), T>()?.call(())?, t); - assert!(t.eq_values(&f.call(&[])?)); + assert_eq!(f.typed::<(), T, _>(&store)?.call(&mut *store, ())?, t); + assert!(t.eq_values(&f.call(&mut *store, &[])?)); Ok(()) } - let store = Store::default(); + let mut store = Store::default(); // 0 element - test(&store, ())?; + test(&mut store, ())?; // 1 element - test(&store, (1i32,))?; - test(&store, (2u32,))?; - test(&store, (3i64,))?; - test(&store, (4u64,))?; - test(&store, (5.0f32,))?; - test(&store, (6.0f64,))?; + test(&mut store, (1i32,))?; + test(&mut store, (2u32,))?; + test(&mut store, (3i64,))?; + test(&mut store, (4u64,))?; + test(&mut store, (5.0f32,))?; + test(&mut store, (6.0f64,))?; // 2 element ... - test(&store, (7i32, 8i32))?; - test(&store, (7i32, 8i64))?; - test(&store, (7i32, 8f32))?; - test(&store, (7i32, 8f64))?; - - test(&store, (7i64, 8i32))?; - test(&store, (7i64, 8i64))?; - test(&store, (7i64, 8f32))?; - test(&store, (7i64, 8f64))?; - - test(&store, (7f32, 8i32))?; - test(&store, (7f32, 8i64))?; - test(&store, (7f32, 8f32))?; - test(&store, (7f32, 8f64))?; - - test(&store, (7f64, 8i32))?; - test(&store, (7f64, 8i64))?; - test(&store, (7f64, 8f32))?; - test(&store, (7f64, 8f64))?; + test(&mut store, (7i32, 8i32))?; + test(&mut store, (7i32, 8i64))?; + test(&mut store, (7i32, 8f32))?; + test(&mut store, (7i32, 8f64))?; + + test(&mut store, (7i64, 8i32))?; + test(&mut store, (7i64, 8i64))?; + test(&mut store, (7i64, 8f32))?; + test(&mut store, (7i64, 8f64))?; + + test(&mut store, (7f32, 8i32))?; + test(&mut store, (7f32, 8i64))?; + test(&mut store, (7f32, 8f32))?; + test(&mut store, (7f32, 8f64))?; + + test(&mut store, (7f64, 8i32))?; + test(&mut store, (7f64, 8i64))?; + test(&mut store, (7f64, 8f32))?; + test(&mut store, (7f64, 8f64))?; // and beyond... - test(&store, (1i32, 2i32, 3i32))?; - test(&store, (1i32, 2f32, 3i32))?; - test(&store, (1f64, 2f32, 3i32))?; - test(&store, (1f64, 2i64, 3i32))?; - test(&store, (1f32, 2f32, 3i64, 4f64))?; - test(&store, (1f64, 2i64, 3i32, 4i64, 5f32))?; - test(&store, (1i32, 2f64, 3i64, 4f64, 5f64, 6f32))?; - test(&store, (1i64, 2i32, 3i64, 4f32, 5f32, 6i32, 7u64))?; - test(&store, (1u32, 2f32, 3u64, 4f64, 5i32, 6f32, 7u64, 8u32))?; + test(&mut store, (1i32, 2i32, 3i32))?; + test(&mut store, (1i32, 2f32, 3i32))?; + test(&mut store, (1f64, 2f32, 3i32))?; + test(&mut store, (1f64, 2i64, 3i32))?; + test(&mut store, (1f32, 2f32, 3i64, 4f64))?; + test(&mut store, (1f64, 2i64, 3i32, 4i64, 5f32))?; + test(&mut store, (1i32, 2f64, 3i64, 4f64, 5f64, 6f32))?; + test(&mut store, (1i64, 2i32, 3i64, 4f32, 5f32, 6i32, 7u64))?; + test(&mut store, (1u32, 2f32, 3u64, 4f64, 5i32, 6f32, 7u64, 8u32))?; test( - &store, + &mut store, (1f32, 2f64, 3f32, 4i32, 5u32, 6i64, 7f32, 8i32, 9u64), )?; return Ok(()); @@ -792,12 +819,68 @@ fn trampoline_for_declared_elem() -> anyhow::Result<()> { "#, )?; - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + + let g = instance.get_typed_func::<(), Option, _>(&mut store, "g")?; - let g = instance.get_typed_func::<(), Option>("g")?; + let func = g.call(&mut store, ())?; + func.unwrap().call(&mut store, &[])?; + Ok(()) +} - let func = g.call(())?; - func.unwrap().call(&[])?; +#[test] +fn wasm_ty_roundtrip() -> Result<(), anyhow::Error> { + let mut store = Store::<()>::default(); + let debug = Func::wrap( + &mut store, + |a: i32, b: u32, c: f32, d: i64, e: u64, f: f64| { + assert_eq!(a, -1); + assert_eq!(b, 1); + assert_eq!(c, 2.0); + assert_eq!(d, -3); + assert_eq!(e, 3); + assert_eq!(f, 4.0); + }, + ); + let module = Module::new( + store.engine(), + r#" + (module + (import "" "" (func $debug (param i32 i32 f32 i64 i64 f64))) + (func (export "foo") (param i32 i32 f32 i64 i64 f64) + (if (i32.ne (local.get 0) (i32.const -1)) + (then unreachable) + ) + (if (i32.ne (local.get 1) (i32.const 1)) + (then unreachable) + ) + (if (f32.ne (local.get 2) (f32.const 2)) + (then unreachable) + ) + (if (i64.ne (local.get 3) (i64.const -3)) + (then unreachable) + ) + (if (i64.ne (local.get 4) (i64.const 3)) + (then unreachable) + ) + (if (f64.ne (local.get 5) (f64.const 4)) + (then unreachable) + ) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + local.get 4 + local.get 5 + call $debug + ) + ) + "#, + )?; + let instance = Instance::new(&mut store, &module, &[debug.into()])?; + let foo = + instance.get_typed_func::<(i32, u32, f32, i64, u64, f64), (), _>(&mut store, "foo")?; + foo.call(&mut store, (-1, 1, 2.0, -3, 3, 4.0))?; Ok(()) } diff --git a/tests/all/funcref.rs b/tests/all/funcref.rs index dd1e0cb9dbc7..676fd255e81d 100644 --- a/tests/all/funcref.rs +++ b/tests/all/funcref.rs @@ -1,11 +1,11 @@ use super::ref_types_module; -use std::cell::Cell; -use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; +use std::sync::Arc; use wasmtime::*; #[test] fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { - let (store, module) = ref_types_module( + let (mut store, module) = ref_types_module( r#" (module (func (export "func") (param funcref) (result funcref) @@ -15,22 +15,22 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let func = instance.get_func("func").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let func = instance.get_func(&mut store, "func").unwrap(); // Pass in a non-null funcref. { - let results = func.call(&[Val::FuncRef(Some(func.clone()))])?; + let results = func.call(&mut store, &[Val::FuncRef(Some(func.clone()))])?; assert_eq!(results.len(), 1); // Can't compare `Func` for equality, so this is the best we can do here. let result_func = results[0].unwrap_funcref().unwrap(); - assert_eq!(func.ty(), result_func.ty()); + assert_eq!(func.ty(&store), result_func.ty(&store)); } // Pass in a null funcref. { - let results = func.call(&[Val::FuncRef(None)])?; + let results = func.call(&mut store, &[Val::FuncRef(None)])?; assert_eq!(results.len(), 1); let result_func = results[0].unwrap_funcref(); @@ -39,24 +39,29 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { // Pass in a `funcref` from another instance. { - let other_instance = Instance::new(&store, &module, &[])?; - let other_instance_func = other_instance.get_func("func").unwrap(); + let other_instance = Instance::new(&mut store, &module, &[])?; + let other_instance_func = other_instance.get_func(&mut store, "func").unwrap(); - let results = func.call(&[Val::FuncRef(Some(other_instance_func.clone()))])?; + let results = func.call( + &mut store, + &[Val::FuncRef(Some(other_instance_func.clone()))], + )?; assert_eq!(results.len(), 1); // Can't compare `Func` for equality, so this is the best we can do here. let result_func = results[0].unwrap_funcref().unwrap(); - assert_eq!(other_instance_func.ty(), result_func.ty()); + assert_eq!(other_instance_func.ty(&store), result_func.ty(&store)); } // Passing in a `funcref` from another store fails. { - let (other_store, other_module) = ref_types_module(r#"(module (func (export "f")))"#)?; - let other_store_instance = Instance::new(&other_store, &other_module, &[])?; - let f = other_store_instance.get_func("f").unwrap(); + let (mut other_store, other_module) = ref_types_module(r#"(module (func (export "f")))"#)?; + let other_store_instance = Instance::new(&mut other_store, &other_module, &[])?; + let f = other_store_instance + .get_func(&mut other_store, "f") + .unwrap(); - assert!(func.call(&[Val::FuncRef(Some(f))]).is_err()); + assert!(func.call(&mut store, &[Val::FuncRef(Some(f))]).is_err()); } Ok(()) @@ -64,7 +69,7 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { #[test] fn receive_null_funcref_from_wasm() -> anyhow::Result<()> { - let (store, module) = ref_types_module( + let (mut store, module) = ref_types_module( r#" (module (func (export "get-null") (result funcref) @@ -74,10 +79,10 @@ fn receive_null_funcref_from_wasm() -> anyhow::Result<()> { "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let get_null = instance.get_func("get-null").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let get_null = instance.get_func(&mut store, "get-null").unwrap(); - let results = get_null.call(&[])?; + let results = get_null.call(&mut store, &[])?; assert_eq!(results.len(), 1); let result_func = results[0].unwrap_funcref(); @@ -88,57 +93,57 @@ fn receive_null_funcref_from_wasm() -> anyhow::Result<()> { #[test] fn wrong_store() -> anyhow::Result<()> { - let dropped = Rc::new(Cell::new(false)); + let dropped = Arc::new(AtomicBool::new(false)); { - let store1 = Store::default(); - let store2 = Store::default(); + let mut store1 = Store::<()>::default(); + let mut store2 = Store::<()>::default(); let set = SetOnDrop(dropped.clone()); - let f1 = Func::wrap(&store1, move || drop(&set)); - let f2 = Func::wrap(&store2, move || Some(f1.clone())); - assert!(f2.call(&[]).is_err()); + let f1 = Func::wrap(&mut store1, move || drop(&set)); + let f2 = Func::wrap(&mut store2, move || Some(f1.clone())); + assert!(f2.call(&mut store2, &[]).is_err()); } - assert!(dropped.get()); + assert!(dropped.load(SeqCst)); return Ok(()); - struct SetOnDrop(Rc>); + struct SetOnDrop(Arc); impl Drop for SetOnDrop { fn drop(&mut self) { - self.0.set(true); + self.0.store(true, SeqCst); } } } #[test] fn func_new_returns_wrong_store() -> anyhow::Result<()> { - let dropped = Rc::new(Cell::new(false)); + let dropped = Arc::new(AtomicBool::new(false)); { - let store1 = Store::default(); - let store2 = Store::default(); + let mut store1 = Store::<()>::default(); + let mut store2 = Store::<()>::default(); let set = SetOnDrop(dropped.clone()); - let f1 = Func::wrap(&store1, move || drop(&set)); + let f1 = Func::wrap(&mut store1, move || drop(&set)); let f2 = Func::new( - &store2, + &mut store2, FuncType::new(None, Some(ValType::FuncRef)), move |_, _, results| { results[0] = f1.clone().into(); Ok(()) }, ); - assert!(f2.call(&[]).is_err()); + assert!(f2.call(&mut store2, &[]).is_err()); } - assert!(dropped.get()); + assert!(dropped.load(SeqCst)); return Ok(()); - struct SetOnDrop(Rc>); + struct SetOnDrop(Arc); impl Drop for SetOnDrop { fn drop(&mut self) { - self.0.set(true); + self.0.store(true, SeqCst); } } } diff --git a/tests/all/gc.rs b/tests/all/gc.rs index e616b2d0c8ad..ed93ede16344 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -1,31 +1,19 @@ use super::ref_types_module; -use std::cell::Cell; -use std::rc::Rc; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; +use std::sync::Arc; use wasmtime::*; -struct SetFlagOnDrop(Rc>); +struct SetFlagOnDrop(Arc); impl Drop for SetFlagOnDrop { fn drop(&mut self) { - self.0.set(true); - } -} - -struct GcOnDrop { - store: Store, - gc_count: Rc>, -} - -impl Drop for GcOnDrop { - fn drop(&mut self) { - self.store.gc(); - self.gc_count.set(self.gc_count.get() + 1); + self.0.store(true, SeqCst); } } #[test] fn smoke_test_gc() -> anyhow::Result<()> { - let (store, module) = ref_types_module( + let (mut store, module) = ref_types_module( r#" (module (import "" "" (func $do_gc)) @@ -47,18 +35,18 @@ fn smoke_test_gc() -> anyhow::Result<()> { "#, )?; - let do_gc = Func::wrap(&store, |caller: Caller| { + let do_gc = Func::wrap(&mut store, |mut caller: Caller<'_, _>| { // Do a GC with `externref`s on the stack in Wasm frames. - caller.store().gc(); + caller.gc(); }); - let instance = Instance::new(&store, &module, &[do_gc.into()])?; - let func = instance.get_func("func").unwrap(); + let instance = Instance::new(&mut store, &module, &[do_gc.into()])?; + let func = instance.get_func(&mut store, "func").unwrap(); - let inner_dropped = Rc::new(Cell::new(false)); + let inner_dropped = Arc::new(AtomicBool::new(false)); let r = ExternRef::new(SetFlagOnDrop(inner_dropped.clone())); { let args = [Val::I32(5), Val::ExternRef(Some(r.clone()))]; - func.call(&args)?; + func.call(&mut store, &args)?; } // Still held alive by the `VMExternRefActivationsTable` (potentially in @@ -72,14 +60,14 @@ fn smoke_test_gc() -> anyhow::Result<()> { // Dropping `r` should drop the inner `SetFlagOnDrop` value. drop(r); - assert!(inner_dropped.get()); + assert!(inner_dropped.load(SeqCst)); Ok(()) } #[test] fn wasm_dropping_refs() -> anyhow::Result<()> { - let (store, module) = ref_types_module( + let (mut store, module) = ref_types_module( r#" (module (func (export "drop_ref") (param externref) @@ -89,32 +77,32 @@ fn wasm_dropping_refs() -> anyhow::Result<()> { "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let drop_ref = instance.get_func("drop_ref").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let drop_ref = instance.get_func(&mut store, "drop_ref").unwrap(); - let num_refs_dropped = Rc::new(Cell::new(0)); + let num_refs_dropped = Arc::new(AtomicUsize::new(0)); // NB: 4096 is greater than the initial `VMExternRefActivationsTable` // capacity, so this will trigger at least one GC. for _ in 0..4096 { let r = ExternRef::new(CountDrops(num_refs_dropped.clone())); let args = [Val::ExternRef(Some(r))]; - drop_ref.call(&args)?; + drop_ref.call(&mut store, &args)?; } - assert!(num_refs_dropped.get() > 0); + assert!(num_refs_dropped.load(SeqCst) > 0); // And after doing a final GC, all the refs should have been dropped. store.gc(); - assert_eq!(num_refs_dropped.get(), 4096); + assert_eq!(num_refs_dropped.load(SeqCst), 4096); return Ok(()); - struct CountDrops(Rc>); + struct CountDrops(Arc); impl Drop for CountDrops { fn drop(&mut self) { - self.0.set(self.0.get() + 1); + self.0.fetch_add(1, SeqCst); } } } @@ -156,54 +144,52 @@ fn many_live_refs() -> anyhow::Result<()> { ", ); - let (store, module) = ref_types_module(&wat)?; + let (mut store, module) = ref_types_module(&wat)?; - let live_refs = Rc::new(Cell::new(0)); + let live_refs = Arc::new(AtomicUsize::new(0)); - let make_ref = Func::wrap(&store, { + let make_ref = Func::wrap(&mut store, { let live_refs = live_refs.clone(); move || Some(ExternRef::new(CountLiveRefs::new(live_refs.clone()))) }); - let observe_ref = Func::wrap(&store, |r: Option| { + let observe_ref = Func::wrap(&mut store, |r: Option| { let r = r.unwrap(); let r = r.data().downcast_ref::().unwrap(); - assert!(r.live_refs.get() > 0); + assert!(r.live_refs.load(SeqCst) > 0); }); - let instance = Instance::new(&store, &module, &[make_ref.into(), observe_ref.into()])?; - let many_live_refs = instance.get_func("many_live_refs").unwrap(); + let instance = Instance::new(&mut store, &module, &[make_ref.into(), observe_ref.into()])?; + let many_live_refs = instance.get_func(&mut store, "many_live_refs").unwrap(); - many_live_refs.call(&[])?; + many_live_refs.call(&mut store, &[])?; store.gc(); - assert_eq!(live_refs.get(), 0); + assert_eq!(live_refs.load(SeqCst), 0); return Ok(()); struct CountLiveRefs { - live_refs: Rc>, + live_refs: Arc, } impl CountLiveRefs { - fn new(live_refs: Rc>) -> Self { - let live = live_refs.get(); - live_refs.set(live + 1); + fn new(live_refs: Arc) -> Self { + live_refs.fetch_add(1, SeqCst); Self { live_refs } } } impl Drop for CountLiveRefs { fn drop(&mut self) { - let live = self.live_refs.get(); - self.live_refs.set(live - 1); + self.live_refs.fetch_sub(1, SeqCst); } } } #[test] fn drop_externref_via_table_set() -> anyhow::Result<()> { - let (store, module) = ref_types_module( + let (mut store, module) = ref_types_module( r#" (module (table $t 1 externref) @@ -215,373 +201,224 @@ fn drop_externref_via_table_set() -> anyhow::Result<()> { "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let table_set = instance.get_func("table-set").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let table_set = instance.get_func(&mut store, "table-set").unwrap(); - let foo_is_dropped = Rc::new(Cell::new(false)); - let bar_is_dropped = Rc::new(Cell::new(false)); + let foo_is_dropped = Arc::new(AtomicBool::new(false)); + let bar_is_dropped = Arc::new(AtomicBool::new(false)); let foo = ExternRef::new(SetFlagOnDrop(foo_is_dropped.clone())); let bar = ExternRef::new(SetFlagOnDrop(bar_is_dropped.clone())); { let args = vec![Val::ExternRef(Some(foo))]; - table_set.call(&args)?; + table_set.call(&mut store, &args)?; } store.gc(); - assert!(!foo_is_dropped.get()); - assert!(!bar_is_dropped.get()); + assert!(!foo_is_dropped.load(SeqCst)); + assert!(!bar_is_dropped.load(SeqCst)); { let args = vec![Val::ExternRef(Some(bar))]; - table_set.call(&args)?; + table_set.call(&mut store, &args)?; } store.gc(); - assert!(foo_is_dropped.get()); - assert!(!bar_is_dropped.get()); + assert!(foo_is_dropped.load(SeqCst)); + assert!(!bar_is_dropped.load(SeqCst)); - table_set.call(&[Val::ExternRef(None)])?; - assert!(foo_is_dropped.get()); - assert!(bar_is_dropped.get()); + table_set.call(&mut store, &[Val::ExternRef(None)])?; + assert!(foo_is_dropped.load(SeqCst)); + assert!(bar_is_dropped.load(SeqCst)); Ok(()) } #[test] -fn gc_in_externref_dtor() -> anyhow::Result<()> { - let (store, module) = ref_types_module( - r#" - (module - (table $t 1 externref) +fn global_drops_externref() -> anyhow::Result<()> { + test_engine(&Engine::default())?; + test_engine(&Engine::new(Config::new().allocation_strategy( + InstanceAllocationStrategy::Pooling { + strategy: PoolingAllocationStrategy::NextAvailable, + module_limits: ModuleLimits::default(), + instance_limits: InstanceLimits::default(), + }, + ))?)?; - (func (export "table-set") (param externref) - (table.set $t (i32.const 0) (local.get 0)) - ) - ) - "#, - )?; - - let instance = Instance::new(&store, &module, &[])?; - let table_set = instance.get_func("table-set").unwrap(); - - let gc_count = Rc::new(Cell::new(0)); + return Ok(()); - // Put a `GcOnDrop` into the table. - { - let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop { - store: store.clone(), - gc_count: gc_count.clone(), - })))]; - table_set.call(&args)?; + fn test_engine(engine: &Engine) -> anyhow::Result<()> { + let mut store = Store::new(&engine, ()); + let flag = Arc::new(AtomicBool::new(false)); + let externref = ExternRef::new(SetFlagOnDrop(flag.clone())); + Global::new( + &mut store, + GlobalType::new(ValType::ExternRef, Mutability::Const), + externref.into(), + )?; + drop(store); + assert!(flag.load(SeqCst)); + + let mut store = Store::new(&engine, ()); + let module = Module::new( + &engine, + r#" + (module + (global (mut externref) (ref.null extern)) + + (func (export "run") (param externref) + local.get 0 + global.set 0 + ) + ) + "#, + )?; + let instance = Instance::new(&mut store, &module, &[])?; + let run = instance.get_typed_func::, (), _>(&mut store, "run")?; + let flag = Arc::new(AtomicBool::new(false)); + let externref = ExternRef::new(SetFlagOnDrop(flag.clone())); + run.call(&mut store, Some(externref))?; + drop(store); + assert!(flag.load(SeqCst)); + Ok(()) } - - // Remove the `GcOnDrop` from the `VMExternRefActivationsTable`. - store.gc(); - - // Overwrite the `GcOnDrop` table element, causing it to be dropped, and - // triggering a GC. - table_set.call(&[Val::ExternRef(None)])?; - assert_eq!(gc_count.get(), 1); - - Ok(()) } #[test] -fn touch_own_table_element_in_externref_dtor() -> anyhow::Result<()> { - let (store, module) = ref_types_module( - r#" - (module - (table $t (export "table") 1 externref) - - (func (export "table-set") (param externref) - (table.set $t (i32.const 0) (local.get 0)) - ) - ) - "#, - )?; - - let instance = Instance::new(&store, &module, &[])?; - let table = instance.get_table("table").unwrap(); - let table_set = instance.get_func("table-set").unwrap(); - - let touched = Rc::new(Cell::new(false)); - - { - let args = vec![Val::ExternRef(Some(ExternRef::new(TouchTableOnDrop { - table, - touched: touched.clone(), - })))]; - table_set.call(&args)?; - } - - // Remove the `TouchTableOnDrop` from the `VMExternRefActivationsTable`. - store.gc(); - - table_set.call(&[Val::ExternRef(Some(ExternRef::new("hello".to_string())))])?; - assert!(touched.get()); +fn table_drops_externref() -> anyhow::Result<()> { + test_engine(&Engine::default())?; + test_engine(&Engine::new(Config::new().allocation_strategy( + InstanceAllocationStrategy::Pooling { + strategy: PoolingAllocationStrategy::NextAvailable, + module_limits: ModuleLimits::default(), + instance_limits: InstanceLimits::default(), + }, + ))?)?; return Ok(()); - struct TouchTableOnDrop { - table: Table, - touched: Rc>, - } - - impl Drop for TouchTableOnDrop { - fn drop(&mut self) { - // From the `Drop` implementation, we see the new table element, not - // `self`. - let elem = self.table.get(0).unwrap().unwrap_externref().unwrap(); - assert!(elem.data().is::()); - assert_eq!(elem.data().downcast_ref::().unwrap(), "hello"); - self.touched.set(true); - } - } -} - -#[test] -fn gc_during_gc_when_passing_refs_into_wasm() -> anyhow::Result<()> { - let (store, module) = ref_types_module( - r#" + fn test_engine(engine: &Engine) -> anyhow::Result<()> { + let mut store = Store::new(&engine, ()); + let flag = Arc::new(AtomicBool::new(false)); + let externref = ExternRef::new(SetFlagOnDrop(flag.clone())); + Table::new( + &mut store, + TableType::new(ValType::ExternRef, Limits::new(1, None)), + externref.into(), + )?; + drop(store); + assert!(flag.load(SeqCst)); + + let mut store = Store::new(&engine, ()); + let module = Module::new( + &engine, + r#" (module - (table $t 1 externref) - (func (export "f") (param externref) - (table.set $t (i32.const 0) (local.get 0)) - ) - ) - "#, - )?; - - let instance = Instance::new(&store, &module, &[])?; - let f = instance.get_func("f").unwrap(); - - let gc_count = Rc::new(Cell::new(0)); - - for _ in 0..1024 { - let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop { - store: store.clone(), - gc_count: gc_count.clone(), - })))]; - f.call(&args)?; - } + (table 1 externref) - f.call(&[Val::ExternRef(None)])?; - store.gc(); - assert_eq!(gc_count.get(), 1024); - - Ok(()) -} - -#[test] -fn gc_during_gc_from_many_table_gets() -> anyhow::Result<()> { - let (store, module) = ref_types_module( - r#" - (module - (import "" "" (func $observe_ref (param externref))) - (table $t 1 externref) - (func (export "init") (param externref) - (table.set $t (i32.const 0) (local.get 0)) - ) - (func (export "run") (param i32) - (loop $continue - (if (i32.eqz (local.get 0)) (return)) - (call $observe_ref (table.get $t (i32.const 0))) - (local.set 0 (i32.sub (local.get 0) (i32.const 1))) - (br $continue) - ) + (func (export "run") (param externref) + i32.const 0 + local.get 0 + table.set 0 ) ) "#, - )?; - - let observe_ref = Func::wrap(&store, |_: Option| {}); - - let instance = Instance::new(&store, &module, &[observe_ref.into()])?; - let init = instance.get_func("init").unwrap(); - let run = instance.get_func("run").unwrap(); - - let gc_count = Rc::new(Cell::new(0)); - - // Initialize the table element with a `GcOnDrop`. This also puts it in the - // `VMExternRefActivationsTable`. - { - let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop { - store: store.clone(), - gc_count: gc_count.clone(), - })))]; - init.call(&args)?; + )?; + let instance = Instance::new(&mut store, &module, &[])?; + let run = instance.get_typed_func::, (), _>(&mut store, "run")?; + let flag = Arc::new(AtomicBool::new(false)); + let externref = ExternRef::new(SetFlagOnDrop(flag.clone())); + run.call(&mut store, Some(externref))?; + drop(store); + assert!(flag.load(SeqCst)); + Ok(()) } - - // Overwrite the `GcOnDrop` with another reference. The `GcOnDrop` is still - // in the `VMExternRefActivationsTable`. - { - let args = vec![Val::ExternRef(Some(ExternRef::new(String::from("hello"))))]; - init.call(&args)?; - } - - // Now call `run`, which does a bunch of `table.get`s, filling up the - // `VMExternRefActivationsTable`'s bump region, and eventually triggering a - // GC that will deallocate our `GcOnDrop` which will also trigger a nested - // GC. - run.call(&[Val::I32(1024)])?; - - // We should have done our nested GC. - assert_eq!(gc_count.get(), 1); - - Ok(()) } #[test] -fn pass_externref_into_wasm_during_destructor_in_gc() -> anyhow::Result<()> { - let (store, module) = ref_types_module( +fn gee_i_sure_hope_refcounting_is_atomic() -> anyhow::Result<()> { + let mut config = Config::new(); + config.wasm_reference_types(true); + config.interruptable(true); + let engine = Engine::new(&config)?; + let mut store = Store::new(&engine, ()); + let module = Module::new( + &engine, r#" (module - (table $t 1 externref) - - (func (export "f") (param externref) - nop - ) - ) - "#, - )?; - - let instance = Instance::new(&store, &module, &[])?; - let f = instance.get_func("f").unwrap(); - let r = ExternRef::new("hello"); - let did_call = Rc::new(Cell::new(false)); - - // Put a `CallOnDrop` into the `VMExternRefActivationsTable`. - { - let args = vec![Val::ExternRef(Some(ExternRef::new(CallOnDrop( - f.clone(), - r.clone(), - did_call.clone(), - ))))]; - f.call(&args)?; - } - - // One ref count for `r`, one for the `CallOnDrop`. - assert_eq!(r.strong_count(), 2); - - // Do a GC, which will see that the only reference holding the `CallOnDrop` - // is the `VMExternRefActivationsTable`, and will drop it. Dropping it will - // cause it to call into `f` again. - store.gc(); - assert!(did_call.get()); - - // The `CallOnDrop` is no longer holding onto `r`, but the - // `VMExternRefActivationsTable` is. - assert_eq!(r.strong_count(), 2); - - // GC again to empty the `VMExternRefActivationsTable`. Now `r` is the only - // thing holding its `externref` alive. - store.gc(); - assert_eq!(r.strong_count(), 1); - - return Ok(()); + (global (mut externref) (ref.null extern)) + (table 1 externref) - struct CallOnDrop(Func, ExternRef, Rc>); + (func (export "run") (param externref) + local.get 0 + global.set 0 + i32.const 0 + local.get 0 + table.set 0 + loop + global.get 0 + global.set 0 - impl Drop for CallOnDrop { - fn drop(&mut self) { - self.0 - .call(&[Val::ExternRef(Some(self.1.clone()))]) - .unwrap(); - self.2.set(true); - } - } -} + i32.const 0 + i32.const 0 + table.get + table.set -#[test] -fn gc_on_drop_in_mutable_externref_global() -> anyhow::Result<()> { - let (store, module) = ref_types_module( - r#" - (module - (global $g (mut externref) (ref.null extern)) + local.get 0 + call $f - (func (export "set-g") (param externref) - (global.set $g (local.get 0)) + br 0 + end ) + + (func $f (param externref)) ) "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let set_g = instance.get_func("set-g").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let run = instance.get_typed_func::, (), _>(&mut store, "run")?; - let gc_count = Rc::new(Cell::new(0)); + let flag = Arc::new(AtomicBool::new(false)); + let externref = ExternRef::new(SetFlagOnDrop(flag.clone())); + let externref2 = externref.clone(); + let handle = store.interrupt_handle()?; - // Put a `GcOnDrop` into the global. - { - let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop { - store: store.clone(), - gc_count: gc_count.clone(), - })))]; - set_g.call(&args)?; - } + let child = std::thread::spawn(move || run.call(&mut store, Some(externref2))); - // Remove the `GcOnDrop` from the `VMExternRefActivationsTable`. - store.gc(); + for _ in 0..10000 { + drop(externref.clone()); + } + handle.interrupt(); - // Overwrite the `GcOnDrop` global value, causing it to be dropped, and - // triggering a GC. - assert_eq!(gc_count.get(), 0); - set_g.call(&[Val::ExternRef(None)])?; - assert_eq!(gc_count.get(), 1); + assert!(child.join().unwrap().is_err()); + assert!(!flag.load(SeqCst)); + assert_eq!(externref.strong_count(), 1); + drop(externref); + assert!(flag.load(SeqCst)); Ok(()) } #[test] -fn touch_own_externref_global_on_drop() -> anyhow::Result<()> { - let (store, module) = ref_types_module( +fn global_init_no_leak() -> anyhow::Result<()> { + let (mut store, module) = ref_types_module( r#" (module - (global $g (export "g") (mut externref) (ref.null extern)) - - (func (export "set-g") (param externref) - (global.set $g (local.get 0)) - ) + (import "" "" (global externref)) + (global externref (global.get 0)) ) "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let g = instance.get_global("g").unwrap(); - let set_g = instance.get_func("set-g").unwrap(); - - let touched = Rc::new(Cell::new(false)); - - { - let args = vec![Val::ExternRef(Some(ExternRef::new(TouchGlobalOnDrop { - g, - touched: touched.clone(), - })))]; - set_g.call(&args)?; - } - - // Remove the `TouchGlobalOnDrop` from the `VMExternRefActivationsTable`. - store.gc(); - - assert!(!touched.get()); - set_g.call(&[Val::ExternRef(Some(ExternRef::new("hello".to_string())))])?; - assert!(touched.get()); - - return Ok(()); - - struct TouchGlobalOnDrop { - g: Global, - touched: Rc>, - } + let externref = ExternRef::new(()); + let global = Global::new( + &mut store, + GlobalType::new(ValType::ExternRef, Mutability::Const), + externref.clone().into(), + )?; + Instance::new(&mut store, &module, &[global.into()])?; + drop(store); + assert_eq!(externref.strong_count(), 1); - impl Drop for TouchGlobalOnDrop { - fn drop(&mut self) { - // From the `Drop` implementation, we see the new global value, not - // `self`. - let r = self.g.get().unwrap_externref().unwrap(); - assert!(r.data().is::()); - assert_eq!(r.data().downcast_ref::().unwrap(), "hello"); - self.touched.set(true); - } - } + Ok(()) } diff --git a/tests/all/globals.rs b/tests/all/globals.rs index 3a7c8b488cf1..7561a3877546 100644 --- a/tests/all/globals.rs +++ b/tests/all/globals.rs @@ -2,56 +2,56 @@ use wasmtime::*; #[test] fn smoke() -> anyhow::Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let g = Global::new( - &store, + &mut store, GlobalType::new(ValType::I32, Mutability::Const), 0.into(), )?; - assert_eq!(g.get().i32(), Some(0)); - assert!(g.set(0.into()).is_err()); + assert_eq!(g.get(&mut store).i32(), Some(0)); + assert!(g.set(&mut store, 0.into()).is_err()); let g = Global::new( - &store, + &mut store, GlobalType::new(ValType::I32, Mutability::Const), 1i32.into(), )?; - assert_eq!(g.get().i32(), Some(1)); + assert_eq!(g.get(&mut store).i32(), Some(1)); let g = Global::new( - &store, + &mut store, GlobalType::new(ValType::I64, Mutability::Const), 2i64.into(), )?; - assert_eq!(g.get().i64(), Some(2)); + assert_eq!(g.get(&mut store).i64(), Some(2)); let g = Global::new( - &store, + &mut store, GlobalType::new(ValType::F32, Mutability::Const), 3.0f32.into(), )?; - assert_eq!(g.get().f32(), Some(3.0)); + assert_eq!(g.get(&mut store).f32(), Some(3.0)); let g = Global::new( - &store, + &mut store, GlobalType::new(ValType::F64, Mutability::Const), 4.0f64.into(), )?; - assert_eq!(g.get().f64(), Some(4.0)); + assert_eq!(g.get(&mut store).f64(), Some(4.0)); Ok(()) } #[test] fn mutability() -> anyhow::Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let g = Global::new( - &store, + &mut store, GlobalType::new(ValType::I32, Mutability::Var), 0.into(), )?; - assert_eq!(g.get().i32(), Some(0)); - g.set(1.into())?; - assert_eq!(g.get().i32(), Some(1)); + assert_eq!(g.get(&mut store).i32(), Some(0)); + g.set(&mut store, 1.into())?; + assert_eq!(g.get(&mut store).i32(), Some(1)); Ok(()) } @@ -61,7 +61,7 @@ fn mutability() -> anyhow::Result<()> { // least some cases of heap corruption. #[test] fn use_after_drop() -> anyhow::Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new( store.engine(), r#" @@ -69,18 +69,16 @@ fn use_after_drop() -> anyhow::Result<()> { (global (export "foo") (mut i32) (i32.const 100))) "#, )?; - let instance = Instance::new(&store, &module, &[])?; - let g = instance.get_global("foo").unwrap(); - assert_eq!(g.get().i32(), Some(100)); - g.set(101.into())?; + let instance = Instance::new(&mut store, &module, &[])?; + let g = instance.get_global(&mut store, "foo").unwrap(); + assert_eq!(g.get(&mut store).i32(), Some(100)); + g.set(&mut store, 101.into())?; drop(instance); - assert_eq!(g.get().i32(), Some(101)); - Instance::new(&store, &module, &[])?; - assert_eq!(g.get().i32(), Some(101)); + assert_eq!(g.get(&mut store).i32(), Some(101)); + Instance::new(&mut store, &module, &[])?; + assert_eq!(g.get(&mut store).i32(), Some(101)); drop(module); - assert_eq!(g.get().i32(), Some(101)); - drop(store); - assert_eq!(g.get().i32(), Some(101)); + assert_eq!(g.get(&mut store).i32(), Some(101)); // spray some heap values let mut x = Vec::new(); @@ -88,6 +86,6 @@ fn use_after_drop() -> anyhow::Result<()> { x.push("xy".to_string()); } drop(x); - assert_eq!(g.get().i32(), Some(101)); + assert_eq!(g.get(&mut store).i32(), Some(101)); Ok(()) } diff --git a/tests/all/host_funcs.rs b/tests/all/host_funcs.rs index 360e2526adee..49fbaa576e08 100644 --- a/tests/all/host_funcs.rs +++ b/tests/all/host_funcs.rs @@ -1,49 +1,46 @@ use anyhow::Result; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use wasmtime::*; -use wasmtime_wasi::{sync::WasiCtxBuilder, Wasi}; +use wasmtime_wasi::sync::WasiCtxBuilder; #[test] +#[should_panic = "cannot use `func_new_async` without enabling async support"] fn async_required() { - let mut config = Config::default(); - config.define_host_func_async( + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); + drop(linker.func_new_async( "", "", FuncType::new(None, None), move |_caller, _params, _results| Box::new(async { Ok(()) }), - ); - - assert_eq!( - Engine::new(&config) - .map_err(|e| e.to_string()) - .err() - .unwrap(), - "an async host function cannot be defined without async support enabled in the config" - ); + )); } #[test] -fn wrap_func() { - let mut config = Config::default(); - - config.wrap_host_func("", "", || {}); - config.wrap_host_func("m", "f", |_: i32| {}); - config.wrap_host_func("m", "f2", |_: i32, _: i64| {}); - config.wrap_host_func("m2", "f", |_: f32, _: f64| {}); - config.wrap_host_func("m2", "f2", || -> i32 { 0 }); - config.wrap_host_func("", "", || -> i64 { 0 }); - config.wrap_host_func("m", "f", || -> f32 { 0.0 }); - config.wrap_host_func("m2", "f", || -> f64 { 0.0 }); - config.wrap_host_func("m3", "", || -> Option { None }); - config.wrap_host_func("m3", "f", || -> Option { None }); - - config.wrap_host_func("", "f1", || -> Result<(), Trap> { loop {} }); - config.wrap_host_func("", "f2", || -> Result { loop {} }); - config.wrap_host_func("", "f3", || -> Result { loop {} }); - config.wrap_host_func("", "f4", || -> Result { loop {} }); - config.wrap_host_func("", "f5", || -> Result { loop {} }); - config.wrap_host_func("", "f6", || -> Result, Trap> { loop {} }); - config.wrap_host_func("", "f7", || -> Result, Trap> { loop {} }); +fn wrap_func() -> Result<()> { + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); + linker.allow_shadowing(true); + + linker.func_wrap("", "", || {})?; + linker.func_wrap("m", "f", |_: i32| {})?; + linker.func_wrap("m", "f2", |_: i32, _: i64| {})?; + linker.func_wrap("m2", "f", |_: f32, _: f64| {})?; + linker.func_wrap("m2", "f2", || -> i32 { 0 })?; + linker.func_wrap("", "", || -> i64 { 0 })?; + linker.func_wrap("m", "f", || -> f32 { 0.0 })?; + linker.func_wrap("m2", "f", || -> f64 { 0.0 })?; + linker.func_wrap("m3", "", || -> Option { None })?; + linker.func_wrap("m3", "f", || -> Option { None })?; + + linker.func_wrap("", "f1", || -> Result<(), Trap> { loop {} })?; + linker.func_wrap("", "f2", || -> Result { loop {} })?; + linker.func_wrap("", "f3", || -> Result { loop {} })?; + linker.func_wrap("", "f4", || -> Result { loop {} })?; + linker.func_wrap("", "f5", || -> Result { loop {} })?; + linker.func_wrap("", "f6", || -> Result, Trap> { loop {} })?; + linker.func_wrap("", "f7", || -> Result, Trap> { loop {} })?; + Ok(()) } #[test] @@ -57,25 +54,27 @@ fn drop_func() -> Result<()> { } } - let mut config = Config::default(); + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); + linker.allow_shadowing(true); let a = A; - config.wrap_host_func("", "", move || { + linker.func_wrap("", "", move || { drop(&a); - }); + })?; assert_eq!(HITS.load(SeqCst), 0); // Define the function again to ensure redefined functions are dropped let a = A; - config.wrap_host_func("", "", move || { + linker.func_wrap("", "", move || { drop(&a); - }); + })?; assert_eq!(HITS.load(SeqCst), 1); - drop(config); + drop(linker); assert_eq!(HITS.load(SeqCst), 2); @@ -94,45 +93,33 @@ fn drop_delayed() -> Result<()> { } } - let mut config = Config::default(); + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); let a = A; - config.wrap_host_func("", "", move || drop(&a)); + linker.func_wrap("", "", move || drop(&a))?; assert_eq!(HITS.load(SeqCst), 0); - let engine = Engine::new(&config)?; let module = Module::new(&engine, &wat::parse_str(r#"(import "" "" (func))"#)?)?; - let store = Store::new(&engine); - let instance = Instance::new( - &store, - &module, - &[store - .get_host_func("", "") - .expect("function should be defined") - .into()], - )?; + let mut store = Store::new(&engine, ()); + let func = linker.get_one_by_name(&mut store, "", Some(""))?; + Instance::new(&mut store, &module, &[func])?; - drop((instance, store)); + drop(store); assert_eq!(HITS.load(SeqCst), 0); - let store = Store::new(&engine); - let instance = Instance::new( - &store, - &module, - &[store - .get_host_func("", "") - .expect("function should be defined") - .into()], - )?; + let mut store = Store::new(&engine, ()); + let func = linker.get_one_by_name(&mut store, "", Some(""))?; + Instance::new(&mut store, &module, &[func])?; - drop((instance, store, engine, module)); + drop(store); assert_eq!(HITS.load(SeqCst), 0); - drop(config); + drop(linker); assert_eq!(HITS.load(SeqCst), 1); @@ -141,67 +128,65 @@ fn drop_delayed() -> Result<()> { #[test] fn signatures_match() -> Result<()> { - let mut config = Config::default(); - - config.wrap_host_func("", "f1", || {}); - config.wrap_host_func("", "f2", || -> i32 { loop {} }); - config.wrap_host_func("", "f3", || -> i64 { loop {} }); - config.wrap_host_func("", "f4", || -> f32 { loop {} }); - config.wrap_host_func("", "f5", || -> f64 { loop {} }); - config.wrap_host_func( + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); + + linker.func_wrap("", "f1", || {})?; + linker.func_wrap("", "f2", || -> i32 { loop {} })?; + linker.func_wrap("", "f3", || -> i64 { loop {} })?; + linker.func_wrap("", "f4", || -> f32 { loop {} })?; + linker.func_wrap("", "f5", || -> f64 { loop {} })?; + linker.func_wrap( "", "f6", |_: f32, _: f64, _: i32, _: i64, _: i32, _: Option, _: Option| -> f64 { loop {} }, - ); - - let engine = Engine::new(&config)?; - let store = Store::new(&engine); - - let f = store - .get_host_func("", "f1") - .expect("func should be defined"); - - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.param_arity(), 0); - assert_eq!(f.ty().results().collect::>(), &[]); - assert_eq!(f.result_arity(), 0); - - let f = store - .get_host_func("", "f2") - .expect("func should be defined"); - - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::I32]); - - let f = store - .get_host_func("", "f3") - .expect("func should be defined"); - - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::I64]); - - let f = store - .get_host_func("", "f4") - .expect("func should be defined"); - - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::F32]); - - let f = store - .get_host_func("", "f5") - .expect("func should be defined"); - - assert_eq!(f.ty().params().collect::>(), &[]); - assert_eq!(f.ty().results().collect::>(), &[ValType::F64]); - - let f = store - .get_host_func("", "f6") - .expect("func should be defined"); + )?; + let mut store = Store::new(&engine, ()); + + let f = linker + .get_one_by_name(&mut store, "", Some("f1"))? + .into_func() + .unwrap(); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[]); + + let f = linker + .get_one_by_name(&mut store, "", Some("f2"))? + .into_func() + .unwrap(); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::I32]); + + let f = linker + .get_one_by_name(&mut store, "", Some("f3"))? + .into_func() + .unwrap(); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::I64]); + + let f = linker + .get_one_by_name(&mut store, "", Some("f4"))? + .into_func() + .unwrap(); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F32]); + + let f = linker + .get_one_by_name(&mut store, "", Some("f5"))? + .into_func() + .unwrap(); + assert_eq!(f.ty(&store).params().collect::>(), &[]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F64]); + + let f = linker + .get_one_by_name(&mut store, "", Some("f6"))? + .into_func() + .unwrap(); assert_eq!( - f.ty().params().collect::>(), + f.ty(&store).params().collect::>(), &[ ValType::F32, ValType::F64, @@ -212,7 +197,7 @@ fn signatures_match() -> Result<()> { ValType::FuncRef, ] ); - assert_eq!(f.ty().results().collect::>(), &[ValType::F64]); + assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F64]); Ok(()) } @@ -249,29 +234,36 @@ fn import_works() -> Result<()> { "#, )?; - let mut config = Config::new(); - config.wasm_reference_types(true); + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); - config.wrap_host_func("", "f1", || { + linker.func_wrap("", "f1", || { assert_eq!(HITS.fetch_add(1, SeqCst), 0); - }); + })?; - config.wrap_host_func("", "f2", |x: i32| -> i32 { + linker.func_wrap("", "f2", |x: i32| -> i32 { assert_eq!(x, 0); assert_eq!(HITS.fetch_add(1, SeqCst), 1); 1 - }); + })?; - config.wrap_host_func("", "f3", |x: i32, y: i64| { + linker.func_wrap("", "f3", |x: i32, y: i64| { assert_eq!(x, 2); assert_eq!(y, 3); assert_eq!(HITS.fetch_add(1, SeqCst), 2); - }); + })?; - config.wrap_host_func( + linker.func_wrap( "", "f4", - |a: i32, b: i64, c: i32, d: f32, e: f64, f: Option, g: Option| { + |mut caller: Caller<'_, _>, + a: i32, + b: i64, + c: i32, + d: f32, + e: f64, + f: Option, + g: Option| { assert_eq!(a, 100); assert_eq!(b, 200); assert_eq!(c, 300); @@ -281,44 +273,28 @@ fn import_works() -> Result<()> { f.as_ref().unwrap().data().downcast_ref::().unwrap(), "hello" ); - assert_eq!(g.as_ref().unwrap().call(&[]).unwrap()[0].unwrap_i32(), 42); + assert_eq!( + g.as_ref().unwrap().call(&mut caller, &[]).unwrap()[0].unwrap_i32(), + 42 + ); assert_eq!(HITS.fetch_add(1, SeqCst), 3); }, - ); + )?; - let engine = Engine::new(&config)?; let module = Module::new(&engine, &wasm)?; - let store = Store::new(&engine); - let instance = Instance::new( - &store, - &module, + let mut store = Store::new(&engine, ()); + let instance = linker.instantiate(&mut store, &module)?; + let run = instance.get_func(&mut store, "run").unwrap(); + let funcref = Val::FuncRef(Some(Func::wrap(&mut store, || -> i32 { 42 }))); + run.call( + &mut store, &[ - store - .get_host_func("", "f1") - .expect("should be defined") - .into(), - store - .get_host_func("", "f2") - .expect("should be defined") - .into(), - store - .get_host_func("", "f3") - .expect("should be defined") - .into(), - store - .get_host_func("", "f4") - .expect("should be defined") - .into(), + Val::ExternRef(Some(ExternRef::new("hello".to_string()))), + funcref, ], )?; - let run = instance.get_func("run").unwrap(); - run.call(&[ - Val::ExternRef(Some(ExternRef::new("hello".to_string()))), - Val::FuncRef(Some(Func::wrap(&store, || -> i32 { 42 }))), - ])?; - assert_eq!(HITS.load(SeqCst), 4); Ok(()) @@ -345,9 +321,10 @@ fn call_import_many_args() -> Result<()> { "#, )?; - let mut config = Config::new(); + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); - config.wrap_host_func( + linker.func_wrap( "", "host", |x1: i32, @@ -371,23 +348,13 @@ fn call_import_many_args() -> Result<()> { assert_eq!(x9, 9); assert_eq!(x10, 10); }, - ); - - let engine = Engine::new(&config)?; - let module = Module::new(&engine, &wasm)?; - - let store = Store::new(&engine); - let instance = Instance::new( - &store, - &module, - &[store - .get_host_func("", "host") - .expect("should be defined") - .into()], )?; - let run = instance.get_func("run").unwrap(); - run.call(&[])?; + let module = Module::new(&engine, &wasm)?; + let mut store = Store::new(&engine, ()); + let instance = linker.instantiate(&mut store, &module)?; + let run = instance.get_func(&mut store, "run").unwrap(); + run.call(&mut store, &[])?; Ok(()) } @@ -428,48 +395,55 @@ fn call_wasm_many_args() -> Result<()> { "#, )?; - let config = Config::new(); - let engine = Engine::new(&config)?; + let engine = Engine::default(); let module = Module::new(&engine, &wasm)?; - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - - let run = instance.get_func("run").unwrap(); - run.call(&[ - 1.into(), - 2.into(), - 3.into(), - 4.into(), - 5.into(), - 6.into(), - 7.into(), - 8.into(), - 9.into(), - 10.into(), - ])?; - - let typed_run = - instance.get_typed_func::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), ()>("run")?; - typed_run.call((1, 2, 3, 4, 5, 6, 7, 8, 9, 10))?; - - let test = instance.get_func("test").unwrap(); - test.call(&[])?; + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + + let run = instance.get_func(&mut store, "run").unwrap(); + run.call( + &mut store, + &[ + 1.into(), + 2.into(), + 3.into(), + 4.into(), + 5.into(), + 6.into(), + 7.into(), + 8.into(), + 9.into(), + 10.into(), + ], + )?; + + let typed_run = instance + .get_typed_func::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), (), _>( + &mut store, "run", + )?; + typed_run.call(&mut store, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))?; + + let test = instance.get_func(&mut store, "test").unwrap(); + test.call(&mut store, &[])?; Ok(()) } #[test] fn trap_smoke() -> Result<()> { - let mut config = Config::default(); - config.wrap_host_func("", "", || -> Result<(), Trap> { Err(Trap::new("test")) }); + let engine = Engine::default(); + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("", "", || -> Result<(), Trap> { Err(Trap::new("test")) })?; - let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); - let f = store.get_host_func("", "").expect("should be defined"); + let f = linker + .get_one_by_name(&mut store, "", Some(""))? + .into_func() + .unwrap(); - let err = f.call(&[]).unwrap_err().downcast::()?; + let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; assert!(err.to_string().contains("test")); assert!(err.i32_exit_status().is_none()); @@ -486,21 +460,18 @@ fn trap_import() -> Result<()> { "#, )?; - let mut config = Config::default(); - config.wrap_host_func("", "", || -> Result<(), Trap> { Err(Trap::new("foo")) }); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + linker.func_wrap("", "", || -> Result<(), Trap> { Err(Trap::new("foo")) })?; - let engine = Engine::new(&config)?; let module = Module::new(&engine, &wasm)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); - let trap = Instance::new( - &store, - &module, - &[store.get_host_func("", "").expect("defined").into()], - ) - .err() - .unwrap() - .downcast::()?; + let trap = linker + .instantiate(&mut store, &module) + .err() + .unwrap() + .downcast::()?; assert!(trap.to_string().contains("foo")); @@ -509,96 +480,122 @@ fn trap_import() -> Result<()> { #[test] fn new_from_signature() -> Result<()> { - let mut config = Config::default(); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); let ty = FuncType::new(None, None); - config.define_host_func("", "f1", ty, |_, _, _| panic!()); + linker.func_new("", "f1", ty, |_, _, _| panic!())?; let ty = FuncType::new(Some(ValType::I32), Some(ValType::F64)); - config.define_host_func("", "f2", ty, |_, _, _| panic!()); - - let engine = Engine::new(&config)?; - let store = Store::new(&engine); - - let f = store.get_host_func("", "f1").expect("func defined"); - assert!(f.typed::<(), ()>().is_ok()); - assert!(f.typed::<(), i32>().is_err()); - assert!(f.typed::().is_err()); - - let f = store.get_host_func("", "f2").expect("func defined"); - assert!(f.typed::<(), ()>().is_err()); - assert!(f.typed::<(), i32>().is_err()); - assert!(f.typed::().is_err()); - assert!(f.typed::().is_ok()); + linker.func_new("", "f2", ty, |_, _, _| panic!())?; + + let mut store = Store::new(&engine, ()); + + let f = linker + .get_one_by_name(&mut store, "", Some("f1"))? + .into_func() + .unwrap(); + assert!(f.typed::<(), (), _>(&store).is_ok()); + assert!(f.typed::<(), i32, _>(&store).is_err()); + assert!(f.typed::(&store).is_err()); + + let f = linker + .get_one_by_name(&mut store, "", Some("f2"))? + .into_func() + .unwrap(); + assert!(f.typed::<(), (), _>(&store).is_err()); + assert!(f.typed::<(), i32, _>(&store).is_err()); + assert!(f.typed::(&store).is_err()); + assert!(f.typed::(&store).is_ok()); Ok(()) } #[test] fn call_wrapped_func() -> Result<()> { - let mut config = Config::default(); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); - config.wrap_host_func("", "f1", |a: i32, b: i64, c: f32, d: f64| { + linker.func_wrap("", "f1", |a: i32, b: i64, c: f32, d: f64| { assert_eq!(a, 1); assert_eq!(b, 2); assert_eq!(c, 3.0); assert_eq!(d, 4.0); - }); + })?; - config.wrap_host_func("", "f2", || 1i32); + linker.func_wrap("", "f2", || 1i32)?; - config.wrap_host_func("", "f3", || 2i64); + linker.func_wrap("", "f3", || 2i64)?; - config.wrap_host_func("", "f4", || 3.0f32); + linker.func_wrap("", "f4", || 3.0f32)?; - config.wrap_host_func("", "f5", || 4.0f64); + linker.func_wrap("", "f5", || 4.0f64)?; - let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); - let f = store.get_host_func("", "f1").expect("func defined"); - f.call(&[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()])?; - f.typed::<(i32, i64, f32, f64), ()>()? - .call((1, 2, 3.0, 4.0))?; - - let f = store.get_host_func("", "f2").expect("func defined"); - let results = f.call(&[])?; + let f = linker + .get_one_by_name(&mut store, "", Some("f1"))? + .into_func() + .unwrap(); + f.call( + &mut store, + &[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()], + )?; + f.typed::<(i32, i64, f32, f64), (), _>(&store)? + .call(&mut store, (1, 2, 3.0, 4.0))?; + + let f = linker + .get_one_by_name(&mut store, "", Some("f2"))? + .into_func() + .unwrap(); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_i32(), 1); - assert_eq!(f.typed::<(), i32>()?.call(())?, 1); + assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1); - let f = store.get_host_func("", "f3").expect("func defined"); - let results = f.call(&[])?; + let f = linker + .get_one_by_name(&mut store, "", Some("f3"))? + .into_func() + .unwrap(); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_i64(), 2); - assert_eq!(f.typed::<(), i64>()?.call(())?, 2); + assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2); - let f = store.get_host_func("", "f4").expect("func defined"); - let results = f.call(&[])?; + let f = linker + .get_one_by_name(&mut store, "", Some("f4"))? + .into_func() + .unwrap(); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_f32(), 3.0); - assert_eq!(f.typed::<(), f32>()?.call(())?, 3.0); + assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0); - let f = store.get_host_func("", "f5").expect("func defined"); - let results = f.call(&[])?; + let f = linker + .get_one_by_name(&mut store, "", Some("f5"))? + .into_func() + .unwrap(); + let results = f.call(&mut store, &[])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_f64(), 4.0); - assert_eq!(f.typed::<(), f64>()?.call(())?, 4.0); + assert_eq!(f.typed::<(), f64, _>(&store)?.call(&mut store, ())?, 4.0); Ok(()) } #[test] fn func_return_nothing() -> Result<()> { - let mut config = Config::default(); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); let ty = FuncType::new(None, Some(ValType::I32)); - - config.define_host_func("", "", ty, |_, _, _| Ok(())); - - let engine = Engine::new(&config)?; - let store = Store::new(&engine); - let f = store.get_host_func("", "").expect("func defined"); - let err = f.call(&[]).unwrap_err().downcast::()?; + linker.func_new("", "", ty, |_, _, _| Ok(()))?; + + let mut store = Store::new(&engine, ()); + let f = linker + .get_one_by_name(&mut store, "", Some(""))? + .into_func() + .unwrap(); + let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; assert!(err .to_string() .contains("function attempted to return an incompatible value")); @@ -629,40 +626,44 @@ fn call_via_funcref() -> Result<()> { "#, )?; - let mut config = Config::default(); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); let a = A; - config.wrap_host_func("", "", move |x: i32, y: i32| { + linker.func_wrap("", "", move |x: i32, y: i32| { drop(&a); x + y - }); + })?; - let engine = Engine::new(&config)?; let module = Module::new(&engine, &wasm)?; - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let f = linker + .get_one_by_name(&mut store, "", Some(""))? + .into_func() + .unwrap(); let results = instance - .get_func("call") + .get_func(&mut store, "call") .unwrap() - .call(&[store.get_host_func("", "").expect("func defined").into()])?; + .call(&mut store, &[f.into()])?; assert_eq!(results.len(), 2); assert_eq!(results[0].unwrap_i32(), 7); { let f = results[1].unwrap_funcref().unwrap(); - let results = f.call(&[1.into(), 2.into()])?; + let results = f.call(&mut store, &[1.into(), 2.into()])?; assert_eq!(results.len(), 1); assert_eq!(results[0].unwrap_i32(), 3); } assert_eq!(HITS.load(SeqCst), 0); - drop((results, instance, store, module, engine)); + drop(store); assert_eq!(HITS.load(SeqCst), 0); - drop(config); + drop(linker); assert_eq!(HITS.load(SeqCst), 1); @@ -672,77 +673,34 @@ fn call_via_funcref() -> Result<()> { #[test] fn store_with_context() -> Result<()> { struct Ctx { - called: std::cell::Cell, + called: bool, } - let mut config = Config::default(); - - config.wrap_host_func("", "", |caller: Caller| { - let ctx = caller - .store() - .get::() - .expect("store should have context"); - ctx.called.set(true); - }); - - let engine = Engine::new(&config)?; - let store = Store::new(&engine); - assert!(store.get::().is_none()); - assert!(store - .set(Ctx { - called: std::cell::Cell::new(false) - }) - .is_ok()); - assert!(store - .set(Ctx { - called: std::cell::Cell::new(false) - }) - .is_err()); - assert!(!store.get::().unwrap().called.get()); - - let f = store.get_host_func("", "").expect("func defined"); - f.call(&[])?; - - assert!(store.get::().unwrap().called.get()); - - Ok(()) -} - -#[test] -fn wasi_imports_missing_context() -> Result<()> { - let mut config = Config::default(); - Wasi::add_to_config(&mut config); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); - let wasm = wat::parse_str( - r#" - (import "wasi_snapshot_preview1" "proc_exit" (func $__wasi_proc_exit (param i32))) - (memory (export "memory") 0) - (func (export "_start") - (call $__wasi_proc_exit (i32.const 123)) - ) - "#, - )?; - - let engine = Engine::new(&config)?; - let module = Module::new(&engine, wasm)?; - let store = Store::new(&engine); - let linker = Linker::new(&store); - let instance = linker.instantiate(&module)?; + linker.func_wrap("", "", |mut caller: Caller<'_, Ctx>| { + caller.data_mut().called = true; + })?; - let start = instance.get_typed_func::<(), ()>("_start")?; + let mut store = Store::new(&engine, Ctx { called: false }); - let trap = start.call(()).unwrap_err(); + let f = linker + .get_one_by_name(&mut store, "", Some(""))? + .into_func() + .unwrap(); + f.call(&mut store, &[])?; - assert!(trap.to_string().contains("context is missing in the store")); - assert!(trap.i32_exit_status().is_none()); + assert!(store.data().called); Ok(()) } #[test] fn wasi_imports() -> Result<()> { - let mut config = Config::default(); - Wasi::add_to_config(&mut config); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + wasmtime_wasi::add_to_linker(&mut linker)?; let wasm = wat::parse_str( r#" @@ -754,15 +712,12 @@ fn wasi_imports() -> Result<()> { "#, )?; - let engine = Engine::new(&config)?; let module = Module::new(&engine, wasm)?; - let store = Store::new(&engine); - assert!(Wasi::set_context(&store, WasiCtxBuilder::new().build()?).is_ok()); - let linker = Linker::new(&store); - let instance = linker.instantiate(&module)?; + let mut store = Store::new(&engine, WasiCtxBuilder::new().build()?); + let instance = linker.instantiate(&mut store, &module)?; - let start = instance.get_typed_func::<(), ()>("_start")?; - let trap = start.call(()).unwrap_err(); + let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?; + let trap = start.call(&mut store, ()).unwrap_err(); assert_eq!(trap.i32_exit_status(), Some(123)); Ok(()) diff --git a/tests/all/iloop.rs b/tests/all/iloop.rs index 847334782a90..550810d4e961 100644 --- a/tests/all/iloop.rs +++ b/tests/all/iloop.rs @@ -1,12 +1,12 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use wasmtime::*; -fn interruptable_store() -> Store { +fn interruptable_store() -> Store<()> { let engine = Engine::new(Config::new().interruptable(true)).unwrap(); - Store::new(&engine) + Store::new(&engine, ()) } -fn hugely_recursive_module(store: &Store) -> anyhow::Result { +fn hugely_recursive_module(engine: &Engine) -> anyhow::Result { let mut wat = String::new(); wat.push_str( r#" @@ -19,30 +19,30 @@ fn hugely_recursive_module(store: &Store) -> anyhow::Result { } wat.push_str("(func call 0)\n"); - Module::new(store.engine(), &wat) + Module::new(engine, &wat) } #[test] fn loops_interruptable() -> anyhow::Result<()> { - let store = interruptable_store(); + let mut store = interruptable_store(); let module = Module::new(store.engine(), r#"(func (export "loop") (loop br 0))"#)?; - let instance = Instance::new(&store, &module, &[])?; - let iloop = instance.get_typed_func::<(), ()>("loop")?; + let instance = Instance::new(&mut store, &module, &[])?; + let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?; store.interrupt_handle()?.interrupt(); - let trap = iloop.call(()).unwrap_err(); + let trap = iloop.call(&mut store, ()).unwrap_err(); assert!(trap.to_string().contains("wasm trap: interrupt")); Ok(()) } #[test] fn functions_interruptable() -> anyhow::Result<()> { - let store = interruptable_store(); - let module = hugely_recursive_module(&store)?; - let func = Func::wrap(&store, || {}); - let instance = Instance::new(&store, &module, &[func.into()])?; - let iloop = instance.get_typed_func::<(), ()>("loop")?; + let mut store = interruptable_store(); + let module = hugely_recursive_module(store.engine())?; + let func = Func::wrap(&mut store, || {}); + let instance = Instance::new(&mut store, &module, &[func.into()])?; + let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?; store.interrupt_handle()?.interrupt(); - let trap = iloop.call(()).unwrap_err(); + let trap = iloop.call(&mut store, ()).unwrap_err(); assert!( trap.to_string().contains("wasm trap: interrupt"), "{}", @@ -57,7 +57,7 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> { // the loop so we can count the number of loop iterations we've executed so // far. static HITS: AtomicUsize = AtomicUsize::new(0); - let store = interruptable_store(); + let mut store = interruptable_store(); let module = Module::new( store.engine(), r#" @@ -70,10 +70,10 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> { ) "#, )?; - let func = Func::wrap(&store, || { + let func = Func::wrap(&mut store, || { HITS.fetch_add(1, SeqCst); }); - let instance = Instance::new(&store, &module, &[func.into()])?; + let instance = Instance::new(&mut store, &module, &[func.into()])?; // Use the instance's interrupt handle to wait for it to enter the loop long // enough and then we signal an interrupt happens. @@ -88,8 +88,8 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> { // Enter the infinitely looping function and assert that our interrupt // handle does indeed actually interrupt the function. - let iloop = instance.get_typed_func::<(), ()>("loop")?; - let trap = iloop.call(()).unwrap_err(); + let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?; + let trap = iloop.call(&mut store, ()).unwrap_err(); thread.join().unwrap(); assert!( trap.to_string().contains("wasm trap: interrupt"), @@ -105,12 +105,12 @@ fn function_interrupt_from_afar() -> anyhow::Result<()> { // the loop so we can count the number of loop iterations we've executed so // far. static HITS: AtomicUsize = AtomicUsize::new(0); - let store = interruptable_store(); - let module = hugely_recursive_module(&store)?; - let func = Func::wrap(&store, || { + let mut store = interruptable_store(); + let module = hugely_recursive_module(store.engine())?; + let func = Func::wrap(&mut store, || { HITS.fetch_add(1, SeqCst); }); - let instance = Instance::new(&store, &module, &[func.into()])?; + let instance = Instance::new(&mut store, &module, &[func.into()])?; // Use the instance's interrupt handle to wait for it to enter the loop long // enough and then we signal an interrupt happens. @@ -124,8 +124,8 @@ fn function_interrupt_from_afar() -> anyhow::Result<()> { // Enter the infinitely looping function and assert that our interrupt // handle does indeed actually interrupt the function. - let iloop = instance.get_typed_func::<(), ()>("loop")?; - let trap = iloop.call(()).unwrap_err(); + let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?; + let trap = iloop.call(&mut store, ()).unwrap_err(); thread.join().unwrap(); assert!( trap.to_string().contains("wasm trap: interrupt"), diff --git a/tests/all/import_calling_export.rs b/tests/all/import_calling_export.rs index 3fc87e2d4663..e26e545aa63a 100644 --- a/tests/all/import_calling_export.rs +++ b/tests/all/import_calling_export.rs @@ -1,6 +1,4 @@ use anyhow::Result; -use std::cell::RefCell; -use std::rc::Rc; use wasmtime::*; #[test] @@ -16,39 +14,38 @@ fn test_import_calling_export() { ) "#; - let store = Store::default(); + let mut store = Store::>::default(); let module = Module::new(store.engine(), WAT).expect("failed to create module"); - let other = Rc::new(RefCell::new(None::)); - let other2 = Rc::downgrade(&other); - - let callback_func = Func::new(&store, FuncType::new(None, None), move |_, _, _| { - other2 - .upgrade() - .unwrap() - .borrow() - .as_ref() - .expect("expected a function ref") - .call(&[]) - .expect("expected function not to trap"); - Ok(()) - }); + let callback_func = Func::new( + &mut store, + FuncType::new(None, None), + move |mut caller, _, _| { + caller + .data() + .unwrap() + .call(&mut caller, &[]) + .expect("expected function not to trap"); + Ok(()) + }, + ); let imports = vec![callback_func.into()]; - let instance = - Instance::new(&store, &module, imports.as_slice()).expect("failed to instantiate module"); + let instance = Instance::new(&mut store, &module, imports.as_slice()) + .expect("failed to instantiate module"); let run_func = instance - .get_func("run") + .get_func(&mut store, "run") .expect("expected a run func in the module"); - *other.borrow_mut() = Some( - instance - .get_func("other") - .expect("expected an other func in the module"), - ); + let other_func = instance + .get_func(&mut store, "other") + .expect("expected an other func in the module"); + *store.data_mut() = Some(other_func); - run_func.call(&[]).expect("expected function not to trap"); + run_func + .call(&mut store, &[]) + .expect("expected function not to trap"); } #[test] @@ -62,11 +59,11 @@ fn test_returns_incorrect_type() -> Result<()> { ) "#; - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new(store.engine(), WAT)?; let callback_func = Func::new( - &store, + &mut store, FuncType::new(None, Some(ValType::I32)), |_, _, results| { // Evil! Returns I64 here instead of promised in the signature I32. @@ -76,14 +73,14 @@ fn test_returns_incorrect_type() -> Result<()> { ); let imports = vec![callback_func.into()]; - let instance = Instance::new(&store, &module, imports.as_slice())?; + let instance = Instance::new(&mut store, &module, imports.as_slice())?; let run_func = instance - .get_func("run") + .get_func(&mut store, "run") .expect("expected a run func in the module"); let trap = run_func - .call(&[]) + .call(&mut store, &[]) .expect_err("the execution should fail") .downcast::()?; assert!(trap diff --git a/tests/all/import_indexes.rs b/tests/all/import_indexes.rs index bfa6865270dc..94c58639b265 100644 --- a/tests/all/import_indexes.rs +++ b/tests/all/import_indexes.rs @@ -14,12 +14,12 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> { ) "#; - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new(store.engine(), WAT)?; let imports = [ Func::new( - &store, + &mut store, FuncType::new(None, Some(ValType::I32)), |_, params, results| { assert!(params.is_empty()); @@ -30,7 +30,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> { ) .into(), Func::new( - &store, + &mut store, FuncType::new(None, Some(ValType::F32)), |_, params, results| { assert!(params.is_empty()); @@ -41,10 +41,10 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> { ) .into(), ]; - let instance = Instance::new(&store, &module, &imports)?; + let instance = Instance::new(&mut store, &module, &imports)?; - let func = instance.get_typed_func::<(), i32>("foo")?; - let result = func.call(())?; + let func = instance.get_typed_func::<(), i32, _>(&mut store, "foo")?; + let result = func.call(&mut store, ())?; assert_eq!(result, 3); Ok(()) } diff --git a/tests/all/instance.rs b/tests/all/instance.rs index 3d8046110ce3..0a2b5562d074 100644 --- a/tests/all/instance.rs +++ b/tests/all/instance.rs @@ -3,12 +3,12 @@ use wasmtime::*; #[test] fn wrong_import_numbers() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?; - assert!(Instance::new(&store, &module, &[]).is_err()); - let func = Func::wrap(&store, || {}); - assert!(Instance::new(&store, &module, &[func.clone().into(), func.into()]).is_err()); + assert!(Instance::new(&mut store, &module, &[]).is_err()); + let func = Func::wrap(&mut store, || {}); + assert!(Instance::new(&mut store, &module, &[func.clone().into(), func.into()]).is_err()); Ok(()) } @@ -22,12 +22,49 @@ fn initializes_linear_memory() -> Result<()> { )"#; let module = Module::new(&Engine::default(), wat)?; - let store = Store::new(module.engine()); - let instance = Instance::new(&store, &module, &[])?; - let memory = instance.get_memory("memory").unwrap(); + let mut store = Store::new(module.engine(), ()); + let instance = Instance::new(&mut store, &module, &[])?; + let memory = instance.get_memory(&mut store, "memory").unwrap(); let mut bytes = [0; 12]; - memory.read(0, &mut bytes)?; + memory.read(&store, 0, &mut bytes)?; assert_eq!(bytes, "Hello World!".as_bytes()); Ok(()) } + +#[test] +fn linear_memory_limits() -> Result<()> { + test(&Engine::default())?; + test(&Engine::new(Config::new().allocation_strategy( + InstanceAllocationStrategy::Pooling { + strategy: PoolingAllocationStrategy::NextAvailable, + module_limits: ModuleLimits { + memory_pages: 65536, + ..ModuleLimits::default() + }, + instance_limits: InstanceLimits::default(), + }, + ))?)?; + return Ok(()); + + fn test(engine: &Engine) -> Result<()> { + let wat = r#" + (module + (memory 65535) + + (func (export "foo") (result i32) + i32.const 1 + memory.grow) + ) + "#; + let module = Module::new(engine, wat)?; + + let mut store = Store::new(engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let foo = instance.get_typed_func::<(), i32, _>(&mut store, "foo")?; + + assert_eq!(foo.call(&mut store, ())?, 65535); + assert_eq!(foo.call(&mut store, ())?, -1); + Ok(()) + } +} diff --git a/tests/all/invoke_func_via_table.rs b/tests/all/invoke_func_via_table.rs index cc77a028c7d0..ada0271881e7 100644 --- a/tests/all/invoke_func_via_table.rs +++ b/tests/all/invoke_func_via_table.rs @@ -3,7 +3,7 @@ use wasmtime::*; #[test] fn test_invoke_func_via_table() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module @@ -14,18 +14,19 @@ fn test_invoke_func_via_table() -> Result<()> { ) "#; let module = Module::new(store.engine(), wat).context("> Error compiling module!")?; - let instance = Instance::new(&store, &module, &[]).context("> Error instantiating module!")?; + let instance = + Instance::new(&mut store, &module, &[]).context("> Error instantiating module!")?; let f = instance - .get_table("table") + .get_table(&mut store, "table") .unwrap() - .get(0) + .get(&mut store, 0) .unwrap() .funcref() .unwrap() .unwrap() .clone(); - let result = f.call(&[]).unwrap(); + let result = f.call(&mut store, &[]).unwrap(); assert_eq!(result[0].unwrap_i64(), 42); Ok(()) } diff --git a/tests/all/limits.rs b/tests/all/limits.rs index f4c74be61228..74a90916feff 100644 --- a/tests/all/limits.rs +++ b/tests/all/limits.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use std::cell::RefCell; -use std::rc::Rc; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; +use std::sync::Arc; use wasmtime::*; #[test] @@ -11,47 +11,50 @@ fn test_limits() -> Result<()> { r#"(module (memory (export "m") 0) (table (export "t") 0 anyfunc))"#, )?; - let store = Store::new_with_limits( - &engine, + let mut store = Store::new(&engine, ()); + store.limiter( StoreLimitsBuilder::new() .memory_pages(10) .table_elements(5) .build(), ); - let instance = Instance::new(&store, &module, &[])?; + let instance = Instance::new(&mut store, &module, &[])?; // Test instance exports and host objects hitting the limit for memory in std::array::IntoIter::new([ - instance.get_memory("m").unwrap(), - Memory::new(&store, MemoryType::new(Limits::new(0, None)))?, + instance.get_memory(&mut store, "m").unwrap(), + Memory::new(&mut store, MemoryType::new(Limits::new(0, None)))?, ]) { - memory.grow(3)?; - memory.grow(5)?; - memory.grow(2)?; + memory.grow(&mut store, 3)?; + memory.grow(&mut store, 5)?; + memory.grow(&mut store, 2)?; assert_eq!( - memory.grow(1).map_err(|e| e.to_string()).unwrap_err(), + memory + .grow(&mut store, 1) + .map_err(|e| e.to_string()) + .unwrap_err(), "failed to grow memory by `1`" ); } // Test instance exports and host objects hitting the limit for table in std::array::IntoIter::new([ - instance.get_table("t").unwrap(), + instance.get_table(&mut store, "t").unwrap(), Table::new( - &store, + &mut store, TableType::new(ValType::FuncRef, Limits::new(0, None)), Val::FuncRef(None), )?, ]) { - table.grow(2, Val::FuncRef(None))?; - table.grow(1, Val::FuncRef(None))?; - table.grow(2, Val::FuncRef(None))?; + table.grow(&mut store, 2, Val::FuncRef(None))?; + table.grow(&mut store, 1, Val::FuncRef(None))?; + table.grow(&mut store, 2, Val::FuncRef(None))?; assert_eq!( table - .grow(1, Val::FuncRef(None)) + .grow(&mut store, 1, Val::FuncRef(None)) .map_err(|e| e.to_string()) .unwrap_err(), "failed to grow table by `1`" @@ -69,38 +72,42 @@ fn test_limits_memory_only() -> Result<()> { r#"(module (memory (export "m") 0) (table (export "t") 0 anyfunc))"#, )?; - let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memory_pages(10).build()); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().memory_pages(10).build()); - let instance = Instance::new(&store, &module, &[])?; + let instance = Instance::new(&mut store, &module, &[])?; // Test instance exports and host objects hitting the limit for memory in std::array::IntoIter::new([ - instance.get_memory("m").unwrap(), - Memory::new(&store, MemoryType::new(Limits::new(0, None)))?, + instance.get_memory(&mut store, "m").unwrap(), + Memory::new(&mut store, MemoryType::new(Limits::new(0, None)))?, ]) { - memory.grow(3)?; - memory.grow(5)?; - memory.grow(2)?; + memory.grow(&mut store, 3)?; + memory.grow(&mut store, 5)?; + memory.grow(&mut store, 2)?; assert_eq!( - memory.grow(1).map_err(|e| e.to_string()).unwrap_err(), + memory + .grow(&mut store, 1) + .map_err(|e| e.to_string()) + .unwrap_err(), "failed to grow memory by `1`" ); } // Test instance exports and host objects *not* hitting the limit for table in std::array::IntoIter::new([ - instance.get_table("t").unwrap(), + instance.get_table(&mut store, "t").unwrap(), Table::new( - &store, + &mut store, TableType::new(ValType::FuncRef, Limits::new(0, None)), Val::FuncRef(None), )?, ]) { - table.grow(2, Val::FuncRef(None))?; - table.grow(1, Val::FuncRef(None))?; - table.grow(2, Val::FuncRef(None))?; - table.grow(1, Val::FuncRef(None))?; + table.grow(&mut store, 2, Val::FuncRef(None))?; + table.grow(&mut store, 1, Val::FuncRef(None))?; + table.grow(&mut store, 2, Val::FuncRef(None))?; + table.grow(&mut store, 1, Val::FuncRef(None))?; } Ok(()) @@ -111,9 +118,10 @@ fn test_initial_memory_limits_exceeded() -> Result<()> { let engine = Engine::default(); let module = Module::new(&engine, r#"(module (memory (export "m") 11))"#)?; - let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memory_pages(10).build()); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().memory_pages(10).build()); - match Instance::new(&store, &module, &[]) { + match Instance::new(&mut store, &module, &[]) { Ok(_) => unreachable!(), Err(e) => assert_eq!( e.to_string(), @@ -121,7 +129,7 @@ fn test_initial_memory_limits_exceeded() -> Result<()> { ), } - match Memory::new(&store, MemoryType::new(Limits::new(25, None))) { + match Memory::new(&mut store, MemoryType::new(Limits::new(25, None))) { Ok(_) => unreachable!(), Err(e) => assert_eq!( e.to_string(), @@ -140,38 +148,38 @@ fn test_limits_table_only() -> Result<()> { r#"(module (memory (export "m") 0) (table (export "t") 0 anyfunc))"#, )?; - let store = - Store::new_with_limits(&engine, StoreLimitsBuilder::new().table_elements(5).build()); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().table_elements(5).build()); - let instance = Instance::new(&store, &module, &[])?; + let instance = Instance::new(&mut store, &module, &[])?; // Test instance exports and host objects *not* hitting the limit for memory in std::array::IntoIter::new([ - instance.get_memory("m").unwrap(), - Memory::new(&store, MemoryType::new(Limits::new(0, None)))?, + instance.get_memory(&mut store, "m").unwrap(), + Memory::new(&mut store, MemoryType::new(Limits::new(0, None)))?, ]) { - memory.grow(3)?; - memory.grow(5)?; - memory.grow(2)?; - memory.grow(1)?; + memory.grow(&mut store, 3)?; + memory.grow(&mut store, 5)?; + memory.grow(&mut store, 2)?; + memory.grow(&mut store, 1)?; } // Test instance exports and host objects hitting the limit for table in std::array::IntoIter::new([ - instance.get_table("t").unwrap(), + instance.get_table(&mut store, "t").unwrap(), Table::new( - &store, + &mut store, TableType::new(ValType::FuncRef, Limits::new(0, None)), Val::FuncRef(None), )?, ]) { - table.grow(2, Val::FuncRef(None))?; - table.grow(1, Val::FuncRef(None))?; - table.grow(2, Val::FuncRef(None))?; + table.grow(&mut store, 2, Val::FuncRef(None))?; + table.grow(&mut store, 1, Val::FuncRef(None))?; + table.grow(&mut store, 2, Val::FuncRef(None))?; assert_eq!( table - .grow(1, Val::FuncRef(None)) + .grow(&mut store, 1, Val::FuncRef(None)) .map_err(|e| e.to_string()) .unwrap_err(), "failed to grow table by `1`" @@ -186,10 +194,10 @@ fn test_initial_table_limits_exceeded() -> Result<()> { let engine = Engine::default(); let module = Module::new(&engine, r#"(module (table (export "t") 23 anyfunc))"#)?; - let store = - Store::new_with_limits(&engine, StoreLimitsBuilder::new().table_elements(4).build()); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().table_elements(4).build()); - match Instance::new(&store, &module, &[]) { + match Instance::new(&mut store, &module, &[]) { Ok(_) => unreachable!(), Err(e) => assert_eq!( e.to_string(), @@ -198,7 +206,7 @@ fn test_initial_table_limits_exceeded() -> Result<()> { } match Table::new( - &store, + &mut store, TableType::new(ValType::FuncRef, Limits::new(99, None)), Val::FuncRef(None), ) { @@ -234,9 +242,10 @@ fn test_pooling_allocator_initial_limits_exceeded() -> Result<()> { r#"(module (memory (export "m1") 2) (memory (export "m2") 5))"#, )?; - let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memory_pages(3).build()); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().memory_pages(3).build()); - match Instance::new(&store, &module, &[]) { + match Instance::new(&mut store, &module, &[]) { Ok(_) => unreachable!(), Err(e) => assert_eq!( e.to_string(), @@ -247,135 +256,136 @@ fn test_pooling_allocator_initial_limits_exceeded() -> Result<()> { // An instance should still be able to be created after the failure above let module = Module::new(&engine, r#"(module (memory (export "m") 2))"#)?; - Instance::new(&store, &module, &[])?; + Instance::new(&mut store, &module, &[])?; Ok(()) } struct MemoryContext { - host_memory_used: usize, - wasm_memory_used: usize, + host_memory_used: AtomicUsize, + wasm_memory_used: AtomicUsize, memory_limit: usize, - limit_exceeded: bool, - limiter_dropped: bool, + limit_exceeded: AtomicBool, + limiter_dropped: AtomicBool, } -struct HostMemoryLimiter(Rc>); +struct HostMemoryLimiter(Arc); impl ResourceLimiter for HostMemoryLimiter { - fn memory_growing(&self, current: u32, desired: u32, maximum: Option) -> bool { - let mut ctx = self.0.borrow_mut(); - + fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option) -> bool { // Check if the desired exceeds a maximum (either from Wasm or from the host) if desired > maximum.unwrap_or(u32::MAX) { - ctx.limit_exceeded = true; + self.0.limit_exceeded.store(true, SeqCst); return false; } - assert_eq!(current as usize * 0x10000, ctx.wasm_memory_used); + assert_eq!( + current as usize * 0x10000, + self.0.wasm_memory_used.load(SeqCst) + ); let desired = desired as usize * 0x10000; - if desired + ctx.host_memory_used > ctx.memory_limit { - ctx.limit_exceeded = true; + if desired + self.0.host_memory_used.load(SeqCst) > self.0.memory_limit { + self.0.limit_exceeded.store(true, SeqCst); return false; } - ctx.wasm_memory_used = desired; + self.0.wasm_memory_used.store(desired, SeqCst); true } - fn table_growing(&self, _current: u32, _desired: u32, _maximum: Option) -> bool { + fn table_growing(&mut self, _current: u32, _desired: u32, _maximum: Option) -> bool { true } } impl Drop for HostMemoryLimiter { fn drop(&mut self) { - self.0.borrow_mut().limiter_dropped = true; + self.0.limiter_dropped.store(true, SeqCst); } } #[test] fn test_custom_limiter() -> Result<()> { - let mut config = Config::default(); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); // This approximates a function that would "allocate" resources that the host tracks. // Here this is a simple function that increments the current host memory "used". - config.wrap_host_func("", "alloc", |caller: Caller, size: u32| -> u32 { - if let Some(ctx) = caller.store().get::>>() { - let mut ctx = ctx.borrow_mut(); + linker.func_wrap( + "", + "alloc", + |caller: Caller<'_, Arc>, size: u32| -> u32 { + let ctx = caller.data(); let size = size as usize; - if size + ctx.host_memory_used + ctx.wasm_memory_used <= ctx.memory_limit { - ctx.host_memory_used += size; + if size + ctx.host_memory_used.load(SeqCst) + ctx.wasm_memory_used.load(SeqCst) + <= ctx.memory_limit + { + ctx.host_memory_used.fetch_add(size, SeqCst); return 1; } - ctx.limit_exceeded = true; - } + ctx.limit_exceeded.store(true, SeqCst); - 0 - }); + 0 + }, + )?; - let engine = Engine::new(&config)?; let module = Module::new( &engine, r#"(module (import "" "alloc" (func $alloc (param i32) (result i32))) (memory (export "m") 0) (func (export "f") (param i32) (result i32) local.get 0 call $alloc))"#, )?; - let context = Rc::new(RefCell::new(MemoryContext { - host_memory_used: 0, - wasm_memory_used: 0, + let context = Arc::new(MemoryContext { + host_memory_used: AtomicUsize::new(0), + wasm_memory_used: AtomicUsize::new(0), memory_limit: 1 << 20, // 16 wasm pages is the limit for both wasm + host memory - limit_exceeded: false, - limiter_dropped: false, - })); - - let store = Store::new_with_limits(&engine, HostMemoryLimiter(context.clone())); - - assert!(store.set(context.clone()).is_ok()); + limit_exceeded: AtomicBool::new(false), + limiter_dropped: AtomicBool::new(false), + }); - let linker = Linker::new(&store); - let instance = linker.instantiate(&module)?; - let memory = instance.get_memory("m").unwrap(); + let mut store = Store::new(&engine, context.clone()); + store.limiter(HostMemoryLimiter(context.clone())); + let instance = linker.instantiate(&mut store, &module)?; + let memory = instance.get_memory(&mut store, "m").unwrap(); // Grow the memory by 640 KiB - memory.grow(3)?; - memory.grow(5)?; - memory.grow(2)?; + memory.grow(&mut store, 3)?; + memory.grow(&mut store, 5)?; + memory.grow(&mut store, 2)?; - assert!(!context.borrow().limit_exceeded); + assert!(!context.limit_exceeded.load(SeqCst)); // Grow the host "memory" by 384 KiB - let f = instance.get_typed_func::("f")?; + let f = instance.get_typed_func::(&mut store, "f")?; - assert_eq!(f.call(1 * 0x10000).unwrap(), 1); - assert_eq!(f.call(3 * 0x10000).unwrap(), 1); - assert_eq!(f.call(2 * 0x10000).unwrap(), 1); + assert_eq!(f.call(&mut store, 1 * 0x10000)?, 1); + assert_eq!(f.call(&mut store, 3 * 0x10000)?, 1); + assert_eq!(f.call(&mut store, 2 * 0x10000)?, 1); // Memory is at the maximum, but the limit hasn't been exceeded - assert!(!context.borrow().limit_exceeded); + assert!(!context.limit_exceeded.load(SeqCst)); // Try to grow the memory again assert_eq!( - memory.grow(1).map_err(|e| e.to_string()).unwrap_err(), + memory + .grow(&mut store, 1) + .map_err(|e| e.to_string()) + .unwrap_err(), "failed to grow memory by `1`" ); - assert!(context.borrow().limit_exceeded); + assert!(context.limit_exceeded.load(SeqCst)); // Try to grow the host "memory" again - assert_eq!(f.call(1).unwrap(), 0); + assert_eq!(f.call(&mut store, 1)?, 0); - assert!(context.borrow().limit_exceeded); + assert!(context.limit_exceeded.load(SeqCst)); - drop(f); - drop(memory); - drop(instance); - drop(linker); drop(store); - assert!(context.borrow().limiter_dropped); + assert!(context.limiter_dropped.load(SeqCst)); Ok(()) } diff --git a/tests/all/linker.rs b/tests/all/linker.rs index 2f1707dd7178..333b238728aa 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -1,89 +1,91 @@ use anyhow::Result; use std::cell::Cell; use std::rc::Rc; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use std::sync::Arc; use wasmtime::*; #[test] fn link_undefined() -> Result<()> { - let store = Store::default(); - let linker = Linker::new(&store); + let mut store = Store::<()>::default(); + let linker = Linker::new(store.engine()); let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?; - assert!(linker.instantiate(&module).is_err()); + assert!(linker.instantiate(&mut store, &module).is_err()); let module = Module::new(store.engine(), r#"(module (import "" "" (global i32)))"#)?; - assert!(linker.instantiate(&module).is_err()); + assert!(linker.instantiate(&mut store, &module).is_err()); let module = Module::new(store.engine(), r#"(module (import "" "" (memory 1)))"#)?; - assert!(linker.instantiate(&module).is_err()); + assert!(linker.instantiate(&mut store, &module).is_err()); let module = Module::new( store.engine(), r#"(module (import "" "" (table 1 funcref)))"#, )?; - assert!(linker.instantiate(&module).is_err()); + assert!(linker.instantiate(&mut store, &module).is_err()); Ok(()) } #[test] fn link_twice_bad() -> Result<()> { - let store = Store::default(); - let mut linker = Linker::new(&store); + let mut store = Store::<()>::default(); + let mut linker = Linker::<()>::new(store.engine()); // functions - linker.func("f", "", || {})?; - assert!(linker.func("f", "", || {}).is_err()); + linker.func_wrap("f", "", || {})?; + assert!(linker.func_wrap("f", "", || {}).is_err()); assert!(linker - .func("f", "", || -> Result<(), Trap> { loop {} }) + .func_wrap("f", "", || -> Result<(), Trap> { loop {} }) .is_err()); // globals let ty = GlobalType::new(ValType::I32, Mutability::Const); - let global = Global::new(&store, ty, Val::I32(0))?; + let global = Global::new(&mut store, ty, Val::I32(0))?; linker.define("g", "1", global.clone())?; assert!(linker.define("g", "1", global.clone()).is_err()); let ty = GlobalType::new(ValType::I32, Mutability::Var); - let global = Global::new(&store, ty, Val::I32(0))?; + let global = Global::new(&mut store, ty, Val::I32(0))?; linker.define("g", "2", global.clone())?; assert!(linker.define("g", "2", global.clone()).is_err()); let ty = GlobalType::new(ValType::I64, Mutability::Const); - let global = Global::new(&store, ty, Val::I64(0))?; + let global = Global::new(&mut store, ty, Val::I64(0))?; linker.define("g", "3", global.clone())?; assert!(linker.define("g", "3", global.clone()).is_err()); // memories let ty = MemoryType::new(Limits::new(1, None)); - let memory = Memory::new(&store, ty)?; + let memory = Memory::new(&mut store, ty)?; linker.define("m", "", memory.clone())?; assert!(linker.define("m", "", memory.clone()).is_err()); let ty = MemoryType::new(Limits::new(2, None)); - let memory = Memory::new(&store, ty)?; + let memory = Memory::new(&mut store, ty)?; assert!(linker.define("m", "", memory.clone()).is_err()); // tables let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); - let table = Table::new(&store, ty, Val::FuncRef(None))?; + let table = Table::new(&mut store, ty, Val::FuncRef(None))?; linker.define("t", "", table.clone())?; assert!(linker.define("t", "", table.clone()).is_err()); let ty = TableType::new(ValType::FuncRef, Limits::new(2, None)); - let table = Table::new(&store, ty, Val::FuncRef(None))?; + let table = Table::new(&mut store, ty, Val::FuncRef(None))?; assert!(linker.define("t", "", table.clone()).is_err()); Ok(()) } #[test] fn function_interposition() -> Result<()> { - let store = Store::default(); - let mut linker = Linker::new(&store); + let mut store = Store::<()>::default(); + let mut linker = Linker::new(store.engine()); linker.allow_shadowing(true); let mut module = Module::new( store.engine(), r#"(module (func (export "green") (result i32) (i32.const 7)))"#, )?; for _ in 0..4 { - let instance = linker.instantiate(&module)?; + let instance = linker.instantiate(&mut store, &module)?; linker.define( "red", "green", - instance.get_export("green").unwrap().clone(), + instance.get_export(&mut store, "green").unwrap().clone(), )?; module = Module::new( store.engine(), @@ -93,10 +95,14 @@ fn function_interposition() -> Result<()> { )"#, )?; } - let instance = linker.instantiate(&module)?; - let func = instance.get_export("green").unwrap().into_func().unwrap(); - let func = func.typed::<(), i32>()?; - assert_eq!(func.call(())?, 112); + let instance = linker.instantiate(&mut store, &module)?; + let func = instance + .get_export(&mut store, "green") + .unwrap() + .into_func() + .unwrap(); + let func = func.typed::<(), i32, _>(&store)?; + assert_eq!(func.call(&mut store, ())?, 112); Ok(()) } @@ -104,19 +110,19 @@ fn function_interposition() -> Result<()> { // differs from the module's name. #[test] fn function_interposition_renamed() -> Result<()> { - let store = Store::default(); - let mut linker = Linker::new(&store); + let mut store = Store::<()>::default(); + let mut linker = Linker::new(store.engine()); linker.allow_shadowing(true); let mut module = Module::new( store.engine(), r#"(module (func (export "export") (result i32) (i32.const 7)))"#, )?; for _ in 0..4 { - let instance = linker.instantiate(&module)?; + let instance = linker.instantiate(&mut store, &module)?; linker.define( "red", "green", - instance.get_export("export").unwrap().clone(), + instance.get_export(&mut store, "export").unwrap().clone(), )?; module = Module::new( store.engine(), @@ -126,10 +132,10 @@ fn function_interposition_renamed() -> Result<()> { )"#, )?; } - let instance = linker.instantiate(&module)?; - let func = instance.get_func("export").unwrap(); - let func = func.typed::<(), i32>()?; - assert_eq!(func.call(())?, 112); + let instance = linker.instantiate(&mut store, &module)?; + let func = instance.get_func(&mut store, "export").unwrap(); + let func = func.typed::<(), i32, _>(&store)?; + assert_eq!(func.call(&mut store, ())?, 112); Ok(()) } @@ -137,16 +143,16 @@ fn function_interposition_renamed() -> Result<()> { // `Linker::define`. #[test] fn module_interposition() -> Result<()> { - let store = Store::default(); - let mut linker = Linker::new(&store); + let mut store = Store::<()>::default(); + let mut linker = Linker::new(store.engine()); linker.allow_shadowing(true); let mut module = Module::new( store.engine(), r#"(module (func (export "export") (result i32) (i32.const 7)))"#, )?; for _ in 0..4 { - let instance = linker.instantiate(&module)?; - linker.instance("instance", &instance)?; + let instance = linker.instantiate(&mut store, &module)?; + linker.instance(&mut store, "instance", instance)?; module = Module::new( store.engine(), r#"(module @@ -155,10 +161,14 @@ fn module_interposition() -> Result<()> { )"#, )?; } - let instance = linker.instantiate(&module)?; - let func = instance.get_export("export").unwrap().into_func().unwrap(); - let func = func.typed::<(), i32>()?; - assert_eq!(func.call(())?, 112); + let instance = linker.instantiate(&mut store, &module)?; + let func = instance + .get_export(&mut store, "export") + .unwrap() + .into_func() + .unwrap(); + let func = func.typed::<(), i32, _>(&store)?; + assert_eq!(func.call(&mut store, ())?, 112); Ok(()) } @@ -174,10 +184,8 @@ fn no_leak() -> Result<()> { let flag = Rc::new(Cell::new(false)); { - let store = Store::default(); - let mut linker = Linker::new(&store); - let drop_me = DropMe(flag.clone()); - linker.func("", "", move || drop(&drop_me))?; + let mut store = Store::new(&Engine::default(), DropMe(flag.clone())); + let mut linker = Linker::new(store.engine()); let module = Module::new( store.engine(), r#" @@ -186,7 +194,7 @@ fn no_leak() -> Result<()> { ) "#, )?; - linker.module("a", &module)?; + linker.module(&mut store, "a", &module)?; } assert!(flag.get(), "store was leaked"); Ok(()) @@ -194,20 +202,20 @@ fn no_leak() -> Result<()> { #[test] fn no_leak_with_imports() -> Result<()> { - struct DropMe(Rc>); + struct DropMe(Arc); impl Drop for DropMe { fn drop(&mut self) { - self.0.set(true); + self.0.fetch_add(1, SeqCst); } } - let flag = Rc::new(Cell::new(false)); + let flag = Arc::new(AtomicUsize::new(0)); { - let store = Store::default(); - let mut linker = Linker::new(&store); + let mut store = Store::new(&Engine::default(), DropMe(flag.clone())); + let mut linker = Linker::new(store.engine()); let drop_me = DropMe(flag.clone()); - linker.func("", "", move || drop(&drop_me))?; + linker.func_wrap("", "", move || drop(&drop_me))?; let module = Module::new( store.engine(), r#" @@ -217,45 +225,56 @@ fn no_leak_with_imports() -> Result<()> { ) "#, )?; - linker.module("a", &module)?; + linker.module(&mut store, "a", &module)?; } - assert!(flag.get(), "store was leaked"); + assert!(flag.load(SeqCst) == 2, "something was leaked"); Ok(()) } #[test] fn get_host_function() -> Result<()> { - let mut config = Config::default(); - config.wrap_host_func("mod", "f1", || {}); - - let engine = Engine::new(&config)?; + let engine = Engine::default(); let module = Module::new(&engine, r#"(module (import "mod" "f1" (func)))"#)?; - let store = Store::new(&engine); - let linker = Linker::new(&store); - assert!(linker.get(&module.imports().nth(0).unwrap()).is_some()); + let mut linker = Linker::new(&engine); + linker.func_wrap("mod", "f1", || {})?; + let mut store = Store::<()>::default(); + assert!(linker + .get(&mut store, &module.imports().nth(0).unwrap()) + .is_some()); Ok(()) } #[test] -fn shadowing_host_function() -> Result<()> { - let mut config = Config::default(); - config.wrap_host_func("mod", "f1", || {}); +fn funcs_live_on_to_fight_another_day() -> Result<()> { + struct DropMe(Arc); - let engine = Engine::new(&config)?; - let store = Store::new(&engine); + impl Drop for DropMe { + fn drop(&mut self) { + self.0.fetch_add(1, SeqCst); + } + } - let mut linker = Linker::new(&store); - assert!(linker - .define("mod", "f1", Func::wrap(&store, || {})) - .is_err()); - linker.define("mod", "f2", Func::wrap(&store, || {}))?; + let flag = Arc::new(AtomicUsize::new(0)); + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + let drop_me = DropMe(flag.clone()); + linker.func_wrap("", "", move || drop(&drop_me))?; + assert_eq!(flag.load(SeqCst), 0); - let mut linker = Linker::new(&store); - linker.allow_shadowing(true); - linker.define("mod", "f1", Func::wrap(&store, || {}))?; - linker.define("mod", "f2", Func::wrap(&store, || {}))?; + let get_and_call = || -> Result<()> { + assert_eq!(flag.load(SeqCst), 0); + let mut store = Store::new(&engine, ()); + let func = linker.get_one_by_name(&mut store, "", Some(""))?; + func.into_func().unwrap().call(&mut store, &[])?; + assert_eq!(flag.load(SeqCst), 0); + Ok(()) + }; + get_and_call()?; + get_and_call()?; + drop(linker); + assert_eq!(flag.load(SeqCst), 1); Ok(()) } diff --git a/tests/all/main.rs b/tests/all/main.rs index 89c686119e42..838a78e5e651 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -24,7 +24,6 @@ mod pooling_allocator; mod stack_overflow; mod table; mod traps; -mod use_after_drop; mod wast; // TODO(#1886): Cranelift only supports reference types on x64. @@ -37,7 +36,7 @@ mod gc; #[cfg(target_arch = "x86_64")] pub(crate) fn ref_types_module( source: &str, -) -> anyhow::Result<(wasmtime::Store, wasmtime::Module)> { +) -> anyhow::Result<(wasmtime::Store<()>, wasmtime::Module)> { use wasmtime::*; let _ = env_logger::try_init(); @@ -46,7 +45,7 @@ pub(crate) fn ref_types_module( config.wasm_reference_types(true); let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let store = Store::new(&engine, ()); let module = Module::new(&engine, source)?; diff --git a/tests/all/memory_creator.rs b/tests/all/memory_creator.rs index 5c49414706ef..cefd41e0dc88 100644 --- a/tests/all/memory_creator.rs +++ b/tests/all/memory_creator.rs @@ -3,22 +3,20 @@ mod not_for_windows { use wasmtime::*; use wasmtime_environ::{WASM_MAX_PAGES, WASM_PAGE_SIZE}; - use libc::c_void; use libc::MAP_FAILED; use libc::{mmap, mprotect, munmap}; use libc::{sysconf, _SC_PAGESIZE}; use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - use std::cell::RefCell; use std::io::Error; use std::ptr::null_mut; use std::sync::{Arc, Mutex}; struct CustomMemory { - mem: *mut c_void, + mem: usize, size: usize, guard_size: usize, - used_wasm_pages: RefCell, + used_wasm_pages: u32, glob_page_counter: Arc>, } @@ -42,10 +40,10 @@ mod not_for_windows { *glob_counter.lock().unwrap() += num_wasm_pages as u64; Self { - mem, + mem: mem as usize, size, guard_size, - used_wasm_pages: RefCell::new(num_wasm_pages), + used_wasm_pages: num_wasm_pages, glob_page_counter: glob_counter, } } @@ -53,26 +51,26 @@ mod not_for_windows { impl Drop for CustomMemory { fn drop(&mut self) { - let n = *self.used_wasm_pages.borrow() as u64; + let n = self.used_wasm_pages as u64; *self.glob_page_counter.lock().unwrap() -= n; - let r = unsafe { munmap(self.mem, self.size) }; + let r = unsafe { munmap(self.mem as *mut _, self.size) }; assert_eq!(r, 0, "munmap failed: {}", Error::last_os_error()); } } unsafe impl LinearMemory for CustomMemory { fn size(&self) -> u32 { - *self.used_wasm_pages.borrow() + self.used_wasm_pages } fn maximum(&self) -> Option { Some((self.size as u32 - self.guard_size as u32) / WASM_PAGE_SIZE) } - fn grow(&self, delta: u32) -> Option { + fn grow(&mut self, delta: u32) -> Option { let delta_size = (delta as usize).checked_mul(WASM_PAGE_SIZE as usize)?; - let prev_pages = *self.used_wasm_pages.borrow(); + let prev_pages = self.used_wasm_pages; let prev_size = (prev_pages as usize).checked_mul(WASM_PAGE_SIZE as usize)?; let new_pages = prev_pages.checked_add(delta)?; @@ -87,7 +85,7 @@ mod not_for_windows { } *self.glob_page_counter.lock().unwrap() += delta as u64; - *self.used_wasm_pages.borrow_mut() = new_pages; + self.used_wasm_pages = new_pages; Some(prev_pages) } @@ -132,19 +130,19 @@ mod not_for_windows { } } - fn config() -> (Store, Arc) { + fn config() -> (Store<()>, Arc) { let mem_creator = Arc::new(CustomMemoryCreator::new()); let mut config = Config::new(); config .with_host_memory(mem_creator.clone()) .static_memory_maximum_size(0) .dynamic_memory_guard_size(0); - (Store::new(&Engine::new(&config).unwrap()), mem_creator) + (Store::new(&Engine::new(&config).unwrap(), ()), mem_creator) } #[test] fn host_memory() -> anyhow::Result<()> { - let (store, mem_creator) = config(); + let (mut store, mem_creator) = config(); let module = Module::new( store.engine(), r#" @@ -153,7 +151,7 @@ mod not_for_windows { ) "#, )?; - Instance::new(&store, &module, &[])?; + Instance::new(&mut store, &module, &[])?; assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 1); @@ -162,7 +160,7 @@ mod not_for_windows { #[test] fn host_memory_grow() -> anyhow::Result<()> { - let (store, mem_creator) = config(); + let (mut store, mem_creator) = config(); let module = Module::new( store.engine(), r#" @@ -174,18 +172,24 @@ mod not_for_windows { "#, )?; - let instance1 = Instance::new(&store, &module, &[])?; - let instance2 = Instance::new(&store, &module, &[])?; + Instance::new(&mut store, &module, &[])?; + let instance2 = Instance::new(&mut store, &module, &[])?; assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2); - assert_eq!(instance2.get_memory("memory").unwrap().size(), 2); + assert_eq!( + instance2 + .get_memory(&mut store, "memory") + .unwrap() + .size(&store), + 2 + ); // we take the lock outside the assert, so it won't get poisoned on assert failure let tot_pages = *mem_creator.num_total_pages.lock().unwrap(); assert_eq!(tot_pages, 4); - drop((instance1, instance2, store, module)); + drop(store); let tot_pages = *mem_creator.num_total_pages.lock().unwrap(); assert_eq!(tot_pages, 0); diff --git a/tests/all/module.rs b/tests/all/module.rs index f6b3b5b0d051..e8c23279292c 100644 --- a/tests/all/module.rs +++ b/tests/all/module.rs @@ -28,7 +28,7 @@ fn caches_across_engines() { .unwrap(); unsafe { - let res = Module::deserialize(&Engine::new(&Config::new()).unwrap(), &bytes); + let res = Module::deserialize(&Engine::default(), &bytes); assert!(res.is_ok()); // differ in shared cranelift flags @@ -70,11 +70,11 @@ fn aot_compiles() -> Result<()> { let module = unsafe { Module::deserialize(&engine, &bytes)? }; - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; - let f = instance.get_typed_func::("f")?; - assert_eq!(f.call(101).unwrap(), 101); + let f = instance.get_typed_func::(&mut store, "f")?; + assert_eq!(f.call(&mut store, 101)?, 101); Ok(()) } diff --git a/tests/all/module_linking.rs b/tests/all/module_linking.rs index 28fb802fae47..498ed62b0409 100644 --- a/tests/all/module_linking.rs +++ b/tests/all/module_linking.rs @@ -217,8 +217,9 @@ fn limit_instances() -> Result<()> { ) "#, )?; - let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().instances(10).build()); - let err = Instance::new(&store, &module, &[]).err().unwrap(); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().instances(10).build()); + let err = Instance::new(&mut store, &module, &[]).err().unwrap(); assert!( err.to_string().contains("resource limit exceeded"), "bad error: {}", @@ -252,8 +253,9 @@ fn limit_memories() -> Result<()> { ) "#, )?; - let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memories(10).build()); - let err = Instance::new(&store, &module, &[]).err().unwrap(); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().memories(10).build()); + let err = Instance::new(&mut store, &module, &[]).err().unwrap(); assert!( err.to_string().contains("resource limit exceeded"), "bad error: {}", @@ -286,8 +288,9 @@ fn limit_tables() -> Result<()> { ) "#, )?; - let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().tables(10).build()); - let err = Instance::new(&store, &module, &[]).err().unwrap(); + let mut store = Store::new(&engine, ()); + store.limiter(StoreLimitsBuilder::new().tables(10).build()); + let err = Instance::new(&mut store, &module, &[]).err().unwrap(); assert!( err.to_string().contains("resource limit exceeded"), "bad error: {}", diff --git a/tests/all/module_serialize.rs b/tests/all/module_serialize.rs index 7f68b48a58ca..aa1a6093ba2f 100644 --- a/tests/all/module_serialize.rs +++ b/tests/all/module_serialize.rs @@ -6,9 +6,9 @@ fn serialize(engine: &Engine, wat: &'static str) -> Result> { Ok(module.serialize()?) } -unsafe fn deserialize_and_instantiate(store: &Store, buffer: &[u8]) -> Result { +unsafe fn deserialize_and_instantiate(store: &mut Store<()>, buffer: &[u8]) -> Result { let module = Module::deserialize(store.engine(), buffer)?; - Ok(Instance::new(&store, &module, &[])?) + Ok(Instance::new(store, &module, &[])?) } #[test] @@ -34,10 +34,10 @@ fn test_module_serialize_simple() -> Result<()> { "(module (func (export \"run\") (result i32) i32.const 42))", )?; - let store = Store::default(); - let instance = unsafe { deserialize_and_instantiate(&store, &buffer)? }; - let run = instance.get_typed_func::<(), i32>("run")?; - let result = run.call(())?; + let mut store = Store::default(); + let instance = unsafe { deserialize_and_instantiate(&mut store, &buffer)? }; + let run = instance.get_typed_func::<(), i32, _>(&mut store, "run")?; + let result = run.call(&mut store, ())?; assert_eq!(42, result); Ok(()) @@ -52,8 +52,8 @@ fn test_module_serialize_fail() -> Result<()> { let mut config = Config::new(); config.cranelift_opt_level(OptLevel::None); - let store = Store::new(&Engine::new(&config)?); - match unsafe { deserialize_and_instantiate(&store, &buffer) } { + let mut store = Store::new(&Engine::new(&config)?, ()); + match unsafe { deserialize_and_instantiate(&mut store, &buffer) } { Ok(_) => bail!("expected failure at deserialization"), Err(_) => (), } diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs index 4f0e3307a46a..a46f0e240230 100644 --- a/tests/all/pooling_allocator.rs +++ b/tests/all/pooling_allocator.rs @@ -21,8 +21,8 @@ fn successful_instantiation() -> Result<()> { let module = Module::new(&engine, r#"(module (memory 1) (table 10 funcref))"#)?; // Module should instantiate - let store = Store::new(&engine); - Instance::new(&store, &module, &[])?; + let mut store = Store::new(&engine, ()); + Instance::new(&mut store, &module, &[])?; Ok(()) } @@ -61,30 +61,36 @@ fn memory_limit() -> Result<()> { // Instantiate the module and grow the memory via the `f` function { - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let f = instance.get_typed_func::<(), i32>("f")?; - - assert_eq!(f.call(()).expect("function should not trap"), 0); - assert_eq!(f.call(()).expect("function should not trap"), 1); - assert_eq!(f.call(()).expect("function should not trap"), 2); - assert_eq!(f.call(()).expect("function should not trap"), -1); - assert_eq!(f.call(()).expect("function should not trap"), -1); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?; + + assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 0); + assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 1); + assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 2); + assert_eq!( + f.call(&mut store, ()).expect("function should not trap"), + -1 + ); + assert_eq!( + f.call(&mut store, ()).expect("function should not trap"), + -1 + ); } // Instantiate the module and grow the memory via the Wasmtime API - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - - let memory = instance.get_memory("m").unwrap(); - assert_eq!(memory.size(), 0); - assert_eq!(memory.grow(1).expect("memory should grow"), 0); - assert_eq!(memory.size(), 1); - assert_eq!(memory.grow(1).expect("memory should grow"), 1); - assert_eq!(memory.size(), 2); - assert_eq!(memory.grow(1).expect("memory should grow"), 2); - assert_eq!(memory.size(), 3); - assert!(memory.grow(1).is_err()); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + + let memory = instance.get_memory(&mut store, "m").unwrap(); + assert_eq!(memory.size(&store), 0); + assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 0); + assert_eq!(memory.size(&store), 1); + assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 1); + assert_eq!(memory.size(&store), 2); + assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 2); + assert_eq!(memory.size(&store), 3); + assert!(memory.grow(&mut store, 1).is_err()); Ok(()) } @@ -112,17 +118,15 @@ fn memory_init() -> Result<()> { r#"(module (memory (export "m") 2) (data (i32.const 65530) "this data spans multiple pages") (data (i32.const 10) "hello world"))"#, )?; - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let memory = instance.get_memory("m").unwrap(); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let memory = instance.get_memory(&mut store, "m").unwrap(); - unsafe { - assert_eq!( - &memory.data_unchecked()[65530..65560], - b"this data spans multiple pages" - ); - assert_eq!(&memory.data_unchecked()[10..21], b"hello world"); - } + assert_eq!( + &memory.data(&store)[65530..65560], + b"this data spans multiple pages" + ); + assert_eq!(&memory.data(&store)[10..21], b"hello world"); Ok(()) } @@ -152,30 +156,31 @@ fn memory_guard_page_trap() -> Result<()> { // Instantiate the module and check for out of bounds trap for _ in 0..10 { - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let m = instance.get_memory("m").unwrap(); - let f = instance.get_typed_func::("f")?; + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let m = instance.get_memory(&mut store, "m").unwrap(); + let f = instance.get_typed_func::(&mut store, "f")?; - let trap = f.call(0).expect_err("function should trap"); + let trap = f.call(&mut store, 0).expect_err("function should trap"); assert!(trap.to_string().contains("out of bounds")); - let trap = f.call(1).expect_err("function should trap"); + let trap = f.call(&mut store, 1).expect_err("function should trap"); assert!(trap.to_string().contains("out of bounds")); - m.grow(1).expect("memory should grow"); - f.call(0).expect("function should not trap"); + m.grow(&mut store, 1).expect("memory should grow"); + f.call(&mut store, 0).expect("function should not trap"); - let trap = f.call(65536).expect_err("function should trap"); + let trap = f.call(&mut store, 65536).expect_err("function should trap"); assert!(trap.to_string().contains("out of bounds")); - let trap = f.call(65537).expect_err("function should trap"); + let trap = f.call(&mut store, 65537).expect_err("function should trap"); assert!(trap.to_string().contains("out of bounds")); - m.grow(1).expect("memory should grow"); - f.call(65536).expect("function should not trap"); + m.grow(&mut store, 1).expect("memory should grow"); + f.call(&mut store, 65536).expect("function should not trap"); - m.grow(1).expect_err("memory should be at the limit"); + m.grow(&mut store, 1) + .expect_err("memory should be at the limit"); } Ok(()) @@ -204,20 +209,20 @@ fn memory_zeroed() -> Result<()> { // Instantiate the module repeatedly after writing data to the entire memory for _ in 0..10 { - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let memory = instance.get_memory("m").unwrap(); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let memory = instance.get_memory(&mut store, "m").unwrap(); - assert_eq!(memory.size(), 1); - assert_eq!(memory.data_size(), 65536); + assert_eq!(memory.size(&store,), 1); + assert_eq!(memory.data_size(&store), 65536); - let ptr = memory.data_ptr(); + let ptr = memory.data_mut(&mut store).as_mut_ptr(); unsafe { for i in 0..8192 { assert_eq!(*ptr.cast::().offset(i), 0); } - std::ptr::write_bytes(ptr, 0xFE, memory.data_size()); + std::ptr::write_bytes(ptr, 0xFE, memory.data_size(&store)); } } @@ -259,36 +264,45 @@ fn table_limit() -> Result<()> { // Instantiate the module and grow the table via the `f` function { - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let f = instance.get_typed_func::<(), i32>("f")?; + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?; for i in 0..TABLE_ELEMENTS { - assert_eq!(f.call(()).expect("function should not trap"), i as i32); + assert_eq!( + f.call(&mut store, ()).expect("function should not trap"), + i as i32 + ); } - assert_eq!(f.call(()).expect("function should not trap"), -1); - assert_eq!(f.call(()).expect("function should not trap"), -1); + assert_eq!( + f.call(&mut store, ()).expect("function should not trap"), + -1 + ); + assert_eq!( + f.call(&mut store, ()).expect("function should not trap"), + -1 + ); } // Instantiate the module and grow the table via the Wasmtime API - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; - let table = instance.get_table("t").unwrap(); + let table = instance.get_table(&mut store, "t").unwrap(); for i in 0..TABLE_ELEMENTS { - assert_eq!(table.size(), i); + assert_eq!(table.size(&store), i); assert_eq!( table - .grow(1, Val::FuncRef(None)) + .grow(&mut store, 1, Val::FuncRef(None)) .expect("table should grow"), i ); } - assert_eq!(table.size(), TABLE_ELEMENTS); - assert!(table.grow(1, Val::FuncRef(None)).is_err()); + assert_eq!(table.size(&store), TABLE_ELEMENTS); + assert!(table.grow(&mut store, 1, Val::FuncRef(None)).is_err()); Ok(()) } @@ -316,22 +330,22 @@ fn table_init() -> Result<()> { r#"(module (table (export "t") 6 funcref) (elem (i32.const 1) 1 2 3 4) (elem (i32.const 0) 0) (func) (func (param i32)) (func (param i32 i32)) (func (param i32 i32 i32)) (func (param i32 i32 i32 i32)))"#, )?; - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let table = instance.get_table("t").unwrap(); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let table = instance.get_table(&mut store, "t").unwrap(); for i in 0..5 { - let v = table.get(i).expect("table should have entry"); + let v = table.get(&mut store, i).expect("table should have entry"); let f = v .funcref() .expect("expected funcref") .expect("expected non-null value"); - assert_eq!(f.ty().params().len(), i as usize); + assert_eq!(f.ty(&store).params().len(), i as usize); } assert!( table - .get(5) + .get(&mut store, 5) .expect("table should have entry") .funcref() .expect("expected funcref") @@ -365,19 +379,21 @@ fn table_zeroed() -> Result<()> { // Instantiate the module repeatedly after filling table elements for _ in 0..10 { - let store = Store::new(&engine); - let instance = Instance::new(&store, &module, &[])?; - let table = instance.get_table("t").unwrap(); - let f = Func::wrap(&store, || {}); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let table = instance.get_table(&mut store, "t").unwrap(); + let f = Func::wrap(&mut store, || {}); - assert_eq!(table.size(), 10); + assert_eq!(table.size(&store), 10); for i in 0..10 { - match table.get(i).unwrap() { + match table.get(&mut store, i).unwrap() { Val::FuncRef(r) => assert!(r.is_none()), _ => panic!("expected a funcref"), } - table.set(i, Val::FuncRef(Some(f.clone()))).unwrap(); + table + .set(&mut store, i, Val::FuncRef(Some(f.clone()))) + .unwrap(); } } @@ -406,13 +422,13 @@ fn instantiation_limit() -> Result<()> { // Instantiate to the limit { - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); for _ in 0..INSTANCE_LIMIT { - Instance::new(&store, &module, &[])?; + Instance::new(&mut store, &module, &[])?; } - match Instance::new(&store, &module, &[]) { + match Instance::new(&mut store, &module, &[]) { Ok(_) => panic!("instantiation should fail"), Err(e) => assert_eq!( e.to_string(), @@ -426,10 +442,10 @@ fn instantiation_limit() -> Result<()> { // With the above store dropped, ensure instantiations can be made - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); for _ in 0..INSTANCE_LIMIT { - Instance::new(&store, &module, &[])?; + Instance::new(&mut store, &module, &[])?; } Ok(()) diff --git a/tests/all/stack_overflow.rs b/tests/all/stack_overflow.rs index 62c37707cb5f..01dcd0170560 100644 --- a/tests/all/stack_overflow.rs +++ b/tests/all/stack_overflow.rs @@ -7,7 +7,7 @@ fn host_always_has_some_stack() -> anyhow::Result<()> { // assume hosts always have at least 512k of stack const HOST_STACK: usize = 512 * 1024; - let store = Store::default(); + let mut store = Store::<()>::default(); // Create a module that's infinitely recursive, but calls the host on each // level of wasm stack to always test how much host stack we have left. @@ -22,13 +22,13 @@ fn host_always_has_some_stack() -> anyhow::Result<()> { ) "#, )?; - let func = Func::wrap(&store, test_host_stack); - let instance = Instance::new(&store, &module, &[func.into()])?; - let foo = instance.get_typed_func::<(), ()>("foo")?; + let func = Func::wrap(&mut store, test_host_stack); + let instance = Instance::new(&mut store, &module, &[func.into()])?; + let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?; // Make sure that our function traps and the trap says that the call stack // has been exhausted. - let trap = foo.call(()).unwrap_err(); + let trap = foo.call(&mut store, ()).unwrap_err(); assert!( trap.to_string().contains("call stack exhausted"), "{}", diff --git a/tests/all/table.rs b/tests/all/table.rs index abdddccc4acc..9706d91b03ad 100644 --- a/tests/all/table.rs +++ b/tests/all/table.rs @@ -2,49 +2,49 @@ use wasmtime::*; #[test] fn get_none() { - let store = Store::default(); + let mut store = Store::<()>::default(); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); - let table = Table::new(&store, ty, Val::FuncRef(None)).unwrap(); - match table.get(0) { + let table = Table::new(&mut store, ty, Val::FuncRef(None)).unwrap(); + match table.get(&mut store, 0) { Some(Val::FuncRef(None)) => {} _ => panic!(), } - assert!(table.get(1).is_none()); + assert!(table.get(&mut store, 1).is_none()); } #[test] fn fill_wrong() { - let store = Store::default(); + let mut store = Store::<()>::default(); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); - let table = Table::new(&store, ty, Val::FuncRef(None)).unwrap(); + let table = Table::new(&mut store, ty, Val::FuncRef(None)).unwrap(); assert_eq!( table - .fill(0, Val::ExternRef(None), 1) + .fill(&mut store, 0, Val::ExternRef(None), 1) .map_err(|e| e.to_string()) .unwrap_err(), - "mismatched element fill type" + "value does not match table element type" ); let ty = TableType::new(ValType::ExternRef, Limits::new(1, None)); - let table = Table::new(&store, ty, Val::ExternRef(None)).unwrap(); + let table = Table::new(&mut store, ty, Val::ExternRef(None)).unwrap(); assert_eq!( table - .fill(0, Val::FuncRef(None), 1) + .fill(&mut store, 0, Val::FuncRef(None), 1) .map_err(|e| e.to_string()) .unwrap_err(), - "mismatched element fill type" + "value does not match table element type" ); } #[test] fn copy_wrong() { - let store = Store::default(); + let mut store = Store::<()>::default(); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); - let table1 = Table::new(&store, ty, Val::FuncRef(None)).unwrap(); + let table1 = Table::new(&mut store, ty, Val::FuncRef(None)).unwrap(); let ty = TableType::new(ValType::ExternRef, Limits::new(1, None)); - let table2 = Table::new(&store, ty, Val::ExternRef(None)).unwrap(); + let table2 = Table::new(&mut store, ty, Val::ExternRef(None)).unwrap(); assert_eq!( - Table::copy(&table1, 0, &table2, 0, 1) + Table::copy(&mut store, &table1, 0, &table2, 0, 1) .map_err(|e| e.to_string()) .unwrap_err(), "tables do not have the same element type" diff --git a/tests/all/traps.rs b/tests/all/traps.rs index af702c247b76..0f4f1c7abdb0 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -5,7 +5,7 @@ use wasmtime::*; #[test] fn test_trap_return() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module (func $hello (import "" "hello")) @@ -15,12 +15,15 @@ fn test_trap_return() -> Result<()> { let module = Module::new(store.engine(), wat)?; let hello_type = FuncType::new(None, None); - let hello_func = Func::new(&store, hello_type, |_, _, _| Err(Trap::new("test 123"))); + let hello_func = Func::new(&mut store, hello_type, |_, _, _| Err(Trap::new("test 123"))); - let instance = Instance::new(&store, &module, &[hello_func.into()])?; - let run_func = instance.get_typed_func::<(), ()>("run")?; + let instance = Instance::new(&mut store, &module, &[hello_func.into()])?; + let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?; - let e = run_func.call(()).err().expect("error calling function"); + let e = run_func + .call(&mut store, ()) + .err() + .expect("error calling function"); assert!(e.to_string().contains("test 123")); Ok(()) @@ -29,7 +32,7 @@ fn test_trap_return() -> Result<()> { #[test] #[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64 fn test_trap_trace() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module $hello_mod (func (export "run") (call $hello)) @@ -38,10 +41,13 @@ fn test_trap_trace() -> Result<()> { "#; let module = Module::new(store.engine(), wat)?; - let instance = Instance::new(&store, &module, &[])?; - let run_func = instance.get_typed_func::<(), ()>("run")?; + let instance = Instance::new(&mut store, &module, &[])?; + let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?; - let e = run_func.call(()).err().expect("error calling function"); + let e = run_func + .call(&mut store, ()) + .err() + .expect("error calling function"); let trace = e.trace(); assert_eq!(trace.len(), 2); @@ -67,7 +73,7 @@ fn test_trap_trace() -> Result<()> { #[test] #[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64 fn test_trap_trace_cb() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module $hello_mod (import "" "throw" (func $throw)) @@ -77,13 +83,16 @@ fn test_trap_trace_cb() -> Result<()> { "#; let fn_type = FuncType::new(None, None); - let fn_func = Func::new(&store, fn_type, |_, _, _| Err(Trap::new("cb throw"))); + let fn_func = Func::new(&mut store, fn_type, |_, _, _| Err(Trap::new("cb throw"))); let module = Module::new(store.engine(), wat)?; - let instance = Instance::new(&store, &module, &[fn_func.into()])?; - let run_func = instance.get_typed_func::<(), ()>("run")?; + let instance = Instance::new(&mut store, &module, &[fn_func.into()])?; + let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?; - let e = run_func.call(()).err().expect("error calling function"); + let e = run_func + .call(&mut store, ()) + .err() + .expect("error calling function"); let trace = e.trace(); assert_eq!(trace.len(), 2); @@ -99,7 +108,7 @@ fn test_trap_trace_cb() -> Result<()> { #[test] #[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64 fn test_trap_stack_overflow() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module $rec_mod (func $run (export "run") (call $run)) @@ -107,10 +116,13 @@ fn test_trap_stack_overflow() -> Result<()> { "#; let module = Module::new(store.engine(), wat)?; - let instance = Instance::new(&store, &module, &[])?; - let run_func = instance.get_typed_func::<(), ()>("run")?; + let instance = Instance::new(&mut store, &module, &[])?; + let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?; - let e = run_func.call(()).err().expect("error calling function"); + let e = run_func + .call(&mut store, ()) + .err() + .expect("error calling function"); let trace = e.trace(); assert!(trace.len() >= 32); @@ -127,7 +139,7 @@ fn test_trap_stack_overflow() -> Result<()> { #[test] #[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64 fn trap_display_pretty() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module $m (func $die unreachable) @@ -138,10 +150,13 @@ fn trap_display_pretty() -> Result<()> { "#; let module = Module::new(store.engine(), wat)?; - let instance = Instance::new(&store, &module, &[])?; - let run_func = instance.get_typed_func::<(), ()>("bar")?; + let instance = Instance::new(&mut store, &module, &[])?; + let run_func = instance.get_typed_func::<(), (), _>(&mut store, "bar")?; - let e = run_func.call(()).err().expect("error calling function"); + let e = run_func + .call(&mut store, ()) + .err() + .expect("error calling function"); assert_eq!( e.to_string(), "\ @@ -159,7 +174,7 @@ wasm backtrace: #[test] #[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64 fn trap_display_multi_module() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module $a (func $die unreachable) @@ -170,8 +185,8 @@ fn trap_display_multi_module() -> Result<()> { "#; let module = Module::new(store.engine(), wat)?; - let instance = Instance::new(&store, &module, &[])?; - let bar = instance.get_export("bar").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let bar = instance.get_export(&mut store, "bar").unwrap(); let wat = r#" (module $b @@ -181,10 +196,13 @@ fn trap_display_multi_module() -> Result<()> { ) "#; let module = Module::new(store.engine(), wat)?; - let instance = Instance::new(&store, &module, &[bar])?; - let bar2 = instance.get_typed_func::<(), ()>("bar2")?; + let instance = Instance::new(&mut store, &module, &[bar])?; + let bar2 = instance.get_typed_func::<(), (), _>(&mut store, "bar2")?; - let e = bar2.call(()).err().expect("error calling function"); + let e = bar2 + .call(&mut store, ()) + .err() + .expect("error calling function"); assert_eq!( e.to_string(), "\ @@ -203,7 +221,7 @@ wasm backtrace: #[test] fn trap_start_function_import() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let binary = wat::parse_str( r#" (module $a @@ -215,8 +233,8 @@ fn trap_start_function_import() -> Result<()> { let module = Module::new(store.engine(), &binary)?; let sig = FuncType::new(None, None); - let func = Func::new(&store, sig, |_, _, _| Err(Trap::new("user trap"))); - let err = Instance::new(&store, &module, &[func.into()]) + let func = Func::new(&mut store, sig, |_, _, _| Err(Trap::new("user trap"))); + let err = Instance::new(&mut store, &module, &[func.into()]) .err() .unwrap(); assert!(err @@ -229,7 +247,7 @@ fn trap_start_function_import() -> Result<()> { #[test] fn rust_panic_import() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let binary = wat::parse_str( r#" (module $a @@ -243,22 +261,17 @@ fn rust_panic_import() -> Result<()> { let module = Module::new(store.engine(), &binary)?; let sig = FuncType::new(None, None); - let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic")); - let instance = Instance::new( - &store, - &module, - &[ - func.into(), - Func::wrap(&store, || panic!("this is another panic")).into(), - ], - )?; - let func = instance.get_typed_func::<(), ()>("foo")?; - let err = panic::catch_unwind(AssertUnwindSafe(|| drop(func.call(())))).unwrap_err(); + let func = Func::new(&mut store, sig, |_, _, _| panic!("this is a panic")); + let func2 = Func::wrap(&mut store, || panic!("this is another panic")); + let instance = Instance::new(&mut store, &module, &[func.into(), func2.into()])?; + let func = instance.get_typed_func::<(), (), _>(&mut store, "foo")?; + let err = + panic::catch_unwind(AssertUnwindSafe(|| drop(func.call(&mut store, ())))).unwrap_err(); assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic")); - let func = instance.get_typed_func::<(), ()>("bar")?; + let func = instance.get_typed_func::<(), (), _>(&mut store, "bar")?; let err = panic::catch_unwind(AssertUnwindSafe(|| { - drop(func.call(())); + drop(func.call(&mut store, ())); })) .unwrap_err(); assert_eq!( @@ -270,7 +283,7 @@ fn rust_panic_import() -> Result<()> { #[test] fn rust_panic_start_function() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let binary = wat::parse_str( r#" (module $a @@ -282,16 +295,16 @@ fn rust_panic_start_function() -> Result<()> { let module = Module::new(store.engine(), &binary)?; let sig = FuncType::new(None, None); - let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic")); + let func = Func::new(&mut store, sig, |_, _, _| panic!("this is a panic")); let err = panic::catch_unwind(AssertUnwindSafe(|| { - drop(Instance::new(&store, &module, &[func.into()])); + drop(Instance::new(&mut store, &module, &[func.into()])); })) .unwrap_err(); assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic")); - let func = Func::wrap(&store, || panic!("this is another panic")); + let func = Func::wrap(&mut store, || panic!("this is another panic")); let err = panic::catch_unwind(AssertUnwindSafe(|| { - drop(Instance::new(&store, &module, &[func.into()])); + drop(Instance::new(&mut store, &module, &[func.into()])); })) .unwrap_err(); assert_eq!( @@ -303,7 +316,7 @@ fn rust_panic_start_function() -> Result<()> { #[test] fn mismatched_arguments() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let binary = wat::parse_str( r#" (module $a @@ -313,18 +326,20 @@ fn mismatched_arguments() -> Result<()> { )?; let module = Module::new(store.engine(), &binary)?; - let instance = Instance::new(&store, &module, &[])?; - let func = instance.get_func("foo").unwrap(); + let instance = Instance::new(&mut store, &module, &[])?; + let func = instance.get_func(&mut store, "foo").unwrap(); assert_eq!( - func.call(&[]).unwrap_err().to_string(), + func.call(&mut store, &[]).unwrap_err().to_string(), "expected 1 arguments, got 0" ); assert_eq!( - func.call(&[Val::F32(0)]).unwrap_err().to_string(), + func.call(&mut store, &[Val::F32(0)]) + .unwrap_err() + .to_string(), "argument type mismatch: found f32 but expected i32", ); assert_eq!( - func.call(&[Val::I32(0), Val::I32(1)]) + func.call(&mut store, &[Val::I32(0), Val::I32(1)]) .unwrap_err() .to_string(), "expected 1 arguments, got 2" @@ -334,7 +349,7 @@ fn mismatched_arguments() -> Result<()> { #[test] fn call_signature_mismatch() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let binary = wat::parse_str( r#" (module $a @@ -351,7 +366,7 @@ fn call_signature_mismatch() -> Result<()> { )?; let module = Module::new(store.engine(), &binary)?; - let err = Instance::new(&store, &module, &[]) + let err = Instance::new(&mut store, &module, &[]) .err() .unwrap() .downcast::() @@ -365,7 +380,7 @@ fn call_signature_mismatch() -> Result<()> { #[test] #[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64 fn start_trap_pretty() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let wat = r#" (module $m (func $die unreachable) @@ -377,7 +392,7 @@ fn start_trap_pretty() -> Result<()> { "#; let module = Module::new(store.engine(), wat)?; - let e = match Instance::new(&store, &module, &[]) { + let e = match Instance::new(&mut store, &module, &[]) { Ok(_) => panic!("expected failure"), Err(e) => e.downcast::()?, }; @@ -399,17 +414,17 @@ wasm backtrace: #[test] #[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64 fn present_after_module_drop() -> Result<()> { - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new(store.engine(), r#"(func (export "foo") unreachable)"#)?; - let instance = Instance::new(&store, &module, &[])?; - let func = instance.get_typed_func::<(), ()>("foo")?; + let instance = Instance::new(&mut store, &module, &[])?; + let func = instance.get_typed_func::<(), (), _>(&mut store, "foo")?; println!("asserting before we drop modules"); - assert_trap(func.call(()).unwrap_err()); + assert_trap(func.call(&mut store, ()).unwrap_err()); drop((instance, module)); println!("asserting after drop"); - assert_trap(func.call(()).unwrap_err()); + assert_trap(func.call(&mut store, ()).unwrap_err()); return Ok(()); fn assert_trap(t: Trap) { @@ -420,10 +435,10 @@ fn present_after_module_drop() -> Result<()> { } fn assert_trap_code(wat: &str, code: wasmtime::TrapCode) { - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new(store.engine(), wat).unwrap(); - let err = match Instance::new(&store, &module, &[]) { + let err = match Instance::new(&mut store, &module, &[]) { Ok(_) => unreachable!(), Err(e) => e, }; @@ -493,19 +508,18 @@ fn parse_dwarf_info() -> Result<()> { let mut config = Config::new(); config.wasm_backtrace_details(WasmBacktraceDetails::Enable); let engine = Engine::new(&config)?; - let store = Store::new(&engine); let module = Module::new(&engine, &wasm)?; - let mut linker = Linker::new(&store); - wasmtime_wasi::Wasi::new( - &store, + let mut linker = Linker::new(&engine); + wasmtime_wasi::add_to_linker(&mut linker)?; + let mut store = Store::new( + &engine, wasmtime_wasi::sync::WasiCtxBuilder::new() .inherit_stdio() .build()?, - ) - .add_to_linker(&mut linker)?; - linker.module("", &module)?; - let run = linker.get_default("")?; - let trap = run.call(&[]).unwrap_err().downcast::()?; + ); + linker.module(&mut store, "", &module)?; + let run = linker.get_default(&mut store, "")?; + let trap = run.call(&mut store, &[]).unwrap_err().downcast::()?; let mut found = false; for frame in trap.trace() { @@ -529,7 +543,7 @@ fn no_hint_even_with_dwarf_info() -> Result<()> { let mut config = Config::new(); config.wasm_backtrace_details(WasmBacktraceDetails::Disable); let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let module = Module::new( &engine, r#" @@ -541,7 +555,7 @@ fn no_hint_even_with_dwarf_info() -> Result<()> { ) "#, )?; - let trap = Instance::new(&store, &module, &[]) + let trap = Instance::new(&mut store, &module, &[]) .err() .unwrap() .downcast::()?; @@ -564,7 +578,7 @@ fn hint_with_dwarf_info() -> Result<()> { if std::env::var("WASMTIME_BACKTRACE_DETAILS").is_ok() { return Ok(()); } - let store = Store::default(); + let mut store = Store::<()>::default(); let module = Module::new( store.engine(), r#" @@ -576,7 +590,7 @@ fn hint_with_dwarf_info() -> Result<()> { ) "#, )?; - let trap = Instance::new(&store, &module, &[]) + let trap = Instance::new(&mut store, &module, &[]) .err() .unwrap() .downcast::()?; @@ -596,30 +610,23 @@ note: run with `WASMTIME_BACKTRACE_DETAILS=1` environment variable to display mo fn multithreaded_traps() -> Result<()> { // Compile and run unreachable on a thread, then moves over the whole store to another thread, // and make sure traps are still correctly caught after notifying the store of the move. - let instance = { - let store = Store::default(); - let module = Module::new( - store.engine(), - r#"(module (func (export "run") unreachable))"#, - )?; - Instance::new(&store, &module, &[])? - }; - - assert!(instance.get_typed_func::<(), ()>("run")?.call(()).is_err()); - - struct SendInstance { - inner: Instance, - } - unsafe impl Send for SendInstance {} + let mut store = Store::<()>::default(); + let module = Module::new( + store.engine(), + r#"(module (func (export "run") unreachable))"#, + )?; + let instance = Instance::new(&mut store, &module, &[])?; - let instance = SendInstance { inner: instance }; + assert!(instance + .get_typed_func::<(), (), _>(&mut store, "run")? + .call(&mut store, ()) + .is_err()); let handle = std::thread::spawn(move || { - let instance = instance.inner; assert!(instance - .get_typed_func::<(), ()>("run") + .get_typed_func::<(), (), _>(&mut store, "run") .unwrap() - .call(()) + .call(&mut store, ()) .is_err()); }); diff --git a/tests/all/use_after_drop.rs b/tests/all/use_after_drop.rs deleted file mode 100644 index 31d472895a07..000000000000 --- a/tests/all/use_after_drop.rs +++ /dev/null @@ -1,21 +0,0 @@ -use anyhow::Result; -use wasmtime::*; - -#[test] -fn use_func_after_drop() -> Result<()> { - let table; - { - let store = Store::default(); - let closed_over_data = String::from("abcd"); - let func = Func::wrap(&store, move || { - assert_eq!(closed_over_data, "abcd"); - }); - let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); - table = Table::new(&store, ty, Val::FuncRef(None))?; - table.set(0, func.into())?; - } - let func = table.get(0).unwrap().funcref().unwrap().unwrap().clone(); - let func = func.typed::<(), ()>()?; - func.call(())?; - Ok(()) -} diff --git a/tests/all/wast.rs b/tests/all/wast.rs index 797e8a13fffc..2fa646614483 100644 --- a/tests/all/wast.rs +++ b/tests/all/wast.rs @@ -71,7 +71,7 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()> }); } - let store = Store::new(&Engine::new(&cfg)?); + let store = Store::new(&Engine::new(&cfg)?, ()); let mut wast_context = WastContext::new(store); wast_context.register_spectest()?; wast_context.run_file(wast)?; diff --git a/tests/host_segfault.rs b/tests/host_segfault.rs index e725ff4303f1..57edb07bac9b 100644 --- a/tests/host_segfault.rs +++ b/tests/host_segfault.rs @@ -97,9 +97,9 @@ fn main() { "make instance then segfault", || { let engine = Engine::default(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let module = Module::new(&engine, "(module)").unwrap(); - let _instance = Instance::new(&store, &module, &[]).unwrap(); + let _instance = Instance::new(&mut store, &module, &[]).unwrap(); segfault(); }, false, @@ -108,9 +108,9 @@ fn main() { "make instance then overrun the stack", || { let engine = Engine::default(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let module = Module::new(&engine, "(module)").unwrap(); - let _instance = Instance::new(&store, &module, &[]).unwrap(); + let _instance = Instance::new(&mut store, &module, &[]).unwrap(); overrun_the_stack(); }, true, @@ -119,10 +119,10 @@ fn main() { "segfault in a host function", || { let engine = Engine::default(); - let store = Store::new(&engine); + let mut store = Store::new(&engine, ()); let module = Module::new(&engine, r#"(import "" "" (func)) (start 0)"#).unwrap(); - let segfault = Func::wrap(&store, || segfault()); - Instance::new(&store, &module, &[segfault.into()]).unwrap(); + let segfault = Func::wrap(&mut store, || segfault()); + Instance::new(&mut store, &module, &[segfault.into()]).unwrap(); unreachable!(); }, false, @@ -133,13 +133,13 @@ fn main() { let mut config = Config::default(); config.async_support(true); let engine = Engine::new(&config).unwrap(); - let store = Store::new(&engine); - let f = Func::wrap0_async(&store, (), |_, _| { + let mut store = Store::new(&engine, ()); + let f = Func::wrap0_async(&mut store, |_| { Box::new(async { overrun_the_stack(); }) }); - run_future(f.call_async(&[])).unwrap(); + run_future(f.call_async(&mut store, &[])).unwrap(); unreachable!(); }, true, @@ -151,13 +151,13 @@ fn main() { config.async_support(true); config.allocation_strategy(InstanceAllocationStrategy::pooling()); let engine = Engine::new(&config).unwrap(); - let store = Store::new(&engine); - let f = Func::wrap0_async(&store, (), |_, _| { + let mut store = Store::new(&engine, ()); + let f = Func::wrap0_async(&mut store, |_| { Box::new(async { overrun_the_stack(); }) }); - run_future(f.call_async(&[])).unwrap(); + run_future(f.call_async(&mut store, &[])).unwrap(); unreachable!(); }, true, From 80b781baf3d04368b8d54491bdf8376166e2d884 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 04:30:03 -0700 Subject: [PATCH 02/90] Fix another example --- crates/wiggle/test-helpers/examples/tracing.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/wiggle/test-helpers/examples/tracing.rs b/crates/wiggle/test-helpers/examples/tracing.rs index 9930f705c039..e9e05e373d33 100644 --- a/crates/wiggle/test-helpers/examples/tracing.rs +++ b/crates/wiggle/test-helpers/examples/tracing.rs @@ -32,7 +32,7 @@ impl_errno!(types::Errno); /// When the `errors` mapping in witx is non-empty, we need to impl the /// types::UserErrorConversion trait that wiggle generates from that mapping. impl<'a> types::UserErrorConversion for WasiCtx<'a> { - fn errno_from_rich_error(&self, e: RichError) -> Result { + fn errno_from_rich_error(&mut self, e: RichError) -> Result { wiggle::tracing::debug!( rich_error = wiggle::tracing::field::debug(&e), "error conversion" @@ -49,7 +49,7 @@ impl<'a> types::UserErrorConversion for WasiCtx<'a> { } impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> { - fn foo(&self, strike: u32, _s: &types::S) -> Result { + fn foo(&mut self, strike: u32, _s: &types::S) -> Result { // We use the argument to this function to exercise all of the // possible error cases we could hit here match strike { @@ -78,12 +78,12 @@ fn main() { env_logger::init(); } - let ctx = WasiCtx::new(); + let mut ctx = WasiCtx::new(); let host_memory = HostMemory::new(); // Exercise each of the branches in `foo`. // Start with the success case: - let r0 = one_error_conversion::foo(&ctx, &host_memory, 0, 0, 8); + let r0 = one_error_conversion::foo(&mut ctx, &host_memory, 0, 0, 8); assert_eq!( r0, Ok(types::Errno::Ok as i32), @@ -92,7 +92,7 @@ fn main() { assert!(ctx.log.borrow().is_empty(), "No error log for strike=0"); // First error case: - let r1 = one_error_conversion::foo(&ctx, &host_memory, 1, 0, 8); + let r1 = one_error_conversion::foo(&mut ctx, &host_memory, 1, 0, 8); assert_eq!( r1, Ok(types::Errno::PicketLine as i32), @@ -105,7 +105,7 @@ fn main() { ); // Second error case: - let r2 = one_error_conversion::foo(&ctx, &host_memory, 2, 0, 8); + let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2, 0, 8); assert_eq!( r2, Ok(types::Errno::InvalidArg as i32), From d6f6ba44b7e64e8442807cde149cc7e699e909dd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 05:38:35 -0700 Subject: [PATCH 03/90] Get the C API compiling again --- crates/c-api/src/error.rs | 13 +- crates/c-api/src/extern.rs | 7 +- crates/c-api/src/func.rs | 300 ++++++++++++--------------- crates/c-api/src/global.rs | 89 ++++---- crates/c-api/src/instance.rs | 210 ++++++++++--------- crates/c-api/src/lib.rs | 29 +-- crates/c-api/src/memory.rs | 36 ++-- crates/c-api/src/module.rs | 154 +++++++------- crates/c-api/src/ref.rs | 3 + crates/c-api/src/store.rs | 104 ++++++---- crates/c-api/src/table.rs | 191 +++++++++-------- crates/wasmtime/src/externals.rs | 6 +- crates/wasmtime/src/func.rs | 3 +- crates/wasmtime/src/func/typed.rs | 9 +- crates/wasmtime/src/instance.rs | 3 +- crates/wasmtime/src/memory.rs | 3 +- crates/wasmtime/src/store/context.rs | 2 + crates/wasmtime/src/store/data.rs | 1 + 18 files changed, 590 insertions(+), 573 deletions(-) diff --git a/crates/c-api/src/error.rs b/crates/c-api/src/error.rs index cbb84873df6c..20a7b9bfadcc 100644 --- a/crates/c-api/src/error.rs +++ b/crates/c-api/src/error.rs @@ -1,6 +1,5 @@ -use crate::{wasm_name_t, wasm_trap_t}; +use crate::wasm_name_t; use anyhow::{anyhow, Error, Result}; -use wasmtime::Trap; #[repr(C)] pub struct wasmtime_error_t { @@ -9,11 +8,11 @@ pub struct wasmtime_error_t { wasmtime_c_api_macros::declare_own!(wasmtime_error_t); -impl wasmtime_error_t { - pub(crate) fn to_trap(self) -> Box { - Box::new(wasm_trap_t::new(Trap::from(self.error))) - } -} +// impl wasmtime_error_t { +// pub(crate) fn to_trap(self) -> Box { +// Box::new(wasm_trap_t::new(Trap::from(self.error))) +// } +// } impl From for wasmtime_error_t { fn from(error: Error) -> wasmtime_error_t { diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs index 414946a293bb..1bca0ca4ed12 100644 --- a/crates/c-api/src/extern.rs +++ b/crates/c-api/src/extern.rs @@ -1,11 +1,12 @@ use crate::{ wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_instance_t, - wasm_memory_t, wasm_module_t, wasm_table_t, + wasm_memory_t, wasm_module_t, wasm_table_t, StoreRef, }; use wasmtime::Extern; #[derive(Clone)] pub struct wasm_extern_t { + pub(crate) store: StoreRef, pub(crate) which: Extern, } @@ -24,8 +25,8 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t { } #[no_mangle] -pub extern "C" fn wasm_extern_type(e: &wasm_extern_t) -> Box { - Box::new(wasm_externtype_t::new(e.which.ty())) +pub unsafe extern "C" fn wasm_extern_type(e: &wasm_extern_t) -> Box { + Box::new(wasm_externtype_t::new(e.which.ty(&e.store.context()))) } #[no_mangle] diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 87351e99cab7..f726760d3175 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -1,12 +1,11 @@ +use crate::wasm_trap_t; use crate::{wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t}; -use crate::{wasm_name_t, wasm_trap_t, wasmtime_error_t}; use anyhow::anyhow; use std::ffi::c_void; -use std::mem::MaybeUninit; use std::panic::{self, AssertUnwindSafe}; use std::ptr; use std::str; -use wasmtime::{Caller, Extern, Func, Trap, Val}; +use wasmtime::{Extern, Func, Trap}; #[derive(Clone)] #[repr(transparent)] @@ -16,10 +15,10 @@ pub struct wasm_func_t { wasmtime_c_api_macros::declare_ref!(wasm_func_t); -#[repr(C)] -pub struct wasmtime_caller_t<'a> { - caller: Caller<'a>, -} +// #[repr(C)] +// pub struct wasmtime_caller_t<'a> { +// caller: Caller<'a>, +// } pub type wasm_func_callback_t = extern "C" fn( args: *const wasm_val_vec_t, @@ -32,24 +31,27 @@ pub type wasm_func_callback_with_env_t = extern "C" fn( results: *mut wasm_val_vec_t, ) -> Option>; -pub type wasmtime_func_callback_t = extern "C" fn( - caller: *const wasmtime_caller_t, - args: *const wasm_val_vec_t, - results: *mut wasm_val_vec_t, -) -> Option>; +// pub type wasmtime_func_callback_t = extern "C" fn( +// caller: *const wasmtime_caller_t, +// args: *const wasm_val_vec_t, +// results: *mut wasm_val_vec_t, +// ) -> Option>; -pub type wasmtime_func_callback_with_env_t = extern "C" fn( - caller: *const wasmtime_caller_t, - env: *mut std::ffi::c_void, - args: *const wasm_val_vec_t, - results: *mut wasm_val_vec_t, -) -> Option>; +// pub type wasmtime_func_callback_with_env_t = extern "C" fn( +// caller: *const wasmtime_caller_t, +// env: *mut std::ffi::c_void, +// args: *const wasm_val_vec_t, +// results: *mut wasm_val_vec_t, +// ) -> Option>; struct Finalizer { env: *mut c_void, finalizer: Option, } +unsafe impl Send for Finalizer {} +unsafe impl Sync for Finalizer {} + impl Drop for Finalizer { fn drop(&mut self) { if let Some(f) = self.finalizer { @@ -66,150 +68,120 @@ impl wasm_func_t { } } - pub(crate) fn func(&self) -> &Func { - match &self.ext.which { + pub(crate) fn func(&self) -> Func { + match self.ext.which { Extern::Func(f) => f, _ => unsafe { std::hint::unreachable_unchecked() }, } } } -impl From for wasm_func_t { - fn from(func: Func) -> wasm_func_t { - wasm_func_t { - ext: wasm_extern_t { which: func.into() }, - } - } -} - -fn create_function( - store: &wasm_store_t, +unsafe fn create_function( + store: &mut wasm_store_t, ty: &wasm_functype_t, - func: impl Fn(Caller<'_>, *const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option> + func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option> + + Send + + Sync + 'static, ) -> Box { - let store = &store.store; let ty = ty.ty().ty.clone(); - let func = Func::new(store, ty, move |caller, params, results| { - let params: wasm_val_vec_t = params - .iter() - .cloned() - .map(|p| wasm_val_t::from_val(p)) - .collect::>() - .into(); - let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into(); - let out = func(caller, ¶ms, &mut out_results); - if let Some(trap) = out { - return Err(trap.trap.clone()); - } + let func = Func::new( + store.store.context_mut(), + ty, + move |_caller, params, results| { + let params: wasm_val_vec_t = params + .iter() + .cloned() + .map(|p| wasm_val_t::from_val(p)) + .collect::>() + .into(); + let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into(); + let out = func(¶ms, &mut out_results); + if let Some(trap) = out { + return Err(trap.trap.clone()); + } - let out_results = out_results.as_slice(); - for i in 0..results.len() { - results[i] = out_results[i].val(); - } - Ok(()) - }); - Box::new(func.into()) + let out_results = out_results.as_slice(); + for i in 0..results.len() { + results[i] = out_results[i].val(); + } + Ok(()) + }, + ); + Box::new(wasm_func_t { + ext: wasm_extern_t { + store: store.store.clone(), + which: func.into(), + }, + }) } #[no_mangle] -pub extern "C" fn wasm_func_new( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_func_new( + store: &mut wasm_store_t, ty: &wasm_functype_t, callback: wasm_func_callback_t, ) -> Box { - create_function(store, ty, move |_caller, params, results| { - callback(params, results) - }) + create_function(store, ty, move |params, results| callback(params, results)) } -#[no_mangle] -pub unsafe extern "C" fn wasmtime_func_new( - store: &wasm_store_t, - ty: &wasm_functype_t, - callback: wasmtime_func_callback_t, -) -> Box { - create_function(store, ty, move |caller, params, results| { - callback(&wasmtime_caller_t { caller }, params, results) - }) -} +// #[no_mangle] +// pub unsafe extern "C" fn wasmtime_func_new( +// store: &wasm_store_t, +// ty: &wasm_functype_t, +// callback: wasmtime_func_callback_t, +// ) -> Box { +// create_function(store, ty, move |params, results| { +// callback(&wasmtime_caller_t { caller }, params, results) +// }) +// } #[no_mangle] -pub extern "C" fn wasm_func_new_with_env( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_func_new_with_env( + store: &mut wasm_store_t, ty: &wasm_functype_t, callback: wasm_func_callback_with_env_t, env: *mut c_void, finalizer: Option, ) -> Box { let finalizer = Finalizer { env, finalizer }; - create_function(store, ty, move |_caller, params, results| { + create_function(store, ty, move |params, results| { callback(finalizer.env, params, results) }) } -#[no_mangle] -pub extern "C" fn wasmtime_func_new_with_env( - store: &wasm_store_t, - ty: &wasm_functype_t, - callback: wasmtime_func_callback_with_env_t, - env: *mut c_void, - finalizer: Option, -) -> Box { - let finalizer = Finalizer { env, finalizer }; - create_function(store, ty, move |caller, params, results| { - callback( - &wasmtime_caller_t { caller }, - finalizer.env, - params, - results, - ) - }) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_func_new_with_env( +// store: &wasm_store_t, +// ty: &wasm_functype_t, +// callback: wasmtime_func_callback_with_env_t, +// env: *mut c_void, +// finalizer: Option, +// ) -> Box { +// let finalizer = Finalizer { env, finalizer }; +// create_function(store, ty, move |caller, params, results| { +// callback( +// &wasmtime_caller_t { caller }, +// finalizer.env, +// params, +// results, +// ) +// }) +// } #[no_mangle] pub unsafe extern "C" fn wasm_func_call( - wasm_func: &wasm_func_t, + func: &mut wasm_func_t, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t, ) -> *mut wasm_trap_t { - let mut trap = ptr::null_mut(); - let error = _wasmtime_func_call( - wasm_func, - (*args).as_slice(), - (*results).as_uninit_slice(), - &mut trap, - ); - match error { - Some(err) => Box::into_raw(err.to_trap()), - None => trap, - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasmtime_func_call( - func: &wasm_func_t, - args: *const wasm_val_vec_t, - results: *mut wasm_val_vec_t, - trap_ptr: &mut *mut wasm_trap_t, -) -> Option> { - _wasmtime_func_call( - func, - (*args).as_slice(), - (*results).as_uninit_slice(), - trap_ptr, - ) -} - -fn _wasmtime_func_call( - func: &wasm_func_t, - args: &[wasm_val_t], - results: &mut [MaybeUninit], - trap_ptr: &mut *mut wasm_trap_t, -) -> Option> { - let func = func.func(); - if results.len() != func.result_arity() { - return Some(Box::new(anyhow!("wrong number of results provided").into())); + let f = func.func(); + let results = (*results).as_uninit_slice(); + let args = (*args).as_slice(); + if results.len() != f.ty(func.ext.store.context()).results().len() { + return Box::into_raw(Box::new(wasm_trap_t::new( + anyhow!("wrong number of results provided").into(), + ))); } let params = args.iter().map(|i| i.val()).collect::>(); @@ -217,20 +189,19 @@ fn _wasmtime_func_call( // want to try to insulate callers against bugs in wasmtime/wasi/etc if we // can. As a result we catch panics here and transform them to traps to // allow the caller to have any insulation possible against Rust panics. - let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(¶ms))); + let result = panic::catch_unwind(AssertUnwindSafe(|| { + f.call(func.ext.store.context_mut(), ¶ms) + })); match result { Ok(Ok(out)) => { for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) { crate::initialize(slot, wasm_val_t::from_val(val)); } - None + ptr::null_mut() } Ok(Err(trap)) => match trap.downcast::() { - Ok(trap) => { - *trap_ptr = Box::into_raw(Box::new(wasm_trap_t::new(trap))); - None - } - Err(err) => Some(Box::new(err.into())), + Ok(trap) => Box::into_raw(Box::new(wasm_trap_t::new(trap))), + Err(err) => Box::into_raw(Box::new(wasm_trap_t::new(err.into()))), }, Err(panic) => { let trap = if let Some(msg) = panic.downcast_ref::() { @@ -241,25 +212,24 @@ fn _wasmtime_func_call( Trap::new("rust panic happened") }; let trap = Box::new(wasm_trap_t::new(trap)); - *trap_ptr = Box::into_raw(trap); - None + Box::into_raw(trap) } } } #[no_mangle] -pub extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box { - Box::new(wasm_functype_t::new(f.func().ty())) +pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box { + Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context()))) } #[no_mangle] -pub extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize { - f.func().param_arity() +pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize { + f.func().ty(f.ext.store.context()).params().len() } #[no_mangle] -pub extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize { - f.func().result_arity() +pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize { + f.func().ty(f.ext.store.context()).results().len() } #[no_mangle] @@ -267,30 +237,30 @@ pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t &mut (*f).ext } -#[no_mangle] -pub extern "C" fn wasmtime_caller_export_get( - caller: &wasmtime_caller_t, - name: &wasm_name_t, -) -> Option> { - let name = str::from_utf8(name.as_slice()).ok()?; - let which = caller.caller.get_export(name)?; - Some(Box::new(wasm_extern_t { which })) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_caller_export_get( +// caller: &wasmtime_caller_t, +// name: &wasm_name_t, +// ) -> Option> { +// let name = str::from_utf8(name.as_slice()).ok()?; +// let which = caller.caller.get_export(name)?; +// Some(Box::new(wasm_extern_t { which })) +// } -#[no_mangle] -pub extern "C" fn wasmtime_func_as_funcref( - func: &wasm_func_t, - funcrefp: &mut MaybeUninit, -) { - let funcref = wasm_val_t::from_val(Val::FuncRef(Some(func.func().clone()))); - crate::initialize(funcrefp, funcref); -} +// #[no_mangle] +// pub extern "C" fn wasmtime_func_as_funcref( +// func: &wasm_func_t, +// funcrefp: &mut MaybeUninit, +// ) { +// let funcref = wasm_val_t::from_val(Val::FuncRef(Some(func.func().clone()))); +// crate::initialize(funcrefp, funcref); +// } -#[no_mangle] -pub extern "C" fn wasmtime_funcref_as_func(val: &wasm_val_t) -> Option> { - if let Val::FuncRef(Some(f)) = val.val() { - Some(Box::new(f.into())) - } else { - None - } -} +// #[no_mangle] +// pub extern "C" fn wasmtime_funcref_as_func(val: &wasm_val_t) -> Option> { +// if let Val::FuncRef(Some(f)) = val.val() { +// Some(Box::new(f.into())) +// } else { +// None +// } +// } diff --git a/crates/c-api/src/global.rs b/crates/c-api/src/global.rs index 8a370ee433bf..8c6809f0a7a3 100644 --- a/crates/c-api/src/global.rs +++ b/crates/c-api/src/global.rs @@ -1,7 +1,5 @@ -use crate::{handle_result, wasmtime_error_t}; use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t}; use std::mem::MaybeUninit; -use std::ptr; use wasmtime::{Extern, Global}; #[derive(Clone)] @@ -20,8 +18,8 @@ impl wasm_global_t { } } - fn global(&self) -> &Global { - match &self.ext.which { + fn global(&self) -> Global { + match self.ext.which { Extern::Global(g) => g, _ => unsafe { std::hint::unreachable_unchecked() }, } @@ -29,65 +27,70 @@ impl wasm_global_t { } #[no_mangle] -pub extern "C" fn wasm_global_new( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_global_new( + store: &mut wasm_store_t, gt: &wasm_globaltype_t, val: &wasm_val_t, ) -> Option> { - let mut global = ptr::null_mut(); - match wasmtime_global_new(store, gt, val, &mut global) { - Some(_err) => None, - None => { - assert!(!global.is_null()); - Some(unsafe { Box::from_raw(global) }) - } - } -} - -#[no_mangle] -pub extern "C" fn wasmtime_global_new( - store: &wasm_store_t, - gt: &wasm_globaltype_t, - val: &wasm_val_t, - ret: &mut *mut wasm_global_t, -) -> Option> { - let global = Global::new(&store.store, gt.ty().ty.clone(), val.val()); - handle_result(global, |global| { - *ret = Box::into_raw(Box::new(wasm_global_t { + match Global::new(store.store.context_mut(), gt.ty().ty.clone(), val.val()) { + Ok(global) => Some(Box::new(wasm_global_t { ext: wasm_extern_t { + store: store.store.clone(), which: global.into(), }, - })); - }) + })), + Err(_) => None, + } } +// #[no_mangle] +// pub extern "C" fn wasmtime_global_new( +// store: &mut wasm_store_t, +// gt: &wasm_globaltype_t, +// val: &wasm_val_t, +// ret: &mut *mut wasm_global_t, +// ) -> Option> { +// let global = Global::new(store.store.context_mut(), gt.ty().ty.clone(), val.val()); +// handle_result(global, |global| { +// *ret = Box::into_raw(Box::new(wasm_global_t { +// ext: wasm_extern_t { +// store: store.store.clone(), +// which: global.into(), +// }, +// })); +// }) +// } + #[no_mangle] pub extern "C" fn wasm_global_as_extern(g: &wasm_global_t) -> &wasm_extern_t { &g.ext } #[no_mangle] -pub extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box { - let globaltype = g.global().ty(); +pub unsafe extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box { + let globaltype = g.global().ty(&g.ext.store.context()); Box::new(wasm_globaltype_t::new(globaltype)) } #[no_mangle] -pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut MaybeUninit) { - crate::initialize(out, wasm_val_t::from_val(g.global().get())); +pub unsafe extern "C" fn wasm_global_get(g: &mut wasm_global_t, out: &mut MaybeUninit) { + let global = g.global(); + crate::initialize( + out, + wasm_val_t::from_val(global.get(g.ext.store.context_mut())), + ); } #[no_mangle] -pub extern "C" fn wasm_global_set(g: &wasm_global_t, val: &wasm_val_t) { - let result = g.global().set(val.val()); - // FIXME(WebAssembly/wasm-c-api#131) should communicate the error here - drop(result); +pub unsafe extern "C" fn wasm_global_set(g: &mut wasm_global_t, val: &wasm_val_t) { + let global = g.global(); + drop(global.set(g.ext.store.context_mut(), val.val())); } -#[no_mangle] -pub extern "C" fn wasmtime_global_set( - g: &wasm_global_t, - val: &wasm_val_t, -) -> Option> { - handle_result(g.global().set(val.val()), |()| {}) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_global_set( +// g: &wasm_global_t, +// val: &wasm_val_t, +// ) -> Option> { +// handle_result(g.global().set(val.val()), |()| {}) +// } diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs index 9a2dd3b2ca98..4d820f031404 100644 --- a/crates/c-api/src/instance.rs +++ b/crates/c-api/src/instance.rs @@ -1,8 +1,6 @@ use crate::{wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_trap_t}; -use crate::{wasm_instancetype_t, wasm_store_t, wasmtime_error_t}; -use anyhow::Result; -use std::ptr; -use wasmtime::{Extern, Instance, Trap}; +use crate::{wasm_instancetype_t, wasm_store_t, StoreRef}; +use wasmtime::{Extern, Instance}; #[derive(Clone)] #[repr(transparent)] @@ -13,9 +11,10 @@ pub struct wasm_instance_t { wasmtime_c_api_macros::declare_ref!(wasm_instance_t); impl wasm_instance_t { - pub(crate) fn new(instance: Instance) -> wasm_instance_t { + pub(crate) fn new(store: StoreRef, instance: Instance) -> wasm_instance_t { wasm_instance_t { ext: wasm_extern_t { + store: store, which: instance.into(), }, } @@ -28,8 +27,8 @@ impl wasm_instance_t { } } - pub(crate) fn instance(&self) -> &Instance { - match &self.ext.which { + pub(crate) fn instance(&self) -> Instance { + match self.ext.which { Extern::Instance(i) => i, _ => unreachable!(), } @@ -38,117 +37,140 @@ impl wasm_instance_t { #[no_mangle] pub unsafe extern "C" fn wasm_instance_new( - store: &wasm_store_t, + store: &mut wasm_store_t, wasm_module: &wasm_module_t, imports: *const wasm_extern_vec_t, result: Option<&mut *mut wasm_trap_t>, ) -> Option> { - let mut instance = ptr::null_mut(); - let mut trap = ptr::null_mut(); - let err = _wasmtime_instance_new( - store, - wasm_module, - (*imports).as_slice(), - &mut instance, - &mut trap, - ); - match err { - Some(err) => { - assert!(trap.is_null()); - assert!(instance.is_null()); - if let Some(result) = result { - *result = Box::into_raw(err.to_trap()); - } - None - } - None => { - if instance.is_null() { - assert!(!trap.is_null()); - if let Some(result) = result { - *result = trap; - } else { - drop(Box::from_raw(trap)) - } - None - } else { - assert!(trap.is_null()); - Some(Box::from_raw(instance)) - } - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasmtime_instance_new( - store: &wasm_store_t, - module: &wasm_module_t, - imports: *const wasm_extern_vec_t, - instance_ptr: &mut *mut wasm_instance_t, - trap_ptr: &mut *mut wasm_trap_t, -) -> Option> { - _wasmtime_instance_new(store, module, (*imports).as_slice(), instance_ptr, trap_ptr) -} - -fn _wasmtime_instance_new( - store: &wasm_store_t, - module: &wasm_module_t, - imports: &[Option>], - instance_ptr: &mut *mut wasm_instance_t, - trap_ptr: &mut *mut wasm_trap_t, -) -> Option> { - let store = &store.store; - let imports = imports + let imports = (*imports) + .as_slice() .iter() .filter_map(|import| match import { Some(i) => Some(i.which.clone()), None => None, }) .collect::>(); - handle_instantiate( - Instance::new(store, module.module(), &imports), - instance_ptr, - trap_ptr, - ) -} - -pub fn handle_instantiate( - instance: Result, - instance_ptr: &mut *mut wasm_instance_t, - trap_ptr: &mut *mut wasm_trap_t, -) -> Option> { - fn write(ptr: &mut *mut T, val: T) { - *ptr = Box::into_raw(Box::new(val)) - } - - match instance { - Ok(instance) => { - write(instance_ptr, wasm_instance_t::new(instance)); + match Instance::new(store.store.context_mut(), wasm_module.module(), &imports) { + Ok(instance) => Some(Box::new(wasm_instance_t::new( + store.store.clone(), + instance, + ))), + Err(e) => { + if let Some(ptr) = result { + *ptr = Box::into_raw(Box::new(wasm_trap_t::new(e.into()))); + } None } - Err(e) => match e.downcast::() { - Ok(trap) => { - write(trap_ptr, wasm_trap_t::new(trap)); - None - } - Err(e) => Some(Box::new(e.into())), - }, } + // let err = _wasmtime_instance_new( + // store, + // wasm_module, + // (*imports).as_slice(), + // &mut instance, + // &mut trap, + // ); + // match err { + // Some(err) => { + // assert!(trap.is_null()); + // assert!(instance.is_null()); + // if let Some(result) = result { + // *result = Box::into_raw(err.to_trap()); + // } + // None + // } + // None => { + // if instance.is_null() { + // assert!(!trap.is_null()); + // if let Some(result) = result { + // *result = trap; + // } else { + // drop(Box::from_raw(trap)) + // } + // None + // } else { + // assert!(trap.is_null()); + // Some(Box::from_raw(instance)) + // } + // } + // } } +// #[no_mangle] +// pub unsafe extern "C" fn wasmtime_instance_new( +// store: &wasm_store_t, +// module: &wasm_module_t, +// imports: *const wasm_extern_vec_t, +// instance_ptr: &mut *mut wasm_instance_t, +// trap_ptr: &mut *mut wasm_trap_t, +// ) -> Option> { +// _wasmtime_instance_new(store, module, (*imports).as_slice(), instance_ptr, trap_ptr) +// } + +// fn _wasmtime_instance_new( +// store: &wasm_store_t, +// module: &wasm_module_t, +// imports: &[Option>], +// instance_ptr: &mut *mut wasm_instance_t, +// trap_ptr: &mut *mut wasm_trap_t, +// ) -> Option> { +// let store = &store.store; +// let imports = imports +// .iter() +// .filter_map(|import| match import { +// Some(i) => Some(i.which.clone()), +// None => None, +// }) +// .collect::>(); +// handle_instantiate( +// Instance::new(store, module.module(), &imports), +// instance_ptr, +// trap_ptr, +// ) +// } + +// pub fn handle_instantiate( +// instance: Result, +// instance_ptr: &mut *mut wasm_instance_t, +// trap_ptr: &mut *mut wasm_trap_t, +// ) -> Option> { +// fn write(ptr: &mut *mut T, val: T) { +// *ptr = Box::into_raw(Box::new(val)) +// } + +// match instance { +// Ok(instance) => { +// write(instance_ptr, wasm_instance_t::new(instance)); +// None +// } +// Err(e) => match e.downcast::() { +// Ok(trap) => { +// write(trap_ptr, wasm_trap_t::new(trap)); +// None +// } +// Err(e) => Some(Box::new(e.into())), +// }, +// } +// } + #[no_mangle] pub extern "C" fn wasm_instance_as_extern(m: &wasm_instance_t) -> &wasm_extern_t { &m.ext } #[no_mangle] -pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wasm_extern_vec_t) { +pub unsafe extern "C" fn wasm_instance_exports( + instance: &mut wasm_instance_t, + out: &mut wasm_extern_vec_t, +) { + let store = instance.ext.store.clone(); out.set_buffer( instance .instance() - .exports() + .exports(instance.ext.store.context_mut()) .map(|e| { Some(Box::new(wasm_extern_t { which: e.into_extern(), + store: store.clone(), })) }) .collect(), @@ -156,6 +178,8 @@ pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wa } #[no_mangle] -pub extern "C" fn wasm_instance_type(f: &wasm_instance_t) -> Box { - Box::new(wasm_instancetype_t::new(f.instance().ty())) +pub unsafe extern "C" fn wasm_instance_type(f: &wasm_instance_t) -> Box { + Box::new(wasm_instancetype_t::new( + f.instance().ty(f.ext.store.context()), + )) } diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 5dc46d8fdb46..4f0553050f92 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -1,12 +1,7 @@ -//! This file defines the extern "C" API, which is compatible with the -//! [Wasm C API](https://github.com/WebAssembly/wasm-c-api). - #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] #![allow(unknown_lints)] #![allow(improper_ctypes_definitions)] -// TODO complete the C API - mod config; mod engine; mod error; @@ -14,7 +9,7 @@ mod r#extern; mod func; mod global; mod instance; -mod linker; +// mod linker; mod memory; mod module; mod r#ref; @@ -31,7 +26,7 @@ pub use crate::error::*; pub use crate::func::*; pub use crate::global::*; pub use crate::instance::*; -pub use crate::linker::*; +// pub use crate::linker::*; pub use crate::memory::*; pub use crate::module::*; pub use crate::r#extern::*; @@ -43,28 +38,16 @@ pub use crate::types::*; pub use crate::val::*; pub use crate::vec::*; -#[cfg(feature = "wasi")] -mod wasi; -#[cfg(feature = "wasi")] -pub use crate::wasi::*; +// #[cfg(feature = "wasi")] +// mod wasi; +// #[cfg(feature = "wasi")] +// pub use crate::wasi::*; #[cfg(feature = "wat")] mod wat2wasm; #[cfg(feature = "wat")] pub use crate::wat2wasm::*; -#[repr(C)] -#[derive(Clone)] -pub struct wasm_foreign_t { - _unused: [u8; 0], -} - -#[repr(C)] -#[derive(Clone)] -pub struct wasm_shared_module_t { - _unused: [u8; 0], -} - /// Initialize a `MaybeUninit` /// /// TODO: Replace calls to this function with diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs index 54d3936849fb..9edc1aa5df81 100644 --- a/crates/c-api/src/memory.rs +++ b/crates/c-api/src/memory.rs @@ -19,8 +19,8 @@ impl wasm_memory_t { } } - fn memory(&self) -> &Memory { - match &self.ext.which { + fn memory(&self) -> Memory { + match self.ext.which { Extern::Memory(m) => m, _ => unsafe { std::hint::unreachable_unchecked() }, } @@ -28,13 +28,14 @@ impl wasm_memory_t { } #[no_mangle] -pub extern "C" fn wasm_memory_new( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_memory_new( + store: &mut wasm_store_t, mt: &wasm_memorytype_t, ) -> Option> { - let memory = Memory::new(&store.store, mt.ty().ty.clone()).ok()?; + let memory = Memory::new(store.store.context_mut(), mt.ty().ty.clone()).ok()?; Some(Box::new(wasm_memory_t { ext: wasm_extern_t { + store: store.store.clone(), which: memory.into(), }, })) @@ -46,27 +47,32 @@ pub extern "C" fn wasm_memory_as_extern(m: &wasm_memory_t) -> &wasm_extern_t { } #[no_mangle] -pub extern "C" fn wasm_memory_type(m: &wasm_memory_t) -> Box { - let ty = m.memory().ty(); +pub unsafe extern "C" fn wasm_memory_type(m: &wasm_memory_t) -> Box { + let ty = m.memory().ty(m.ext.store.context()); Box::new(wasm_memorytype_t::new(ty)) } #[no_mangle] -pub extern "C" fn wasm_memory_data(m: &wasm_memory_t) -> *mut u8 { - m.memory().data_ptr() +pub unsafe extern "C" fn wasm_memory_data(m: &wasm_memory_t) -> *mut u8 { + m.memory().data_ptr(m.ext.store.context()) } #[no_mangle] -pub extern "C" fn wasm_memory_data_size(m: &wasm_memory_t) -> usize { - m.memory().data_size() +pub unsafe extern "C" fn wasm_memory_data_size(m: &wasm_memory_t) -> usize { + m.memory().data_size(m.ext.store.context()) } #[no_mangle] -pub extern "C" fn wasm_memory_size(m: &wasm_memory_t) -> wasm_memory_pages_t { - m.memory().size() +pub unsafe extern "C" fn wasm_memory_size(m: &wasm_memory_t) -> wasm_memory_pages_t { + m.memory().size(m.ext.store.context()) } #[no_mangle] -pub extern "C" fn wasm_memory_grow(m: &wasm_memory_t, delta: wasm_memory_pages_t) -> bool { - m.memory().grow(delta).is_ok() +pub unsafe extern "C" fn wasm_memory_grow( + m: &mut wasm_memory_t, + delta: wasm_memory_pages_t, +) -> bool { + let memory = m.memory(); + let mut store = m.ext.store.context_mut(); + memory.grow(&mut store, delta).is_ok() } diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 4b60c67e73cb..7ca33af2a710 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -1,9 +1,7 @@ use crate::{ - handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t, - wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_moduletype_t, wasm_store_t, - wasmtime_error_t, + wasm_byte_vec_t, wasm_exporttype_t, wasm_exporttype_vec_t, wasm_extern_t, wasm_importtype_t, + wasm_importtype_vec_t, wasm_moduletype_t, wasm_store_t, StoreRef, }; -use std::ptr; use wasmtime::{Engine, Extern, Module}; #[derive(Clone)] @@ -15,9 +13,10 @@ pub struct wasm_module_t { wasmtime_c_api_macros::declare_ref!(wasm_module_t); impl wasm_module_t { - pub(crate) fn new(module: Module) -> wasm_module_t { + pub(crate) fn new(store: StoreRef, module: Module) -> wasm_module_t { wasm_module_t { ext: wasm_extern_t { + store: store, which: module.into(), }, } @@ -47,50 +46,46 @@ pub struct wasm_shared_module_t { wasmtime_c_api_macros::declare_own!(wasm_shared_module_t); #[no_mangle] -pub extern "C" fn wasm_module_new( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_module_new( + store: &mut wasm_store_t, binary: &wasm_byte_vec_t, ) -> Option> { - let mut ret = ptr::null_mut(); - let engine = wasm_engine_t { - engine: store.store.engine().clone(), - }; - match wasmtime_module_new(&engine, binary, &mut ret) { - Some(_err) => None, - None => { - assert!(!ret.is_null()); - Some(unsafe { Box::from_raw(ret) }) - } + match Module::from_binary(store.store.context().engine(), binary.as_slice()) { + Ok(module) => Some(Box::new(wasm_module_t::new(store.store.clone(), module))), + Err(_) => None, } } -#[no_mangle] -pub extern "C" fn wasmtime_module_new( - engine: &wasm_engine_t, - binary: &wasm_byte_vec_t, - ret: &mut *mut wasm_module_t, -) -> Option> { - let binary = binary.as_slice(); - handle_result(Module::from_binary(&engine.engine, binary), |module| { - let module = Box::new(wasm_module_t::new(module)); - *ret = Box::into_raw(module); - }) -} - -#[no_mangle] -pub extern "C" fn wasm_module_validate(store: &wasm_store_t, binary: &wasm_byte_vec_t) -> bool { - wasmtime_module_validate(store, binary).is_none() -} +// #[no_mangle] +// pub extern "C" fn wasmtime_module_new( +// engine: &wasm_engine_t, +// binary: &wasm_byte_vec_t, +// ret: &mut *mut wasm_module_t, +// ) -> Option> { +// let binary = binary.as_slice(); +// handle_result(Module::from_binary(&engine.engine, binary), |module| { +// let module = Box::new(wasm_module_t::new(module)); +// *ret = Box::into_raw(module); +// }) +// } #[no_mangle] -pub extern "C" fn wasmtime_module_validate( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_module_validate( + store: &mut wasm_store_t, binary: &wasm_byte_vec_t, -) -> Option> { - let binary = binary.as_slice(); - handle_result(Module::validate(store.store.engine(), binary), |()| {}) +) -> bool { + Module::validate(store.store.context().engine(), binary.as_slice()).is_ok() } +// #[no_mangle] +// pub extern "C" fn wasmtime_module_validate( +// store: &wasm_store_t, +// binary: &wasm_byte_vec_t, +// ) -> Option> { +// let binary = binary.as_slice(); +// handle_result(Module::validate(store.store.engine(), binary), |()| {}) +// } + #[no_mangle] pub extern "C" fn wasm_module_as_extern(m: &wasm_module_t) -> &wasm_extern_t { &m.ext @@ -135,64 +130,61 @@ pub extern "C" fn wasm_module_share(module: &wasm_module_t) -> Box Option> { let module = shared_module.module.clone(); - if !Engine::same(store.store.engine(), module.engine()) { - return None; + if Engine::same(store.store.context().engine(), module.engine()) { + Some(Box::new(wasm_module_t::new(store.store.clone(), module))) + } else { + None } - Some(Box::new(wasm_module_t::new(module))) } #[no_mangle] pub extern "C" fn wasm_module_serialize(module: &wasm_module_t, ret: &mut wasm_byte_vec_t) { - drop(wasmtime_module_serialize(module, ret)); + if let Ok(buf) = module.module().serialize() { + ret.set_buffer(buf); + } + // drop(wasmtime_module_serialize(module, ret)); } #[no_mangle] -pub extern "C" fn wasm_module_deserialize( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_module_deserialize( + store: &mut wasm_store_t, binary: &wasm_byte_vec_t, ) -> Option> { - let mut ret = ptr::null_mut(); - let engine = wasm_engine_t { - engine: store.store.engine().clone(), - }; - match wasmtime_module_deserialize(&engine, binary, &mut ret) { - Some(_err) => None, - None => { - assert!(!ret.is_null()); - Some(unsafe { Box::from_raw(ret) }) - } + match Module::deserialize(store.store.context().engine(), binary.as_slice()) { + Ok(module) => Some(Box::new(wasm_module_t::new(store.store.clone(), module))), + Err(_) => None, } } -#[no_mangle] -pub extern "C" fn wasmtime_module_serialize( - module: &wasm_module_t, - ret: &mut wasm_byte_vec_t, -) -> Option> { - handle_result(module.module().serialize(), |buf| { - ret.set_buffer(buf); - }) -} - -#[no_mangle] -pub extern "C" fn wasmtime_module_deserialize( - engine: &wasm_engine_t, - binary: &wasm_byte_vec_t, - ret: &mut *mut wasm_module_t, -) -> Option> { - handle_result( - unsafe { Module::deserialize(&engine.engine, binary.as_slice()) }, - |module| { - let module = Box::new(wasm_module_t::new(module)); - *ret = Box::into_raw(module); - }, - ) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_module_serialize( +// module: &wasm_module_t, +// ret: &mut wasm_byte_vec_t, +// ) -> Option> { +// handle_result(module.module().serialize(), |buf| { +// ret.set_buffer(buf); +// }) +// } + +// #[no_mangle] +// pub extern "C" fn wasmtime_module_deserialize( +// engine: &wasm_engine_t, +// binary: &wasm_byte_vec_t, +// ret: &mut *mut wasm_module_t, +// ) -> Option> { +// handle_result( +// unsafe { Module::deserialize(&engine.engine, binary.as_slice()) }, +// |module| { +// let module = Box::new(wasm_module_t::new(module)); +// *ret = Box::into_raw(module); +// }, +// ) +// } #[no_mangle] pub extern "C" fn wasm_module_type(f: &wasm_module_t) -> Box { diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs index 890a6e96e819..87f749ad5037 100644 --- a/crates/c-api/src/ref.rs +++ b/crates/c-api/src/ref.rs @@ -93,6 +93,9 @@ struct CExternRef { finalizer: Option, } +unsafe impl Send for CExternRef {} +unsafe impl Sync for CExternRef {} + impl Drop for CExternRef { fn drop(&mut self) { if let Some(f) = self.finalizer { diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index e303b0e382c3..9963c2734b6a 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -1,10 +1,27 @@ -use crate::{wasm_engine_t, wasmtime_error_t}; -use wasmtime::{InterruptHandle, Store}; +use crate::wasm_engine_t; +use std::cell::UnsafeCell; +use std::sync::Arc; +use wasmtime::{AsContext, AsContextMut, Store, StoreContext, StoreContextMut}; + +#[derive(Clone)] +pub struct StoreRef { + store: Arc>>, +} + +impl StoreRef { + pub unsafe fn context(&self) -> StoreContext<'_, ()> { + (*self.store.get()).as_context() + } + + pub unsafe fn context_mut(&mut self) -> StoreContextMut<'_, ()> { + (*self.store.get()).as_context_mut() + } +} #[repr(C)] #[derive(Clone)] pub struct wasm_store_t { - pub(crate) store: Store, + pub(crate) store: StoreRef, } wasmtime_c_api_macros::declare_own!(wasm_store_t); @@ -12,52 +29,55 @@ wasmtime_c_api_macros::declare_own!(wasm_store_t); #[no_mangle] pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box { let engine = &engine.engine; + let store = Store::new(engine, ()); Box::new(wasm_store_t { - store: Store::new(&engine), + store: StoreRef { + store: Arc::new(UnsafeCell::new(store)), + }, }) } -#[no_mangle] -pub extern "C" fn wasmtime_store_gc(store: &wasm_store_t) { - store.store.gc(); -} +// #[no_mangle] +// pub extern "C" fn wasmtime_store_gc(store: &wasm_store_t) { +// store.store.gc(); +// } -#[repr(C)] -pub struct wasmtime_interrupt_handle_t { - handle: InterruptHandle, -} +// #[repr(C)] +// pub struct wasmtime_interrupt_handle_t { +// handle: InterruptHandle, +// } -wasmtime_c_api_macros::declare_own!(wasmtime_interrupt_handle_t); +// wasmtime_c_api_macros::declare_own!(wasmtime_interrupt_handle_t); -#[no_mangle] -pub extern "C" fn wasmtime_interrupt_handle_new( - store: &wasm_store_t, -) -> Option> { - Some(Box::new(wasmtime_interrupt_handle_t { - handle: store.store.interrupt_handle().ok()?, - })) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_interrupt_handle_new( +// store: &wasm_store_t, +// ) -> Option> { +// Some(Box::new(wasmtime_interrupt_handle_t { +// handle: store.store.interrupt_handle().ok()?, +// })) +// } -#[no_mangle] -pub extern "C" fn wasmtime_interrupt_handle_interrupt(handle: &wasmtime_interrupt_handle_t) { - handle.handle.interrupt(); -} +// #[no_mangle] +// pub extern "C" fn wasmtime_interrupt_handle_interrupt(handle: &wasmtime_interrupt_handle_t) { +// handle.handle.interrupt(); +// } -#[no_mangle] -pub extern "C" fn wasmtime_store_add_fuel( - store: &wasm_store_t, - fuel: u64, -) -> Option> { - crate::handle_result(store.store.add_fuel(fuel), |()| {}) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_store_add_fuel( +// store: &wasm_store_t, +// fuel: u64, +// ) -> Option> { +// crate::handle_result(store.store.add_fuel(fuel), |()| {}) +// } -#[no_mangle] -pub extern "C" fn wasmtime_store_fuel_consumed(store: &wasm_store_t, fuel: &mut u64) -> bool { - match store.store.fuel_consumed() { - Some(amt) => { - *fuel = amt; - true - } - None => false, - } -} +// #[no_mangle] +// pub extern "C" fn wasmtime_store_fuel_consumed(store: &wasm_store_t, fuel: &mut u64) -> bool { +// match store.store.fuel_consumed() { +// Some(amt) => { +// *fuel = amt; +// true +// } +// None => false, +// } +// } diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs index 6438f4976f39..d9a987dc8be5 100644 --- a/crates/c-api/src/table.rs +++ b/crates/c-api/src/table.rs @@ -1,7 +1,6 @@ use crate::r#ref::{ref_to_val, val_into_ref}; -use crate::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t}; +use crate::wasm_ref_t; use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t}; -use std::ptr; use wasmtime::{Extern, Table, TableType, Val, ValType}; #[derive(Clone)] @@ -22,8 +21,8 @@ impl wasm_table_t { } } - fn table(&self) -> &Table { - match &self.ext.which { + fn table(&self) -> Table { + match self.ext.which { Extern::Table(t) => t, _ => unsafe { std::hint::unreachable_unchecked() }, } @@ -42,133 +41,143 @@ fn ref_to_val_for_table(r: Option<&wasm_ref_t>, table_ty: &TableType) -> Val { } #[no_mangle] -pub extern "C" fn wasm_table_new( - store: &wasm_store_t, +pub unsafe extern "C" fn wasm_table_new( + store: &mut wasm_store_t, tt: &wasm_tabletype_t, init: Option<&wasm_ref_t>, ) -> Option> { let init = ref_to_val_for_table(init, &tt.ty().ty); - let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?; + let table = Table::new(store.store.context_mut(), tt.ty().ty.clone(), init).ok()?; Some(Box::new(wasm_table_t { ext: wasm_extern_t { + store: store.store.clone(), which: table.into(), }, })) } -#[no_mangle] -pub extern "C" fn wasmtime_funcref_table_new( - store: &wasm_store_t, - tt: &wasm_tabletype_t, - init: Option<&wasm_func_t>, - out: &mut *mut wasm_table_t, -) -> Option> { - let init: Val = match init { - Some(val) => Val::FuncRef(Some(val.func().clone())), - None => Val::FuncRef(None), - }; - handle_result( - Table::new(&store.store, tt.ty().ty.clone(), init), - |table| { - *out = Box::into_raw(Box::new(wasm_table_t { - ext: wasm_extern_t { - which: table.into(), - }, - })); - }, - ) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_funcref_table_new( +// store: &wasm_store_t, +// tt: &wasm_tabletype_t, +// init: Option<&wasm_func_t>, +// out: &mut *mut wasm_table_t, +// ) -> Option> { +// let init: Val = match init { +// Some(val) => Val::FuncRef(Some(val.func().clone())), +// None => Val::FuncRef(None), +// }; +// handle_result( +// Table::new(store.store.context(), tt.ty().ty.clone(), init), +// |table| { +// *out = Box::into_raw(Box::new(wasm_table_t { +// ext: wasm_extern_t { +// store: store.store.clone(), +// which: table.into(), +// }, +// })); +// }, +// ) +// } #[no_mangle] -pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box { - let ty = t.table().ty(); - Box::new(wasm_tabletype_t::new(ty)) +pub unsafe extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box { + let table = t.table(); + let store = t.ext.store.context(); + Box::new(wasm_tabletype_t::new(table.ty(&store))) } #[no_mangle] -pub extern "C" fn wasm_table_get( - t: &wasm_table_t, +pub unsafe extern "C" fn wasm_table_get( + t: &mut wasm_table_t, index: wasm_table_size_t, ) -> Option> { - let val = t.table().get(index)?; + let table = t.table(); + let val = table.get(t.ext.store.context_mut(), index)?; val_into_ref(val) } -#[no_mangle] -pub extern "C" fn wasmtime_funcref_table_get( - t: &wasm_table_t, - index: wasm_table_size_t, - ptr: &mut *mut wasm_func_t, -) -> bool { - match t.table().get(index) { - Some(val) => { - *ptr = match val { - Val::FuncRef(None) => ptr::null_mut(), - Val::FuncRef(Some(f)) => Box::into_raw(Box::new(f.into())), - _ => return false, - }; - } - - _ => return false, - } - true -} +// #[no_mangle] +// pub extern "C" fn wasmtime_funcref_table_get( +// t: &wasm_table_t, +// index: wasm_table_size_t, +// ptr: &mut *mut wasm_func_t, +// ) -> bool { +// let table = t.table(); +// let mut store = t.ext.store.context(); +// match table.get(&mut store, index) { +// Some(val) => { +// *ptr = match val { +// Val::FuncRef(None) => ptr::null_mut(), +// Val::FuncRef(Some(f)) => Box::into_raw(Box::new(f.into())), +// _ => return false, +// }; +// } + +// _ => return false, +// } +// true +// } #[no_mangle] pub unsafe extern "C" fn wasm_table_set( - t: &wasm_table_t, + t: &mut wasm_table_t, index: wasm_table_size_t, r: Option<&wasm_ref_t>, ) -> bool { - let val = ref_to_val_for_table(r, &t.table().ty()); - t.table().set(index, val).is_ok() + let table = t.table(); + let val = ref_to_val_for_table(r, &table.ty(t.ext.store.context())); + table.set(t.ext.store.context_mut(), index, val).is_ok() } -#[no_mangle] -pub extern "C" fn wasmtime_funcref_table_set( - t: &wasm_table_t, - index: wasm_table_size_t, - val: Option<&wasm_func_t>, -) -> Option> { - let val = match val { - Some(val) => Val::FuncRef(Some(val.func().clone())), - None => Val::FuncRef(None), - }; - handle_result(t.table().set(index, val), |()| {}) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_funcref_table_set( +// t: &wasm_table_t, +// index: wasm_table_size_t, +// val: Option<&wasm_func_t>, +// ) -> Option> { +// let val = match val { +// Some(val) => Val::FuncRef(Some(val.func().clone())), +// None => Val::FuncRef(None), +// }; +// handle_result(t.table().set(index, val), |()| {}) +// } #[no_mangle] -pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t { - t.table().size() +pub unsafe extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t { + let table = t.table(); + let store = t.ext.store.context(); + table.size(&store) } #[no_mangle] pub unsafe extern "C" fn wasm_table_grow( - t: &wasm_table_t, + t: &mut wasm_table_t, delta: wasm_table_size_t, init: Option<&wasm_ref_t>, ) -> bool { - let init = ref_to_val_for_table(init, &t.table().ty()); - t.table().grow(delta, init).is_ok() + let table = t.table(); + let init = ref_to_val_for_table(init, &table.ty(t.ext.store.context())); + table.grow(t.ext.store.context_mut(), delta, init).is_ok() } -#[no_mangle] -pub extern "C" fn wasmtime_funcref_table_grow( - t: &wasm_table_t, - delta: wasm_table_size_t, - init: Option<&wasm_func_t>, - prev_size: Option<&mut wasm_table_size_t>, -) -> Option> { - let val = match init { - Some(val) => Val::FuncRef(Some(val.func().clone())), - None => Val::FuncRef(None), - }; - handle_result(t.table().grow(delta, val), |prev| { - if let Some(ptr) = prev_size { - *ptr = prev; - } - }) -} +// #[no_mangle] +// pub extern "C" fn wasmtime_funcref_table_grow( +// t: &wasm_table_t, +// delta: wasm_table_size_t, +// init: Option<&wasm_func_t>, +// prev_size: Option<&mut wasm_table_size_t>, +// ) -> Option> { +// let val = match init { +// Some(val) => Val::FuncRef(Some(val.func().clone())), +// None => Val::FuncRef(None), +// }; +// andle_result(t.table().grow(delta, val), |prev| { +// if let Some(ptr) = prev_size { +// *ptr = prev; +// } +// }) +// } #[no_mangle] pub extern "C" fn wasm_table_as_extern(t: &wasm_table_t) -> &wasm_extern_t { diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index 99dc44073fea..2f8b881883c6 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -206,7 +206,8 @@ impl From for Extern { /// Globals are internally reference counted so you can `clone` a `Global`. The /// cloning process only performs a shallow clone, so two cloned `Global` /// instances are equivalent in their functionality. -#[derive(Clone)] +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] // here for the C API pub struct Global(Stored); impl Global { @@ -347,7 +348,8 @@ impl Global { /// Tables are internally reference counted so you can `clone` a `Table`. The /// cloning process only performs a shallow clone, so two cloned `Table` /// instances are equivalent in their functionality. -#[derive(Clone)] +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] // here for the C API pub struct Table(Stored); impl Table { diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 25f6298ab993..3d825ff501c9 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -200,7 +200,8 @@ use wasmtime_runtime::{ /// # Ok(()) /// # } /// ``` -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] // here for the C API pub struct Func(Stored); pub(crate) enum FuncData { diff --git a/crates/wasmtime/src/func/typed.rs b/crates/wasmtime/src/func/typed.rs index 969d734f3e28..ccc7979c2094 100644 --- a/crates/wasmtime/src/func/typed.rs +++ b/crates/wasmtime/src/func/typed.rs @@ -15,18 +15,17 @@ use wasmtime_runtime::{VMContext, VMFunctionBody}; /// /// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`]. /// For more documentation about this see those methods. -#[repr(transparent)] +#[repr(transparent)] // here for the C API pub struct TypedFunc { _a: marker::PhantomData Results>, func: Func, } +impl Copy for TypedFunc {} + impl Clone for TypedFunc { fn clone(&self) -> TypedFunc { - TypedFunc { - _a: marker::PhantomData, - func: self.func.clone(), - } + *self } } diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index b82cecacd1de..7d933c1cb7b7 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -33,7 +33,8 @@ use wasmtime_runtime::{ /// /// When interacting with any wasm code you'll want to make an [`Instance`] to /// call any code or execute anything! -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] pub struct Instance(Stored); impl Instance { diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/memory.rs index a6b25c030578..db0e227eb4e7 100644 --- a/crates/wasmtime/src/memory.rs +++ b/crates/wasmtime/src/memory.rs @@ -245,7 +245,8 @@ impl std::error::Error for MemoryAccessError {} /// /// [interface types]: https://github.com/webassembly/interface-types /// [open an issue]: https://github.com/bytecodealliance/wasmtime/issues/new -#[derive(Clone)] +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] // here for the C API pub struct Memory(Stored); impl Memory { diff --git a/crates/wasmtime/src/store/context.rs b/crates/wasmtime/src/store/context.rs index 37bc7e0c42ad..1c1d486bfec5 100644 --- a/crates/wasmtime/src/store/context.rs +++ b/crates/wasmtime/src/store/context.rs @@ -2,9 +2,11 @@ use crate::store::{Store, StoreInner}; use std::ops::{Deref, DerefMut}; /// TODO +#[repr(transparent)] // here for the C API pub struct StoreContext<'a, T>(pub(super) &'a StoreInner); /// TODO +#[repr(transparent)] // here for the C API pub struct StoreContextMut<'a, T>(pub(super) &'a mut StoreInner); impl<'a, T> StoreContextMut<'a, T> { diff --git a/crates/wasmtime/src/store/data.rs b/crates/wasmtime/src/store/data.rs index bf8d7355aa33..65a67412ee3e 100644 --- a/crates/wasmtime/src/store/data.rs +++ b/crates/wasmtime/src/store/data.rs @@ -169,6 +169,7 @@ where } } +#[repr(transparent)] pub struct Stored { // See documentation below on `INDEX_BITS` for how this is interpreted. bits: u64, From 8bfe58edef2fb1a721f32b7d30551bffb1df0e2b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 07:10:27 -0700 Subject: [PATCH 04/90] more c api --- crates/c-api/include/wasmtime.h | 150 ++++++++++++++++++-------------- crates/c-api/src/module.rs | 127 +++++++++++++++++---------- 2 files changed, 165 insertions(+), 112 deletions(-) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index f163f923bbd3..b0b75206cc3b 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -1,11 +1,9 @@ /** * @file * - * Wasmtime-specific extensions to the WebAssembly C API. + * Wasmtime's C API * - * This file contains all of the Wasmtime-specific functions which will not be - * present in other engines. The intention of this file is to augment the - * functionality provided in `wasm.h`. + * TODO */ @@ -19,28 +17,42 @@ extern "C" { #endif -#define own - -#define WASMTIME_DECLARE_OWN(name) \ - typedef struct wasmtime_##name##_t wasmtime_##name##_t; \ - \ - WASM_API_EXTERN void wasmtime_##name##_delete(own wasmtime_##name##_t*); - /** * \typedef wasmtime_error_t * \brief Convenience alias for #wasmtime_error_t * - * \struct wasmtime_error_t + * \struct wasmtime_error * \brief Errors generated by Wasmtime. * * This opaque type represents an error that happened as part of one of the * functions below. Errors primarily have an error message associated with them * at this time, which you can acquire by calling #wasmtime_error_message. - * - * \fn void wasmtime_error_delete(own wasmtime_error_t *); + */ +typedef struct wasmtime_error wasmtime_error_t; + +/* * \brief Deletes an error. */ -WASMTIME_DECLARE_OWN(error) +WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error); + +/** + * \brief Converts from the text format of WebAssembly to to the binary format. + * + * \param wat this it the input buffer with the WebAssembly Text Format inside of + * it. This will be parsed and converted to the binary format. + * \param ret if the conversion is successful, this byte vector is filled in with + * the WebAssembly binary format. + * + * \return a non-null error if parsing fails, or returns `NULL`. If parsing + * fails then `ret` isn't touched. + * + * This function does not take ownership of `wat`, and the caller is expected to + * deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. + */ +WASM_API_EXTERN own wasmtime_error_t* wasmtime_wat2wasm( + const wasm_byte_vec_t *wat, + own wasm_byte_vec_t *ret +); /** * \brief Returns the string description of this error. @@ -298,24 +310,69 @@ WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_t*, const char*); /** - * \brief Converts from the text format of WebAssembly to to the binary format. + * \typedef wasmtime_module_t + * \brief Convenience alias for #wasmtime_module * - * \param wat this it the input buffer with the WebAssembly Text Format inside of - * it. This will be parsed and converted to the binary format. - * \param ret if the conversion is successful, this byte vector is filled in with - * the WebAssembly binary format. + * \struct wasmtime_module + * \brief A compiled Wasmtime module. * - * \return a non-null error if parsing fails, or returns `NULL`. If parsing - * fails then `ret` isn't touched. + * This type represents a compiled WebAssembly module. The compiled module is + * ready to be instantiated and can be inspected for imports/exports. It is safe + * to use a #wasmtime_module_t across multiple threads simultaneously. + */ +typedef struct wasmtime_module wasmtime_module_t; + +/* + * \brief Deletes a module. + */ +WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m); + +/** + * \brief Compiles a WebAssembly binary into a #wasmtime_module_t * - * This function does not take ownership of `wat`, and the caller is expected to - * deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. + * This function will compile a WebAssembly binary into an owned #wasm_module_t. + * This performs the same as #wasm_module_new except that it returns a + * #wasmtime_error_t type to get richer error information. + * + * On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is + * filled in with a #wasm_module_t. On failure the #wasmtime_error_t is + * non-`NULL` and the `ret` pointer is unmodified. + * + * This function does not take ownership of any of its arguments, but the + * returned error and module are owned by the caller. */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_wat2wasm( - const wasm_byte_vec_t *wat, - own wasm_byte_vec_t *ret +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( + wasm_engine_t *engine, + const wasm_byte_vec_t *binary, + own wasm_module_t **ret +); + +/** + * \brief Validate a WebAssembly binary. + * + * This function will validate the provided byte sequence to determine if it is + * a valid WebAssembly binary within the context of the engine provided. + * + * This function does not take ownership of its arguments but the caller is + * expected to deallocate the returned error if it is non-`NULL`. + * + * If the binary validates then `NULL` is returned, otherwise the error returned + * describes why the binary did not validate. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate( + wasm_engine_t *engine, + const wasm_byte_vec_t *binary ); +/* WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); */ +/* WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out); */ + +/** + * \brief Serializes = + */ +WASM_API_EXTERN void wasmtime_module_serialize(const wasmtime_module_t*, wasm_byte_vec_t* out); +WASM_API_EXTERN wasmtime_error_t* wasmtime_module_deserialize(wasm_engine_t*, const wasm_byte_vec_t*); + /** * \brief Perform garbage collection within the given store. * @@ -798,45 +855,6 @@ WASM_API_EXTERN own wasmtime_error_t *wasmtime_instance_new( own wasm_trap_t **trap ); -/** - * \brief Wasmtime-specific function to compile a module. - * - * This function will compile a WebAssembly binary into an owned #wasm_module_t. - * This performs the same as #wasm_module_new except that it returns a - * #wasmtime_error_t type to get richer error information. - * - * On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is - * filled in with a #wasm_module_t. On failure the #wasmtime_error_t is - * non-`NULL` and the `ret` pointer is unmodified. - * - * This function does not take ownership of any of its arguments, but the - * returned error and module are owned by the caller. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_module_new( - wasm_engine_t *engine, - const wasm_byte_vec_t *binary, - own wasm_module_t **ret -); - -/** - * \brief Wasmtime-specific function to validate a module. - * - * This function will validate the provided byte sequence to determine if it is - * a valid WebAssembly binary. This function performs the same as - * #wasm_module_validate except that it returns a #wasmtime_error_t which - * contains an error message if validation fails. - * - * This function does not take ownership of its arguments but the caller is - * expected to deallocate the returned error if it is non-`NULL`. - * - * If the binary validates then `NULL` is returned, otherwise the error returned - * describes why the binary did not validate. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_module_validate( - wasm_store_t *store, - const wasm_byte_vec_t *binary -); - /** * \brief Creates a new host-defined wasm table. diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 7ca33af2a710..31b15e1461d4 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -1,6 +1,7 @@ use crate::{ - wasm_byte_vec_t, wasm_exporttype_t, wasm_exporttype_vec_t, wasm_extern_t, wasm_importtype_t, - wasm_importtype_vec_t, wasm_moduletype_t, wasm_store_t, StoreRef, + handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t, + wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_moduletype_t, wasm_store_t, + wasmtime_error_t, StoreRef, }; use wasmtime::{Engine, Extern, Module}; @@ -56,19 +57,6 @@ pub unsafe extern "C" fn wasm_module_new( } } -// #[no_mangle] -// pub extern "C" fn wasmtime_module_new( -// engine: &wasm_engine_t, -// binary: &wasm_byte_vec_t, -// ret: &mut *mut wasm_module_t, -// ) -> Option> { -// let binary = binary.as_slice(); -// handle_result(Module::from_binary(&engine.engine, binary), |module| { -// let module = Box::new(wasm_module_t::new(module)); -// *ret = Box::into_raw(module); -// }) -// } - #[no_mangle] pub unsafe extern "C" fn wasm_module_validate( store: &mut wasm_store_t, @@ -77,15 +65,6 @@ pub unsafe extern "C" fn wasm_module_validate( Module::validate(store.store.context().engine(), binary.as_slice()).is_ok() } -// #[no_mangle] -// pub extern "C" fn wasmtime_module_validate( -// store: &wasm_store_t, -// binary: &wasm_byte_vec_t, -// ) -> Option> { -// let binary = binary.as_slice(); -// handle_result(Module::validate(store.store.engine(), binary), |()| {}) -// } - #[no_mangle] pub extern "C" fn wasm_module_as_extern(m: &wasm_module_t) -> &wasm_extern_t { &m.ext @@ -147,7 +126,6 @@ pub extern "C" fn wasm_module_serialize(module: &wasm_module_t, ret: &mut wasm_b if let Ok(buf) = module.module().serialize() { ret.set_buffer(buf); } - // drop(wasmtime_module_serialize(module, ret)); } #[no_mangle] @@ -161,32 +139,89 @@ pub unsafe extern "C" fn wasm_module_deserialize( } } +pub struct wasmtime_module_t { + module: Module, +} + +#[no_mangle] +pub extern "C" fn wasmtime_module_new( + engine: &wasm_engine_t, + binary: &wasm_byte_vec_t, + out: &mut *mut wasmtime_module_t, +) -> Option> { + handle_result( + Module::from_binary(&engine.engine, binary.as_slice()), + |module| { + *out = Box::into_raw(Box::new(wasmtime_module_t { module })); + }, + ) +} + +#[no_mangle] +pub extern "C" fn wasmtime_module_delete(_module: Box) {} + +#[no_mangle] +pub extern "C" fn wasmtime_module_validate( + engine: &wasm_engine_t, + binary: &wasm_byte_vec_t, +) -> Option> { + handle_result(Module::validate(&engine.engine, binary.as_slice()), |()| {}) +} + // #[no_mangle] -// pub extern "C" fn wasmtime_module_serialize( -// module: &wasm_module_t, -// ret: &mut wasm_byte_vec_t, -// ) -> Option> { -// handle_result(module.module().serialize(), |buf| { -// ret.set_buffer(buf); -// }) +// pub extern "C" fn wasm_module_exports(module: &wasm_module_t, out: &mut wasm_exporttype_vec_t) { +// let exports = module +// .module() +// .exports() +// .map(|e| { +// Some(Box::new(wasm_exporttype_t::new( +// e.name().to_owned(), +// e.ty(), +// ))) +// }) +// .collect::>(); +// out.set_buffer(exports); // } // #[no_mangle] -// pub extern "C" fn wasmtime_module_deserialize( -// engine: &wasm_engine_t, -// binary: &wasm_byte_vec_t, -// ret: &mut *mut wasm_module_t, -// ) -> Option> { -// handle_result( -// unsafe { Module::deserialize(&engine.engine, binary.as_slice()) }, -// |module| { -// let module = Box::new(wasm_module_t::new(module)); -// *ret = Box::into_raw(module); -// }, -// ) +// pub extern "C" fn wasm_module_imports(module: &wasm_module_t, out: &mut wasm_importtype_vec_t) { +// let imports = module +// .module() +// .imports() +// .map(|i| { +// Some(Box::new(wasm_importtype_t::new( +// i.module().to_owned(), +// i.name().map(|s| s.to_owned()), +// i.ty(), +// ))) +// }) +// .collect::>(); +// out.set_buffer(imports); // } #[no_mangle] -pub extern "C" fn wasm_module_type(f: &wasm_module_t) -> Box { - Box::new(wasm_moduletype_t::new(f.module().ty())) +pub extern "C" fn wasmtime_module_serialize( + module: &wasmtime_module_t, + ret: &mut wasm_byte_vec_t, +) -> Option> { + handle_result(module.module.serialize(), |buf| ret.set_buffer(buf)) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_module_deserialize( + engine: &wasm_engine_t, + binary: &wasm_byte_vec_t, + out: &mut *mut wasmtime_module_t, +) -> Option> { + handle_result( + Module::deserialize(&engine.engine, binary.as_slice()), + |module| { + *out = Box::into_raw(Box::new(wasmtime_module_t { module })); + }, + ) +} + +#[no_mangle] +pub extern "C" fn wasmtime_module_type(m: &wasmtime_module_t) -> Box { + Box::new(wasm_moduletype_t::new(m.module.ty())) } From 3ec6c6a22e1a8b73ef5b603a2e4ec8377d4f0591 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 11:43:16 -0700 Subject: [PATCH 05/90] Lots of C API working now (hopefully) --- crates/c-api/include/wasmtime.h | 1303 +--------------------- crates/c-api/include/wasmtime/config.h | 258 +++++ crates/c-api/include/wasmtime/error.h | 45 + crates/c-api/include/wasmtime/extern.h | 46 + crates/c-api/include/wasmtime/func.h | 127 +++ crates/c-api/include/wasmtime/global.h | 60 + crates/c-api/include/wasmtime/instance.h | 100 ++ crates/c-api/include/wasmtime/linker.h | 212 ++++ crates/c-api/include/wasmtime/memory.h | 47 + crates/c-api/include/wasmtime/module.h | 160 +++ crates/c-api/include/wasmtime/store.h | 129 +++ crates/c-api/include/wasmtime/table.h | 103 ++ crates/c-api/include/wasmtime/trap.h | 46 + crates/c-api/include/wasmtime/val.h | 97 ++ crates/c-api/src/error.rs | 6 - crates/c-api/src/extern.rs | 90 +- crates/c-api/src/func.rs | 217 ++-- crates/c-api/src/global.rs | 68 +- crates/c-api/src/instance.rs | 189 ++-- crates/c-api/src/lib.rs | 24 +- crates/c-api/src/linker.rs | 131 +-- crates/c-api/src/memory.rs | 52 +- crates/c-api/src/module.rs | 77 +- crates/c-api/src/ref.rs | 64 -- crates/c-api/src/store.rs | 131 ++- crates/c-api/src/table.rs | 149 ++- crates/c-api/src/trap.rs | 11 + crates/c-api/src/types/extern.rs | 24 +- crates/c-api/src/types/instance.rs | 25 +- crates/c-api/src/types/module.rs | 29 +- crates/c-api/src/types/val.rs | 9 + crates/c-api/src/val.rs | 124 +- crates/c-api/src/vec.rs | 22 +- crates/wasmtime/src/func.rs | 16 +- crates/wasmtime/src/ref.rs | 1 + crates/wasmtime/src/store.rs | 19 + 36 files changed, 2331 insertions(+), 1880 deletions(-) create mode 100644 crates/c-api/include/wasmtime/config.h create mode 100644 crates/c-api/include/wasmtime/error.h create mode 100644 crates/c-api/include/wasmtime/extern.h create mode 100644 crates/c-api/include/wasmtime/func.h create mode 100644 crates/c-api/include/wasmtime/global.h create mode 100644 crates/c-api/include/wasmtime/instance.h create mode 100644 crates/c-api/include/wasmtime/linker.h create mode 100644 crates/c-api/include/wasmtime/memory.h create mode 100644 crates/c-api/include/wasmtime/module.h create mode 100644 crates/c-api/include/wasmtime/store.h create mode 100644 crates/c-api/include/wasmtime/table.h create mode 100644 crates/c-api/include/wasmtime/trap.h create mode 100644 crates/c-api/include/wasmtime/val.h diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index b0b75206cc3b..53c4d9a917e2 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -1,40 +1,24 @@ -/** - * @file - * - * Wasmtime's C API - * - * TODO - */ - - #ifndef WASMTIME_API_H #define WASMTIME_API_H -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { #endif -/** - * \typedef wasmtime_error_t - * \brief Convenience alias for #wasmtime_error_t - * - * \struct wasmtime_error - * \brief Errors generated by Wasmtime. - * - * This opaque type represents an error that happened as part of one of the - * functions below. Errors primarily have an error message associated with them - * at this time, which you can acquire by calling #wasmtime_error_message. - */ -typedef struct wasmtime_error wasmtime_error_t; - -/* - * \brief Deletes an error. - */ -WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error); - /** * \brief Converts from the text format of WebAssembly to to the binary format. * @@ -49,1268 +33,11 @@ WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error); * This function does not take ownership of `wat`, and the caller is expected to * deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_wat2wasm( +WASM_API_EXTERN wasmtime_error_t* wasmtime_wat2wasm( const wasm_byte_vec_t *wat, - own wasm_byte_vec_t *ret -); - -/** - * \brief Returns the string description of this error. - * - * This will "render" the error to a string and then return the string - * representation of the error to the caller. The `message` argument should be - * uninitialized before this function is called and the caller is responsible - * for deallocating it with #wasm_byte_vec_delete afterwards. - */ -WASM_API_EXTERN void wasmtime_error_message( - const wasmtime_error_t *error, - own wasm_name_t *message -); - -/** - * \brief Specifier for how Wasmtime will compile code, values are in - * #wasmtime_strategy_enum - */ -typedef uint8_t wasmtime_strategy_t; - -/** - * \brief Different ways that Wasmtime can compile WebAssembly - * - * The default value is #WASMTIME_STRATEGY_AUTO. - */ -enum wasmtime_strategy_enum { // Strategy - /// Wasmtime will automatically determine whether to use Cranelift or - /// Lightbeam, and currently it will always pick Cranelift. This default may - /// change over time though. - WASMTIME_STRATEGY_AUTO, - - /// Indicates that Cranelift will unconditionally use Cranelift to compile - /// WebAssembly code. - WASMTIME_STRATEGY_CRANELIFT, - - /// Indicates that Cranelift will unconditionally use Lightbeam to compile - /// WebAssembly code. Note that Lightbeam isn't always enabled at compile - /// time, and if that's the case an error will be returned. - WASMTIME_STRATEGY_LIGHTBEAM, -}; - -/** - * \brief Specifier of what optimization level to use for generated JIT code. - * - * See #wasmtime_opt_level_enum for possible values. - */ -typedef uint8_t wasmtime_opt_level_t; - -/** - * \brief Different ways Wasmtime can optimize generated code. - * - * The default value is #WASMTIME_OPT_LEVEL_SPEED. - */ -enum wasmtime_opt_level_enum { // OptLevel - /// Generated code will not be optimized at all. - WASMTIME_OPT_LEVEL_NONE, - /// Generated code will be optimized purely for speed. - WASMTIME_OPT_LEVEL_SPEED, - /// Generated code will be optimized, but some speed optimizations are - /// disabled if they cause the generated code to be significantly larger. - WASMTIME_OPT_LEVEL_SPEED_AND_SIZE, -}; - -/** - * \brief Different ways wasmtime can enable profiling JIT code. - * - * See #wasmtime_profiling_strategy_enum for possible values. - */ -typedef uint8_t wasmtime_profiling_strategy_t; - -/** - * \brief Different ways to profile JIT code. - * - * The default is #WASMTIME_PROFILING_STRATEGY_NONE. - */ -enum wasmtime_profiling_strategy_enum { // ProfilingStrategy - /// No profiling is enabled at runtime. - WASMTIME_PROFILING_STRATEGY_NONE, - /// Linux's "jitdump" support in `perf` is enabled and when Wasmtime is run - /// under `perf` necessary calls will be made to profile generated JIT code. - WASMTIME_PROFILING_STRATEGY_JITDUMP, - /// Support for VTune will be enabled and the VTune runtime will be informed, - /// at runtime, about JIT code. - /// - /// Note that this isn't always enabled at build time. - WASMTIME_PROFILING_STRATEGY_VTUNE, -}; - -#define WASMTIME_CONFIG_PROP(ret, name, ty) \ - WASM_API_EXTERN ret wasmtime_config_##name##_set(wasm_config_t*, ty); - -/** - * \brief Configures whether DWARF debug information is constructed at runtime - * to describe JIT code. - * - * This setting is `false` by default. When enabled it will attempt to inform - * native debuggers about DWARF debugging information for JIT code to more - * easily debug compiled WebAssembly via native debuggers. This can also - * sometimes improve the quality of output when profiling is enabled. - */ -WASMTIME_CONFIG_PROP(void, debug_info, bool) - -/** - * \brief Enables WebAssembly code to be interrupted. - * - * This setting is `false` by default. When enabled it will enable getting an - * interrupt handle via #wasmtime_interrupt_handle_new which can be used to - * interrupt currently-executing WebAssembly code. - */ -WASMTIME_CONFIG_PROP(void, interruptable, bool) - -/** - * \brief Whether or not fuel is enabled for generated code. - * - * This setting is `false` by default. When enabled it will enable fuel counting - * meaning that fuel will be consumed every time a wasm instruction is executed, - * and trap when reaching zero. - */ -WASMTIME_CONFIG_PROP(void, consume_fuel, bool) - -/** - * \brief Configures the maximum stack size, in bytes, that JIT code can use. - * - * This setting is 2MB by default. Configuring this setting will limit the - * amount of native stack space that JIT code can use while it is executing. If - * you're hitting stack overflow you can try making this setting larger, or if - * you'd like to limit wasm programs to less stack you can also configure this. - * - * Note that this setting is not interpreted with 100% precision. Additionally - * the amount of stack space that wasm takes is always relative to the first - * invocation of wasm on the stack, so recursive calls with host frames in the - * middle will all need to fit within this setting. - */ -WASMTIME_CONFIG_PROP(void, max_wasm_stack, size_t) - -/** - * \brief Configures whether the WebAssembly threading proposal is enabled. - * - * This setting is `false` by default. - * - * Note that threads are largely unimplemented in Wasmtime at this time. - */ -WASMTIME_CONFIG_PROP(void, wasm_threads, bool) - -/** - * \brief Configures whether the WebAssembly reference types proposal is - * enabled. - * - * This setting is `false` by default. - */ -WASMTIME_CONFIG_PROP(void, wasm_reference_types, bool) - -/** - * \brief Configures whether the WebAssembly SIMD proposal is - * enabled. - * - * This setting is `false` by default. - */ -WASMTIME_CONFIG_PROP(void, wasm_simd, bool) - -/** - * \brief Configures whether the WebAssembly bulk memory proposal is - * enabled. - * - * This setting is `false` by default. - */ -WASMTIME_CONFIG_PROP(void, wasm_bulk_memory, bool) - -/** - * \brief Configures whether the WebAssembly multi value proposal is - * enabled. - * - * This setting is `true` by default. - */ -WASMTIME_CONFIG_PROP(void, wasm_multi_value, bool) - -/** - * \brief Configures whether the WebAssembly module linking proposal is - * enabled. - * - * This setting is `false` by default. - */ -WASMTIME_CONFIG_PROP(void, wasm_module_linking, bool) - -/** - * \brief Configures how JIT code will be compiled. - * - * This setting is #WASMTIME_STRATEGY_AUTO by default. - * - * If the compilation strategy selected could not be enabled then an error is - * returned. - */ -WASMTIME_CONFIG_PROP(wasmtime_error_t*, strategy, wasmtime_strategy_t) - -/** - * \brief Configures whether Cranelift's debug verifier is enabled. - * - * This setting in `false` by default. - * - * When cranelift is used for compilation this enables expensive debug checks - * within Cranelift itself to verify it's correct. - */ -WASMTIME_CONFIG_PROP(void, cranelift_debug_verifier, bool) - -/** - * \brief Configures Cranelift's optimization level for JIT code. - * - * This setting in #WASMTIME_OPT_LEVEL_SPEED by default. - */ -WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t) - -/** - * \brief Configures the profiling strategy used for JIT code. - * - * This setting in #WASMTIME_PROFILING_STRATEGY_NONE by default. - */ -WASMTIME_CONFIG_PROP(wasmtime_error_t*, profiler, wasmtime_profiling_strategy_t) - -/** - * \brief Configures the maximum size for memory to be considered "static" - * - * For more information see the Rust documentation at - * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_maximum_size. - */ -WASMTIME_CONFIG_PROP(void, static_memory_maximum_size, uint64_t) - -/** - * \brief Configures the guard region size for "static" memory. - * - * For more information see the Rust documentation at - * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_guard_size. - */ -WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t) - -/** - * \brief Configures the guard region size for "dynamic" memory. - * - * For more information see the Rust documentation at - * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.dynamic_memory_guard_size. - */ -WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) - -/** - * \brief Enables Wasmtime's cache and loads configuration from the specified - * path. - * - * By default the Wasmtime compilation cache is disabled. The configuration path - * here can be `NULL` to use the default settings, and otherwise the argument - * here must be a file on the filesystem with TOML configuration - - * https://bytecodealliance.github.io/wasmtime/cli-cache.html. - * - * An error is returned if the cache configuration could not be loaded or if the - * cache could not be enabled. - */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_t*, const char*); - -/** - * \typedef wasmtime_module_t - * \brief Convenience alias for #wasmtime_module - * - * \struct wasmtime_module - * \brief A compiled Wasmtime module. - * - * This type represents a compiled WebAssembly module. The compiled module is - * ready to be instantiated and can be inspected for imports/exports. It is safe - * to use a #wasmtime_module_t across multiple threads simultaneously. - */ -typedef struct wasmtime_module wasmtime_module_t; - -/* - * \brief Deletes a module. - */ -WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m); - -/** - * \brief Compiles a WebAssembly binary into a #wasmtime_module_t - * - * This function will compile a WebAssembly binary into an owned #wasm_module_t. - * This performs the same as #wasm_module_new except that it returns a - * #wasmtime_error_t type to get richer error information. - * - * On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is - * filled in with a #wasm_module_t. On failure the #wasmtime_error_t is - * non-`NULL` and the `ret` pointer is unmodified. - * - * This function does not take ownership of any of its arguments, but the - * returned error and module are owned by the caller. - */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( - wasm_engine_t *engine, - const wasm_byte_vec_t *binary, - own wasm_module_t **ret -); - -/** - * \brief Validate a WebAssembly binary. - * - * This function will validate the provided byte sequence to determine if it is - * a valid WebAssembly binary within the context of the engine provided. - * - * This function does not take ownership of its arguments but the caller is - * expected to deallocate the returned error if it is non-`NULL`. - * - * If the binary validates then `NULL` is returned, otherwise the error returned - * describes why the binary did not validate. - */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate( - wasm_engine_t *engine, - const wasm_byte_vec_t *binary -); - -/* WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); */ -/* WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out); */ - -/** - * \brief Serializes = - */ -WASM_API_EXTERN void wasmtime_module_serialize(const wasmtime_module_t*, wasm_byte_vec_t* out); -WASM_API_EXTERN wasmtime_error_t* wasmtime_module_deserialize(wasm_engine_t*, const wasm_byte_vec_t*); - -/** - * \brief Perform garbage collection within the given store. - * - * Garbage collects `externref`s that are used within this store. Any - * `externref`s that are discovered to be unreachable by other code or objects - * will have their finalizers run. - * - * The `store` argument must not be NULL. - */ -WASM_API_EXTERN void wasmtime_store_gc(wasm_store_t* store); - -/** - * \typedef wasmtime_linker_t - * \brief Convenience alias for #wasmtime_linker_t - * - * \struct wasmtime_linker_t - * \brief Object used to conveniently link together and instantiate wasm - * modules. - * - * This type corresponds to the `wasmtime::Linker` type in Rust. This - * Wasmtime-specific extension is intended to make it easier to manage a set of - * modules that link together, or to make it easier to link WebAssembly modules - * to WASI. - * - * A #wasmtime_linker_t is a higher level way to instantiate a module than - * #wasm_instance_new since it works at the "string" level of imports rather - * than requiring 1:1 mappings. - * - * \fn void wasmtime_linker_delete(own wasmtime_linker_t *); - * \brief Deletes a linker. - */ -WASMTIME_DECLARE_OWN(linker) - -/** - * \brief Creates a new linker which will link together objects in the specified - * store. - * - * This function does not take ownership of the store argument, and the caller - * is expected to delete the returned linker. - */ -WASM_API_EXTERN own wasmtime_linker_t* wasmtime_linker_new(wasm_store_t* store); - -/** - * \brief Configures whether this linker allows later definitions to shadow - * previous definitions. - * - * By default this setting is `false`. - */ -WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, bool allow_shadowing); - -/** - * \brief Defines a new item in this linker. - * - * \param linker the linker the name is being defined in. - * \param module the module name the item is defined under. - * \param name the field name the item is defined under - * \param item the item that is being defined in this linker. - * - * \return On success `NULL` is returned, otherwise an error is returned which - * describes why the definition failed. - * - * For more information about name resolution consult the [Rust - * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_linker_define( - wasmtime_linker_t *linker, - const wasm_name_t *module, - const wasm_name_t *name, - const wasm_extern_t *item -); - -/** - * \brief Defines a WASI instance in this linker. - * - * \param linker the linker the name is being defined in. - * \param instance a previously-created WASI instance. - * - * \return On success `NULL` is returned, otherwise an error is returned which - * describes why the definition failed. - * - * For more information about name resolution consult the [Rust - * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_linker_define_wasi( - wasmtime_linker_t *linker, - const wasi_instance_t *instance -); - -/** - * \brief Defines an instance under the specified name in this linker. - * - * \param linker the linker the name is being defined in. - * \param name the module name to define `instance` under. - * \param instance a previously-created instance. - * - * \return On success `NULL` is returned, otherwise an error is returned which - * describes why the definition failed. - * - * This function will take all of the exports of the `instance` provided and - * defined them under a module called `name` with a field name as the export's - * own name. - * - * For more information about name resolution consult the [Rust - * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_linker_define_instance( - wasmtime_linker_t *linker, - const wasm_name_t *name, - const wasm_instance_t *instance -); - -/** - * \brief Instantiates a #wasm_module_t with the items defined in this linker. - * - * \param linker the linker used to instantiate the provided module. - * \param module the module that is being instantiated. - * \param instance the returned instance, if successful. - * \param trap a trap returned, if the start function traps. - * - * \return One of three things can happen as a result of this function. First - * the module could be successfully instantiated and returned through - * `instance`, meaning the return value and `trap` are both set to `NULL`. - * Second the start function may trap, meaning the return value and `instance` - * are set to `NULL` and `trap` describes the trap that happens. Finally - * instantiation may fail for another reason, in which case an error is returned - * and `trap` and `instance` are set to `NULL`. - * - * This function will attempt to satisfy all of the imports of the `module` - * provided with items previously defined in this linker. If any name isn't - * defined in the linker than an error is returned. (or if the previously - * defined item is of the wrong type). - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_linker_instantiate( - const wasmtime_linker_t *linker, - const wasm_module_t *module, - own wasm_instance_t **instance, - own wasm_trap_t **trap -); - -/** - * \brief Defines automatic instantiations of a #wasm_module_t in this linker. - * - * \param linker the linker the module is being added to - * \param name the name of the module within the linker - * \param module the module that's being instantiated - * - * \return An error if the module could not be instantiated or added or `NULL` - * on success. - * - * This function automatically handles [Commands and - * Reactors](https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi) - * instantiation and initialization. - * - * For more information see the [Rust - * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.module). - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_linker_module( - const wasmtime_linker_t *linker, - const wasm_name_t *name, - const wasm_module_t *module -); - -/** - * \brief Acquires the "default export" of the named module in this linker. - * - * \param linker the linker to load from - * \param name the name of the module to get the default export for - * \param func where to store the extracted default function. - * - * \return An error is returned if the default export could not be found, or - * `NULL` is returned and `func` is filled in otherwise. - * - * For more information see the [Rust - * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.get_default). - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_linker_get_default( - const wasmtime_linker_t *linker, - const wasm_name_t *name, - own wasm_func_t **func -); - -/** - * \brief Loads an item by name from this linker. - * - * \param linker the linker to load from - * \param module the name of the module to get - * \param name the name of the field to get - * \param item where to store the extracted item - * - * \return An error is returned if the item isn't defined or has more than one - * definition, or `NULL` is returned and `item` is filled in otherwise. - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_linker_get_one_by_name( - const wasmtime_linker_t *linker, - const wasm_name_t *module, - const wasm_name_t *name, - own wasm_extern_t **item -); - -/** - * \brief Structure used to learn about the caller of a host-defined function. - * - * This structure is the first argument of #wasmtime_func_callback_t and - * wasmtime_func_callback_with_env_t. The main purpose of this structure is for - * building a WASI-like API which can inspect the memory of the caller, - * regardless of the caller. - * - * This is intended to be a temporary API extension until interface types have - * become more prevalent. This is not intended to be supported until the end of - * time, but it will be supported so long as WASI requires it. - */ -typedef struct wasmtime_caller_t wasmtime_caller_t; - -/** - * \brief Callback signature for #wasmtime_func_new. - * - * This function is the same as #wasm_func_callback_t except that its first - * argument is a #wasmtime_caller_t which allows learning information about the - * caller. - */ -typedef own wasm_trap_t* (*wasmtime_func_callback_t)(const wasmtime_caller_t* caller, const wasm_val_vec_t *args, wasm_val_vec_t *results); - -/** - * \brief Callback signature for #wasmtime_func_new_with_env. - * - * This function is the same as #wasm_func_callback_with_env_t except that its - * first argument is a #wasmtime_caller_t which allows learning information - * about the caller. - */ -typedef own wasm_trap_t* (*wasmtime_func_callback_with_env_t)(const wasmtime_caller_t* caller, void* env, const wasm_val_vec_t *args, wasm_val_vec_t *results); - -/** - * \brief Creates a new host-defined function. - * - * This function is the same as #wasm_func_new, except the callback has the type - * signature #wasmtime_func_callback_t which gives a #wasmtime_caller_t as its - * first argument. - */ -WASM_API_EXTERN own wasm_func_t* wasmtime_func_new(wasm_store_t*, const wasm_functype_t*, wasmtime_func_callback_t callback); - -/** - * \brief Creates a new host-defined function. - * - * This function is the same as #wasm_func_new_with_env, except the callback - * has the type signature #wasmtime_func_callback_with_env_t which gives a - * #wasmtime_caller_t as its first argument. - */ -WASM_API_EXTERN own wasm_func_t* wasmtime_func_new_with_env( - wasm_store_t* store, - const wasm_functype_t* type, - wasmtime_func_callback_with_env_t callback, - void* env, - void (*finalizer)(void*) + wasm_byte_vec_t *ret ); -/** - * \brief Creates a new `funcref` value referencing `func`. - * - * Create a `funcref` value that references `func` and writes it to `funcrefp`. - * - * Gives ownership fo the `funcref` value written to `funcrefp`. - * - * Both `func` and `funcrefp` must not be NULL. - */ -WASM_API_EXTERN void wasmtime_func_as_funcref(const wasm_func_t* func, wasm_val_t* funcrefp); - -/** - * \brief Get the `wasm_func_t*` referenced by the given `funcref` value. - * - * Gets an owning handle to the `wasm_func_t*` that the given `funcref` value is - * referencing. Returns NULL if the value is not a `funcref`, or if the value is - * a null function reference. - * - * The `val` pointer must not be NULL. - */ -WASM_API_EXTERN own wasm_func_t* wasmtime_funcref_as_func(const wasm_val_t* val); - -/** - * \brief Loads a #wasm_extern_t from the caller's context - * - * This function will attempt to look up the export named `name` on the caller - * instance provided. If it is found then the #wasm_extern_t for that is - * returned, otherwise `NULL` is returned. - * - * Note that this only works for exported memories right now for WASI - * compatibility. - */ -WASM_API_EXTERN own wasm_extern_t* wasmtime_caller_export_get(const wasmtime_caller_t* caller, const wasm_name_t* name); - -/** - * \typedef wasmtime_interrupt_handle_t - * \brief Convenience alias for #wasmtime_interrupt_handle_t - * - * \struct wasmtime_interrupt_handle_t - * \brief A handle used to interrupt executing WebAssembly code. - * - * This structure is an opaque handle that represents a handle to a store. This - * handle can be used to remotely (from another thread) interrupt currently - * executing WebAssembly code. - * - * This structure is safe to share from multiple threads. - * - * \fn void wasmtime_interrupt_handle_delete(own wasmtime_interrupt_handle_t *); - * \brief Deletes an interrupt handle. - */ -WASMTIME_DECLARE_OWN(interrupt_handle) - -/** - * \brief Creates a new interrupt handle to interrupt executing WebAssembly from - * the provided store. - * - * There are a number of caveats about how interrupt is handled in Wasmtime. For - * more information see the [Rust - * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Store.html#method.interrupt_handle). - * - * This function returns `NULL` if the store's configuration does not have - * interrupts enabled. See #wasmtime_config_interruptable_set. - */ -WASM_API_EXTERN own wasmtime_interrupt_handle_t *wasmtime_interrupt_handle_new(wasm_store_t *store); - -/** - * \brief Adds fuel to this Store for wasm to consume while executing. - * - * For this method to work fuel consumption must be enabled via - * #wasmtime_config_consume_fuel_set. By default a Store starts with 0 fuel - * for wasm to execute with (meaning it will immediately trap). - * This function must be called for the store to have - * some fuel to allow WebAssembly to execute. - * - * Note that at this time when fuel is entirely consumed it will cause - * wasm to trap. More usages of fuel are planned for the future. - * - * If fuel is not enabled within this store then an error is returned. If fuel - * is successfully added then NULL is returned. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_store_add_fuel(wasm_store_t *store, uint64_t fuel); - -/** - * \brief Returns the amount of fuel consumed by this store's execution so far. - * - * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set - * then this function will return false. Otherwise true is returned and the - * fuel parameter is filled in with fuel consuemd so far. - * - * Also note that fuel, if enabled, must be originally configured via - * #wasmtime_store_add_fuel. - */ -WASM_API_EXTERN bool wasmtime_store_fuel_consumed(wasm_store_t *store, uint64_t *fuel); - -/** - * \brief Requests that WebAssembly code running in the store attached to this - * interrupt handle is interrupted. - * - * For more information about interrupts see #wasmtime_interrupt_handle_new. - * - * Note that this is safe to call from any thread. - */ -WASM_API_EXTERN void wasmtime_interrupt_handle_interrupt(wasmtime_interrupt_handle_t *handle); - -/** - * \brief Attempts to extract a WASI-specific exit status from this trap. - * - * Returns `true` if the trap is a WASI "exit" trap and has a return status. If - * `true` is returned then the exit status is returned through the `status` - * pointer. If `false` is returned then this is not a wasi exit trap. - */ -WASM_API_EXTERN bool wasmtime_trap_exit_status(const wasm_trap_t*, int *status); - -/** - * \brief Returns a human-readable name for this frame's function. - * - * This function will attempt to load a human-readable name for function this - * frame points to. This function may return `NULL`. - * - * The lifetime of the returned name is the same as the #wasm_frame_t itself. - */ -WASM_API_EXTERN const wasm_name_t *wasmtime_frame_func_name(const wasm_frame_t*); - -/** - * \brief Returns a human-readable name for this frame's module. - * - * This function will attempt to load a human-readable name for module this - * frame points to. This function may return `NULL`. - * - * The lifetime of the returned name is the same as the #wasm_frame_t itself. - */ -WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t*); - -/** - * \brief Call a WebAssembly function. - * - * This function is similar to #wasm_func_call, but with a few tweaks: - * - * * An error *and* a trap can be returned - * * Errors are returned if `args` have the wrong types, if the args/results - * arrays have the wrong lengths, or if values come from the wrong store. - * - * There are three possible return states from this function: - * - * 1. The returned error is non-null. This means `results` - * wasn't written to and `trap` will have `NULL` written to it. This state - * means that programmer error happened when calling the function (e.g. the - * size of the args/results were wrong) - * 2. The trap pointer is filled in. This means the returned error is `NULL` and - * `results` was not written to. This state means that the function was - * executing but hit a wasm trap while executing. - * 3. The error and trap returned are both `NULL` and `results` are written to. - * This means that the function call worked and the specified results were - * produced. - * - * The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be - * `NULL` if the corresponding length is zero. - * - * Does not take ownership of `wasm_val_t` arguments. Gives ownership of - * `wasm_val_t` results. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_func_call( - wasm_func_t *func, - const wasm_val_vec_t *args, - wasm_val_vec_t *results, - own wasm_trap_t **trap -); - -/** - * \brief Creates a new global value. - * - * Similar to #wasm_global_new, but with a few tweaks: - * - * * An error is returned instead of #wasm_global_t, which is taken as an - * out-parameter - * * An error happens when the `type` specified does not match the type of the - * value `val`, or if it comes from a different store than `store`. - * - * This function does not take ownership of any of its arguments but returned - * values are owned by the caller. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_global_new( - wasm_store_t *store, - const wasm_globaltype_t *type, - const wasm_val_t *val, - own wasm_global_t **ret -); - -/** - * \brief Sets a global to a new value. - * - * This function is the same as #wasm_global_set, except in the case of an error - * a #wasmtime_error_t is returned. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_global_set( - wasm_global_t *global, - const wasm_val_t *val -); - -/** - * \brief Wasmtime-specific function to instantiate a module. - * - * This function is similar to #wasm_instance_new, but with a few tweaks: - * - * * An error message can be returned from this function. - * * The `trap` pointer is required to not be NULL. - * - * The states of return values from this function are similar to - * #wasmtime_func_call where an error can be returned meaning something like a - * link error in this context. A trap can be returned (meaning no error or - * instance is returned), or an instance can be returned (meaning no error or - * trap is returned). - * - * This function does not take ownership of any of its arguments, but all return - * values are owned by the caller. - * - * See #wasm_instance_new for information about how to fill in the `imports` - * array. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_instance_new( - wasm_store_t *store, - const wasm_module_t *module, - const wasm_extern_vec_t* imports, - own wasm_instance_t **instance, - own wasm_trap_t **trap -); - - -/** - * \brief Creates a new host-defined wasm table. - * - * This function is the same as #wasm_table_new except that it's specialized for - * funcref tables by taking a `wasm_func_t` initialization value. Additionally - * it returns errors via #wasmtime_error_t. - * - * This function does not take ownership of any of its parameters, but yields - * ownership of returned values (the table and error). - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_funcref_table_new( - wasm_store_t *store, - const wasm_tabletype_t *element_ty, - wasm_func_t *init, - own wasm_table_t **table -); - -/** - * \brief Gets a value in a table. - * - * This function is the same as #wasm_table_get except that it's specialized for - * funcref tables by returning a `wasm_func_t` value. Additionally a `bool` - * return value indicates whether the `index` provided was in bounds. - * - * This function does not take ownership of any of its parameters, but yields - * ownership of returned #wasm_func_t. - */ -WASM_API_EXTERN bool wasmtime_funcref_table_get( - const wasm_table_t *table, - wasm_table_size_t index, - own wasm_func_t **func -); - -/** - * \brief Sets a value in a table. - * - * This function is similar to #wasm_table_set, but has a few differences: - * - * * An error is returned through #wasmtime_error_t describing erroneous - * situations. - * * The value being set is specialized to #wasm_func_t. - * - * This function does not take ownership of any of its parameters, but yields - * ownership of returned #wasmtime_error_t. - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_funcref_table_set( - wasm_table_t *table, - wasm_table_size_t index, - const wasm_func_t *value -); - -/** - * \brief Grows a table. - * - * This function is similar to #wasm_table_grow, but has a few differences: - * - * * An error is returned through #wasmtime_error_t describing erroneous - * situations. - * * The initialization value is specialized to #wasm_func_t. - * * The previous size of the table is returned through `prev_size`. - * - * This function does not take ownership of any of its parameters, but yields - * ownership of returned #wasmtime_error_t. - */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_funcref_table_grow( - wasm_table_t *table, - wasm_table_size_t delta, - const wasm_func_t *init, - wasm_table_size_t *prev_size -); - -/** - * \brief Create a new `externref` value. - * - * Creates a new `externref` value wrapping the provided data, and writes it to - * `valp`. - * - * This function does not take an associated finalizer to clean up the data when - * the reference is reclaimed. If you need a finalizer to clean up the data, - * then use #wasmtime_externref_new_with_finalizer. - * - * Gives ownership of the newly created `externref` value. - */ -WASM_API_EXTERN void wasmtime_externref_new(own void *data, wasm_val_t *valp); - -/** - * \brief A finalizer for an `externref`'s wrapped data. - * - * A finalizer callback to clean up an `externref`'s wrapped data after the - * `externref` has been reclaimed. This is an opportunity to run destructors, - * free dynamically allocated memory, close file handles, etc. - */ -typedef void (*wasmtime_externref_finalizer_t)(void*); - -/** - * \brief Create a new `externref` value with a finalizer. - * - * Creates a new `externref` value wrapping the provided data, and writes it to - * `valp`. - * - * When the reference is reclaimed, the wrapped data is cleaned up with the - * provided finalizer. If you do not need to clean up the wrapped data, then use - * #wasmtime_externref_new. - * - * Gives ownership of the newly created `externref` value. - */ -WASM_API_EXTERN void wasmtime_externref_new_with_finalizer( - own void *data, - wasmtime_externref_finalizer_t finalizer, - wasm_val_t *valp -); - -/** - * \brief Get an `externref`'s wrapped data - * - * If the given value is a reference to a non-null `externref`, writes the - * wrapped data that was passed into #wasmtime_externref_new or - * #wasmtime_externref_new_with_finalizer when creating the given `externref` to - * `datap`, and returns `true`. - * - * If the value is a reference to a null `externref`, writes `NULL` to `datap` - * and returns `true`. - * - * If the given value is not an `externref`, returns `false` and leaves `datap` - * unmodified. - * - * Does not take ownership of `val`. Does not give up ownership of the `void*` - * data written to `datap`. - * - * Both `val` and `datap` must not be `NULL`. - */ -WASM_API_EXTERN bool wasmtime_externref_data(wasm_val_t* val, void** datap); - -/** - * \brief This function serializes compiled module artifacts - * as blob data. - * - * \param module the module - * \param ret if the conversion is successful, this byte vector is filled in with - * the serialized compiled module. - * - * \return a non-null error if parsing fails, or returns `NULL`. If parsing - * fails then `ret` isn't touched. - * - * This function does not take ownership of `module`, and the caller is - * expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. - */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_module_serialize( - wasm_module_t* module, - own wasm_byte_vec_t *ret -); - -/** - * \brief Build a module from serialized data. - * - * This function does not take ownership of any of its arguments, but the - * returned error and module are owned by the caller. - * - * This function is not safe to receive arbitrary user input. See the Rust - * documentation for more information on what inputs are safe to pass in here - * (e.g. only that of #wasmtime_module_serialize) - */ -WASM_API_EXTERN own wasmtime_error_t *wasmtime_module_deserialize( - wasm_engine_t *engine, - const wasm_byte_vec_t *serialized, - own wasm_module_t **ret -); - -/** - * \struct wasm_instancetype_t - * \brief An opaque object representing the type of a function. - * - * \typedef wasm_instancetype_t - * \brief Convenience alias for #wasm_instancetype_t - * - * \struct wasm_instancetype_vec_t - * \brief A list of #wasm_instancetype_t values. - * - * \var wasm_instancetype_vec_t::size - * \brief Length of this vector. - * - * \var wasm_instancetype_vec_t::data - * \brief Pointer to the base of this vector - * - * \typedef wasm_instancetype_vec_t - * \brief Convenience alias for #wasm_instancetype_vec_t - * - * \fn void wasm_instancetype_delete(own wasm_instancetype_t *); - * \brief Deletes a type. - * - * \fn void wasm_instancetype_vec_new_empty(own wasm_instancetype_vec_t *out); - * \brief Creates an empty vector. - * - * See #wasm_byte_vec_new_empty for more information. - * - * \fn void wasm_instancetype_vec_new_uninitialized(own wasm_instancetype_vec_t *out, size_t); - * \brief Creates a vector with the given capacity. - * - * See #wasm_byte_vec_new_uninitialized for more information. - * - * \fn void wasm_instancetype_vec_new(own wasm_instancetype_vec_t *out, size_t, own wasm_instancetype_t *const[]); - * \brief Creates a vector with the provided contents. - * - * See #wasm_byte_vec_new for more information. - * - * \fn void wasm_instancetype_vec_copy(own wasm_instancetype_vec_t *out, const wasm_instancetype_vec_t *) - * \brief Copies one vector to another - * - * See #wasm_byte_vec_copy for more information. - * - * \fn void wasm_instancetype_vec_delete(own wasm_instancetype_vec_t *out) - * \brief Deallocates memory for a vector. - * - * See #wasm_byte_vec_delete for more information. - * - * \fn own wasm_instancetype_t* wasm_instancetype_copy(const wasm_instancetype_t *) - * \brief Creates a new value which matches the provided one. - * - * The caller is responsible for deleting the returned value. - */ -WASM_DECLARE_TYPE(instancetype) - -/** - * \brief Returns the list of exports that this instance type provides. - * - * This function does not take ownership of the provided instance type but - * ownership of `out` is passed to the caller. Note that `out` is treated as - * uninitialized when passed to this function. - */ -WASM_API_EXTERN void wasm_instancetype_exports(const wasm_instancetype_t*, own wasm_exporttype_vec_t* out); - -/** - * \brief Converts a #wasm_instancetype_t to a #wasm_externtype_t - * - * The returned value is owned by the #wasm_instancetype_t argument and should not - * be deleted. - */ -WASM_API_EXTERN wasm_externtype_t* wasm_instancetype_as_externtype(wasm_instancetype_t*); - -/** - * \brief Attempts to convert a #wasm_externtype_t to a #wasm_instancetype_t - * - * The returned value is owned by the #wasm_instancetype_t argument and should not - * be deleted. Returns `NULL` if the provided argument is not a - * #wasm_instancetype_t. - */ -WASM_API_EXTERN wasm_instancetype_t* wasm_externtype_as_instancetype(wasm_externtype_t*); - -/** - * \brief Converts a #wasm_instancetype_t to a #wasm_externtype_t - * - * The returned value is owned by the #wasm_instancetype_t argument and should not - * be deleted. - */ -WASM_API_EXTERN const wasm_externtype_t* wasm_instancetype_as_externtype_const(const wasm_instancetype_t*); - -/** - * \brief Attempts to convert a #wasm_externtype_t to a #wasm_instancetype_t - * - * The returned value is owned by the #wasm_instancetype_t argument and should not - * be deleted. Returns `NULL` if the provided argument is not a - * #wasm_instancetype_t. - */ -WASM_API_EXTERN const wasm_instancetype_t* wasm_externtype_as_instancetype_const(const wasm_externtype_t*); - -/** - * \struct wasm_moduletype_t - * \brief An opaque object representing the type of a function. - * - * \typedef wasm_moduletype_t - * \brief Convenience alias for #wasm_moduletype_t - * - * \struct wasm_moduletype_vec_t - * \brief A list of #wasm_moduletype_t values. - * - * \var wasm_moduletype_vec_t::size - * \brief Length of this vector. - * - * \var wasm_moduletype_vec_t::data - * \brief Pointer to the base of this vector - * - * \typedef wasm_moduletype_vec_t - * \brief Convenience alias for #wasm_moduletype_vec_t - * - * \fn void wasm_moduletype_delete(own wasm_moduletype_t *); - * \brief Deletes a type. - * - * \fn void wasm_moduletype_vec_new_empty(own wasm_moduletype_vec_t *out); - * \brief Creates an empty vector. - * - * See #wasm_byte_vec_new_empty for more information. - * - * \fn void wasm_moduletype_vec_new_uninitialized(own wasm_moduletype_vec_t *out, size_t); - * \brief Creates a vector with the given capacity. - * - * See #wasm_byte_vec_new_uninitialized for more information. - * - * \fn void wasm_moduletype_vec_new(own wasm_moduletype_vec_t *out, size_t, own wasm_moduletype_t *const[]); - * \brief Creates a vector with the provided contents. - * - * See #wasm_byte_vec_new for more information. - * - * \fn void wasm_moduletype_vec_copy(own wasm_moduletype_vec_t *out, const wasm_moduletype_vec_t *) - * \brief Copies one vector to another - * - * See #wasm_byte_vec_copy for more information. - * - * \fn void wasm_moduletype_vec_delete(own wasm_moduletype_vec_t *out) - * \brief Deallocates memory for a vector. - * - * See #wasm_byte_vec_delete for more information. - * - * \fn own wasm_moduletype_t* wasm_moduletype_copy(const wasm_moduletype_t *) - * \brief Creates a new value which matches the provided one. - * - * The caller is responsible for deleting the returned value. - */ -WASM_DECLARE_TYPE(moduletype) - -/** - * \brief Returns the list of imports that this module type requires. - * - * This function does not take ownership of the provided module type but - * ownership of `out` is passed to the caller. Note that `out` is treated as - * uninitialized when passed to this function. - */ -WASM_API_EXTERN void wasm_moduletype_imports(const wasm_moduletype_t*, own wasm_importtype_vec_t* out); - -/** - * \brief Returns the list of exports that this module type provides. - * - * This function does not take ownership of the provided module type but - * ownership of `out` is passed to the caller. Note that `out` is treated as - * uninitialized when passed to this function. - */ -WASM_API_EXTERN void wasm_moduletype_exports(const wasm_moduletype_t*, own wasm_exporttype_vec_t* out); - -/** - * \brief Converts a #wasm_moduletype_t to a #wasm_externtype_t - * - * The returned value is owned by the #wasm_moduletype_t argument and should not - * be deleted. - */ -WASM_API_EXTERN wasm_externtype_t* wasm_moduletype_as_externtype(wasm_moduletype_t*); - -/** - * \brief Attempts to convert a #wasm_externtype_t to a #wasm_moduletype_t - * - * The returned value is owned by the #wasm_moduletype_t argument and should not - * be deleted. Returns `NULL` if the provided argument is not a - * #wasm_moduletype_t. - */ -WASM_API_EXTERN wasm_moduletype_t* wasm_externtype_as_moduletype(wasm_externtype_t*); - -/** - * \brief Converts a #wasm_moduletype_t to a #wasm_externtype_t - * - * The returned value is owned by the #wasm_moduletype_t argument and should not - * be deleted. - */ -WASM_API_EXTERN const wasm_externtype_t* wasm_moduletype_as_externtype_const(const wasm_moduletype_t*); - -/** - * \brief Attempts to convert a #wasm_externtype_t to a #wasm_moduletype_t - * - * The returned value is owned by the #wasm_moduletype_t argument and should not - * be deleted. Returns `NULL` if the provided argument is not a - * #wasm_moduletype_t. - */ -WASM_API_EXTERN const wasm_moduletype_t* wasm_externtype_as_moduletype_const(const wasm_externtype_t*); - -/** - * \brief Converts a #wasm_module_t to #wasm_extern_t. - * - * The returned #wasm_extern_t is owned by the #wasm_module_t argument. Callers - * should not delete the returned value, and it only lives as long as the - * #wasm_module_t argument. - */ -WASM_API_EXTERN wasm_extern_t* wasm_module_as_extern(wasm_module_t*); - -/** - * \brief Converts a #wasm_extern_t to #wasm_module_t. - * - * The returned #wasm_module_t is owned by the #wasm_extern_t argument. Callers - * should not delete the returned value, and it only lives as long as the - * #wasm_extern_t argument. - * - * If the #wasm_extern_t argument isn't a #wasm_module_t then `NULL` is returned. - */ -WASM_API_EXTERN wasm_module_t* wasm_extern_as_module(wasm_extern_t*); - -/** - * \brief Converts a #wasm_extern_t to #wasm_instance_t. - * - * The returned #wasm_instance_t is owned by the #wasm_extern_t argument. Callers - * should not delete the returned value, and it only lives as long as the - * #wasm_extern_t argument. - */ -WASM_API_EXTERN const wasm_module_t* wasm_extern_as_module_const(const wasm_extern_t*); - -/** - * \brief Converts a #wasm_instance_t to #wasm_extern_t. - * - * The returned #wasm_extern_t is owned by the #wasm_instance_t argument. Callers - * should not delete the returned value, and it only lives as long as the - * #wasm_instance_t argument. - */ -WASM_API_EXTERN wasm_extern_t* wasm_instance_as_extern(wasm_instance_t*); - -/** - * \brief Converts a #wasm_extern_t to #wasm_instance_t. - * - * The returned #wasm_instance_t is owned by the #wasm_extern_t argument. Callers - * should not delete the returned value, and it only lives as long as the - * #wasm_extern_t argument. - * - * If the #wasm_extern_t argument isn't a #wasm_instance_t then `NULL` is returned. - */ -WASM_API_EXTERN wasm_instance_t* wasm_extern_as_instance(wasm_extern_t*); - -/** - * \brief Converts a #wasm_extern_t to #wasm_instance_t. - * - * The returned #wasm_instance_t is owned by the #wasm_extern_t argument. Callers - * should not delete the returned value, and it only lives as long as the - * #wasm_extern_t argument. - */ -WASM_API_EXTERN const wasm_instance_t* wasm_extern_as_instance_const(const wasm_extern_t*); - -/** - * \brief Returns the type of this instance. - * - * The returned #wasm_instancetype_t is expected to be deallocated by the caller. - */ -WASM_API_EXTERN own wasm_instancetype_t* wasm_instance_type(const wasm_instance_t*); - -/** - * \brief Returns the type of this module. - * - * The returned #wasm_moduletype_t is expected to be deallocated by the caller. - */ -WASM_API_EXTERN own wasm_moduletype_t* wasm_module_type(const wasm_module_t*); - -/** - * \brief Value of #wasm_externkind_enum corresponding to a wasm module. - */ -#define WASM_EXTERN_MODULE 4 - -/** - * \brief Value of #wasm_externkind_enum corresponding to a wasm instance. - */ -#define WASM_EXTERN_INSTANCE 5 - -#undef own - #ifdef __cplusplus } // extern "C" #endif diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h new file mode 100644 index 000000000000..538454052781 --- /dev/null +++ b/crates/c-api/include/wasmtime/config.h @@ -0,0 +1,258 @@ +#ifndef WASMTIME_CONFIG_H +#define WASMTIME_CONFIG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Specifier for how Wasmtime will compile code, values are in + * #wasmtime_strategy_enum + */ +typedef uint8_t wasmtime_strategy_t; + +/** + * \brief Different ways that Wasmtime can compile WebAssembly + * + * The default value is #WASMTIME_STRATEGY_AUTO. + */ +enum wasmtime_strategy_enum { // Strategy + /// Wasmtime will automatically determine whether to use Cranelift or + /// Lightbeam, and currently it will always pick Cranelift. This default may + /// change over time though. + WASMTIME_STRATEGY_AUTO, + + /// Indicates that Cranelift will unconditionally use Cranelift to compile + /// WebAssembly code. + WASMTIME_STRATEGY_CRANELIFT, + + /// Indicates that Cranelift will unconditionally use Lightbeam to compile + /// WebAssembly code. Note that Lightbeam isn't always enabled at compile + /// time, and if that's the case an error will be returned. + WASMTIME_STRATEGY_LIGHTBEAM, +}; + +/** + * \brief Specifier of what optimization level to use for generated JIT code. + * + * See #wasmtime_opt_level_enum for possible values. + */ +typedef uint8_t wasmtime_opt_level_t; + +/** + * \brief Different ways Wasmtime can optimize generated code. + * + * The default value is #WASMTIME_OPT_LEVEL_SPEED. + */ +enum wasmtime_opt_level_enum { // OptLevel + /// Generated code will not be optimized at all. + WASMTIME_OPT_LEVEL_NONE, + /// Generated code will be optimized purely for speed. + WASMTIME_OPT_LEVEL_SPEED, + /// Generated code will be optimized, but some speed optimizations are + /// disabled if they cause the generated code to be significantly larger. + WASMTIME_OPT_LEVEL_SPEED_AND_SIZE, +}; + +/** + * \brief Different ways wasmtime can enable profiling JIT code. + * + * See #wasmtime_profiling_strategy_enum for possible values. + */ +typedef uint8_t wasmtime_profiling_strategy_t; + +/** + * \brief Different ways to profile JIT code. + * + * The default is #WASMTIME_PROFILING_STRATEGY_NONE. + */ +enum wasmtime_profiling_strategy_enum { // ProfilingStrategy + /// No profiling is enabled at runtime. + WASMTIME_PROFILING_STRATEGY_NONE, + /// Linux's "jitdump" support in `perf` is enabled and when Wasmtime is run + /// under `perf` necessary calls will be made to profile generated JIT code. + WASMTIME_PROFILING_STRATEGY_JITDUMP, + /// Support for VTune will be enabled and the VTune runtime will be informed, + /// at runtime, about JIT code. + /// + /// Note that this isn't always enabled at build time. + WASMTIME_PROFILING_STRATEGY_VTUNE, +}; + +#define WASMTIME_CONFIG_PROP(ret, name, ty) \ + WASM_API_EXTERN ret wasmtime_config_##name##_set(wasm_config_t*, ty); + +/** + * \brief Configures whether DWARF debug information is constructed at runtime + * to describe JIT code. + * + * This setting is `false` by default. When enabled it will attempt to inform + * native debuggers about DWARF debugging information for JIT code to more + * easily debug compiled WebAssembly via native debuggers. This can also + * sometimes improve the quality of output when profiling is enabled. + */ +WASMTIME_CONFIG_PROP(void, debug_info, bool) + +/** + * \brief Enables WebAssembly code to be interrupted. + * + * This setting is `false` by default. When enabled it will enable getting an + * interrupt handle via #wasmtime_interrupt_handle_new which can be used to + * interrupt currently-executing WebAssembly code. + */ +WASMTIME_CONFIG_PROP(void, interruptable, bool) + +/** + * \brief Whether or not fuel is enabled for generated code. + * + * This setting is `false` by default. When enabled it will enable fuel counting + * meaning that fuel will be consumed every time a wasm instruction is executed, + * and trap when reaching zero. + */ +WASMTIME_CONFIG_PROP(void, consume_fuel, bool) + +/** + * \brief Configures the maximum stack size, in bytes, that JIT code can use. + * + * This setting is 2MB by default. Configuring this setting will limit the + * amount of native stack space that JIT code can use while it is executing. If + * you're hitting stack overflow you can try making this setting larger, or if + * you'd like to limit wasm programs to less stack you can also configure this. + * + * Note that this setting is not interpreted with 100% precision. Additionally + * the amount of stack space that wasm takes is always relative to the first + * invocation of wasm on the stack, so recursive calls with host frames in the + * middle will all need to fit within this setting. + */ +WASMTIME_CONFIG_PROP(void, max_wasm_stack, size_t) + +/** + * \brief Configures whether the WebAssembly threading proposal is enabled. + * + * This setting is `false` by default. + * + * Note that threads are largely unimplemented in Wasmtime at this time. + */ +WASMTIME_CONFIG_PROP(void, wasm_threads, bool) + +/** + * \brief Configures whether the WebAssembly reference types proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_reference_types, bool) + +/** + * \brief Configures whether the WebAssembly SIMD proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_simd, bool) + +/** + * \brief Configures whether the WebAssembly bulk memory proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_bulk_memory, bool) + +/** + * \brief Configures whether the WebAssembly multi value proposal is + * enabled. + * + * This setting is `true` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_multi_value, bool) + +/** + * \brief Configures whether the WebAssembly module linking proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_module_linking, bool) + +/** + * \brief Configures how JIT code will be compiled. + * + * This setting is #WASMTIME_STRATEGY_AUTO by default. + * + * If the compilation strategy selected could not be enabled then an error is + * returned. + */ +WASMTIME_CONFIG_PROP(wasmtime_error_t*, strategy, wasmtime_strategy_t) + +/** + * \brief Configures whether Cranelift's debug verifier is enabled. + * + * This setting in `false` by default. + * + * When cranelift is used for compilation this enables expensive debug checks + * within Cranelift itself to verify it's correct. + */ +WASMTIME_CONFIG_PROP(void, cranelift_debug_verifier, bool) + +/** + * \brief Configures Cranelift's optimization level for JIT code. + * + * This setting in #WASMTIME_OPT_LEVEL_SPEED by default. + */ +WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t) + +/** + * \brief Configures the profiling strategy used for JIT code. + * + * This setting in #WASMTIME_PROFILING_STRATEGY_NONE by default. + */ +WASMTIME_CONFIG_PROP(wasmtime_error_t*, profiler, wasmtime_profiling_strategy_t) + +/** + * \brief Configures the maximum size for memory to be considered "static" + * + * For more information see the Rust documentation at + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_maximum_size. + */ +WASMTIME_CONFIG_PROP(void, static_memory_maximum_size, uint64_t) + +/** + * \brief Configures the guard region size for "static" memory. + * + * For more information see the Rust documentation at + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_guard_size. + */ +WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t) + +/** + * \brief Configures the guard region size for "dynamic" memory. + * + * For more information see the Rust documentation at + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.dynamic_memory_guard_size. + */ +WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) + +/** + * \brief Enables Wasmtime's cache and loads configuration from the specified + * path. + * + * By default the Wasmtime compilation cache is disabled. The configuration path + * here can be `NULL` to use the default settings, and otherwise the argument + * here must be a file on the filesystem with TOML configuration - + * https://bytecodealliance.github.io/wasmtime/cli-cache.html. + * + * An error is returned if the cache configuration could not be loaded or if the + * cache could not be enabled. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_t*, const char*); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_CONFIG_H + diff --git a/crates/c-api/include/wasmtime/error.h b/crates/c-api/include/wasmtime/error.h new file mode 100644 index 000000000000..87f3415371f8 --- /dev/null +++ b/crates/c-api/include/wasmtime/error.h @@ -0,0 +1,45 @@ +#ifndef WASMTIME_ERROR_H +#define WASMTIME_ERROR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_error_t + * \brief Convenience alias for #wasmtime_error_t + * + * \struct wasmtime_error + * \brief Errors generated by Wasmtime. + * + * This opaque type represents an error that happened as part of one of the + * functions below. Errors primarily have an error message associated with them + * at this time, which you can acquire by calling #wasmtime_error_message. + */ +typedef struct wasmtime_error wasmtime_error_t; + +/* + * \brief Deletes an error. + */ +WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error); + +/** + * \brief Returns the string description of this error. + * + * This will "render" the error to a string and then return the string + * representation of the error to the caller. The `message` argument should be + * uninitialized before this function is called and the caller is responsible + * for deallocating it with #wasm_byte_vec_delete afterwards. + */ +WASM_API_EXTERN void wasmtime_error_message( + const wasmtime_error_t *error, + wasm_name_t *message +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_ERROR_H diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h new file mode 100644 index 000000000000..82ac133923da --- /dev/null +++ b/crates/c-api/include/wasmtime/extern.h @@ -0,0 +1,46 @@ +#ifndef WASMTIME_EXTERN_H +#define WASMTIME_EXTERN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint64_t wasmtime_func_t; +typedef uint64_t wasmtime_table_t; +typedef uint64_t wasmtime_memory_t; +typedef uint64_t wasmtime_instance_t; +typedef uint64_t wasmtime_global_t; + +typedef enum wasmtime_extern_kind { + WASMTIME_EXTERN_FUNC, + WASMTIME_EXTERN_GLOBAL, + WASMTIME_EXTERN_TABLE, + WASMTIME_EXTERN_MEMORY, + WASMTIME_EXTERN_INSTANCE, + WASMTIME_EXTERN_MODULE, +} wasmtime_extern_kind_t; + +typedef union wasmtime_extern_union { + wasmtime_func_t func; + wasmtime_global_t global; + wasmtime_table_t table; + wasmtime_memory_t memory; + wasmtime_instance_t instance; + wasmtime_module_t *module; +} wasmtime_extern_union_t; + +typedef struct wasmtime_extern { + wasmtime_extern_kind_t kind; + wasmtime_extern_union_t of; +} wasmtime_extern_t; + +void wasmtime_extern_delete(wasmtime_extern_t *val); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_EXTERN_H + diff --git a/crates/c-api/include/wasmtime/func.h b/crates/c-api/include/wasmtime/func.h new file mode 100644 index 000000000000..8aa90b0bbdf7 --- /dev/null +++ b/crates/c-api/include/wasmtime/func.h @@ -0,0 +1,127 @@ +#ifndef WASMTIME_FUNC_H +#define WASMTIME_FUNC_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Structure used to learn about the caller of a host-defined function. + * + * This structure is the first argument of #wasmtime_func_callback_t and + * wasmtime_func_callback_with_env_t. The main purpose of this structure is for + * building a WASI-like API which can inspect the memory of the caller, + * regardless of the caller. + * + * This is intended to be a temporary API extension until interface types have + * become more prevalent. This is not intended to be supported until the end of + * time, but it will be supported so long as WASI requires it. + */ +typedef struct wasmtime_caller wasmtime_caller_t; + +/** + * \brief Callback signature for #wasmtime_func_new. + * + * This function is the same as #wasm_func_callback_t except that its first + * argument is a #wasmtime_caller_t which allows learning information about the + * caller. + */ +typedef wasm_trap_t* (*wasmtime_func_callback_t)( + void *env, + const wasmtime_caller_t* caller, + const wasmtime_val_t *args, + size_t nargs, + wasmtime_val_t *results, + size_t nresults); + +/** + * \brief Creates a new host-defined function. + * + * TODO + * + * This function is the same as #wasm_func_new, except the callback has the type + * signature #wasmtime_func_callback_t which gives a #wasmtime_caller_t as its + * first argument. + */ +WASM_API_EXTERN wasm_func_t wasmtime_func_new( + wasmtime_context_t *store, + const wasm_functype_t* type, + wasmtime_func_callback_t callback, + void *env, + void (*finalizer)(void*) +); + +WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( + const wasmtime_context_t *store, + wasmtime_func_t func +); + +/** + * \brief Call a WebAssembly function. + * + * This function is similar to #wasm_func_call, but with a few tweaks: + * + * * An error *and* a trap can be returned + * * Errors are returned if `args` have the wrong types, if the args/results + * arrays have the wrong lengths, or if values come from the wrong store. + * + * There are three possible return states from this function: + * + * 1. The returned error is non-null. This means `results` + * wasn't written to and `trap` will have `NULL` written to it. This state + * means that programmer error happened when calling the function (e.g. the + * size of the args/results were wrong) + * 2. The trap pointer is filled in. This means the returned error is `NULL` and + * `results` was not written to. This state means that the function was + * executing but hit a wasm trap while executing. + * 3. The error and trap returned are both `NULL` and `results` are written to. + * This means that the function call worked and the specified results were + * produced. + * + * The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be + * `NULL` if the corresponding length is zero. + * + * Does not take ownership of `wasm_val_t` arguments. Gives ownership of + * `wasm_val_t` results. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call( + wasmtime_context_t *store, + wasmtime_func_t func, + const wasmtime_val_t *args, + size_t nargs, + wasmtime_val_t *results, + size_t nresults, + wasm_trap_t **trap +); + +/** + * \brief Loads a #wasm_extern_t from the caller's context + * + * This function will attempt to look up the export named `name` on the caller + * instance provided. If it is found then the #wasm_extern_t for that is + * returned, otherwise `NULL` is returned. + * + * Note that this only works for exported memories right now for WASI + * compatibility. + * + * TODO + */ +WASM_API_EXTERN bool wasmtime_caller_export_get( + wasmtime_caller_t *caller, + const char *name, + size_t name_len, + wasmtime_extern_t *item +); + +WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* caller); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_FUNC_H diff --git a/crates/c-api/include/wasmtime/global.h b/crates/c-api/include/wasmtime/global.h new file mode 100644 index 000000000000..5efdc56c34b9 --- /dev/null +++ b/crates/c-api/include/wasmtime/global.h @@ -0,0 +1,60 @@ +#ifndef WASMTIME_GLOBAL_H +#define WASMTIME_GLOBAL_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Creates a new global value. + * + * Similar to #wasm_global_new, but with a few tweaks: + * + * * An error is returned instead of #wasm_global_t, which is taken as an + * out-parameter + * * An error happens when the `type` specified does not match the type of the + * value `val`, or if it comes from a different store than `store`. + * + * This function does not take ownership of any of its arguments but returned + * values are owned by the caller. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new( + wasmtime_context_t *store, + const wasm_globaltype_t *type, + const wasmtime_val_t *val, + wasmtime_global_t *ret +); + +WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( + const wasmtime_context_t *store, + wasmtime_global_t global +); + +WASM_API_EXTERN void wasmtime_global_get( + wasmtime_context_t *store, + wasmtime_global_t global, + wasmtime_val_t *out +); + +/** + * \brief Sets a global to a new value. + * + * This function is the same as #wasm_global_set, except in the case of an error + * a #wasmtime_error_t is returned. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_global_set( + wasmtime_context_t *store, + wasmtime_global_t global, + const wasmtime_val_t *val +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_GLOBAL_H diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h new file mode 100644 index 000000000000..12276dcfb702 --- /dev/null +++ b/crates/c-api/include/wasmtime/instance.h @@ -0,0 +1,100 @@ +#ifndef WASMTIME_INSTANCE_H +#define WASMTIME_INSTANCE_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief An opaque object representing the type of an instance. + */ +typedef struct wasmtime_instancetype wasmtime_instancetype_t; + + +/** + * \brief Returns the list of exports that this instance type provides. + * + * This function does not take ownership of the provided instance type but + * ownership of `out` is passed to the caller. Note that `out` is treated as + * uninitialized when passed to this function. + */ +WASM_API_EXTERN void wasmtime_instancetype_exports(const wasmtime_instancetype_t*, wasm_exporttype_vec_t* out); + +/** + * \brief Converts a #wasmtime_instancetype_t to a #wasm_externtype_t + * + * The returned value is owned by the #wasmtime_instancetype_t argument and should not + * be deleted. + */ +WASM_API_EXTERN wasm_externtype_t* wasmtime_instancetype_as_externtype(wasmtime_instancetype_t*); + +/** + * \brief Attempts to convert a #wasm_externtype_t to a #wasmtime_instancetype_t + * + * The returned value is owned by the #wasmtime_instancetype_t argument and should not + * be deleted. Returns `NULL` if the provided argument is not a + * #wasmtime_instancetype_t. + */ +WASM_API_EXTERN wasmtime_instancetype_t* wasm_externtype_as_instancetype(wasm_externtype_t*); + +/** + * \brief Wasmtime-specific function to instantiate a module. + * + * This function is similar to #wasm_instance_new, but with a few tweaks: + * + * * An error message can be returned from this function. + * * The `trap` pointer is required to not be NULL. + * + * The states of return values from this function are similar to + * #wasmtime_func_call where an error can be returned meaning something like a + * link error in this context. A trap can be returned (meaning no error or + * instance is returned), or an instance can be returned (meaning no error or + * trap is returned). + * + * This function does not take ownership of any of its arguments, but all return + * values are owned by the caller. + * + * See #wasm_instance_new for information about how to fill in the `imports` + * array. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( + wasmtime_context_t *store, + const wasmtime_module_t *module, + const wasmtime_extern_t* imports, + size_t nimports, + wasmtime_instance_t *instance, + wasm_trap_t **trap +); + +WASM_API_EXTERN wasmtime_instancetype_t *wasmtime_instance_type( + const wasmtime_context_t *store, + wasmtime_instance_t instance +); + +WASM_API_EXTERN bool wasmtime_instance_export_get( + wasmtime_context_t *store, + wasmtime_instance_t instance, + char *name, + size_t name_len, + wasmtime_extern_t *item +); + +WASM_API_EXTERN bool wasmtime_instance_export_nth( + wasmtime_context_t *store, + wasmtime_instance_t instance, + size_t index, + char **name, + size_t *name_len, + wasmtime_extern_t *item +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_INSTANCE_H diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h new file mode 100644 index 000000000000..71b9dd1cf0a5 --- /dev/null +++ b/crates/c-api/include/wasmtime/linker.h @@ -0,0 +1,212 @@ +#ifndef WASMTIME_LINKER_H +#define WASMTIME_LINKER_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Object used to conveniently link together and instantiate wasm + * modules. + * + * This type corresponds to the `wasmtime::Linker` type in Rust. This + * Wasmtime-specific extension is intended to make it easier to manage a set of + * modules that link together, or to make it easier to link WebAssembly modules + * to WASI. + * + * A #wasmtime_linker_t is a higher level way to instantiate a module than + * #wasm_instance_new since it works at the "string" level of imports rather + * than requiring 1:1 mappings. + */ +typedef struct wasmtime_linker wasmtime_linker_t; + +/** + * \brief Creates a new linker which will link together objects in the specified + * store. + * + * This function does not take ownership of the store argument, and the caller + * is expected to delete the returned linker. + */ +WASM_API_EXTERN wasmtime_linker_t* wasmtime_linker_new(wasm_engine_t* engine); + +WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker); + +/** + * \brief Configures whether this linker allows later definitions to shadow + * previous definitions. + * + * By default this setting is `false`. + */ +WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, bool allow_shadowing); + +/** + * \brief Defines a new item in this linker. + * + * \param linker the linker the name is being defined in. + * \param module the module name the item is defined under. + * \param name the field name the item is defined under + * \param item the item that is being defined in this linker. + * + * \return On success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + * + * For more information about name resolution consult the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( + wasmtime_linker_t *linker, + const char *module, + size_t module_len, + const char *name, + size_t name_len, + const wasmtime_extern_t *item +); + +/** + * \brief Defines a WASI instance in this linker. + * + * \param linker the linker the name is being defined in. + * \param instance a previously-created WASI instance. + * + * \return On success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + * + * For more information about name resolution consult the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). + */ +/* WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( */ +/* wasmtime_linker_t *linker, */ +/* const wasi_instance_t *instance */ +/* ); */ + +/** + * \brief Defines an instance under the specified name in this linker. + * + * \param linker the linker the name is being defined in. + * \param name the module name to define `instance` under. + * \param instance a previously-created instance. + * + * \return On success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + * + * This function will take all of the exports of the `instance` provided and + * defined them under a module called `name` with a field name as the export's + * own name. + * + * For more information about name resolution consult the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance( + wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *name, + size_t name_len, + wasmtime_instance_t instance +); + +/** + * \brief Instantiates a #wasm_module_t with the items defined in this linker. + * + * \param linker the linker used to instantiate the provided module. + * \param module the module that is being instantiated. + * \param instance the returned instance, if successful. + * \param trap a trap returned, if the start function traps. + * + * \return One of three things can happen as a result of this function. First + * the module could be successfully instantiated and returned through + * `instance`, meaning the return value and `trap` are both set to `NULL`. + * Second the start function may trap, meaning the return value and `instance` + * are set to `NULL` and `trap` describes the trap that happens. Finally + * instantiation may fail for another reason, in which case an error is returned + * and `trap` and `instance` are set to `NULL`. + * + * This function will attempt to satisfy all of the imports of the `module` + * provided with items previously defined in this linker. If any name isn't + * defined in the linker than an error is returned. (or if the previously + * defined item is of the wrong type). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate( + const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const wasmtime_module_t *module, + wasmtime_instance_t *instance, + wasm_trap_t **trap +); + +/** + * \brief Defines automatic instantiations of a #wasm_module_t in this linker. + * + * \param linker the linker the module is being added to + * \param name the name of the module within the linker + * \param module the module that's being instantiated + * + * \return An error if the module could not be instantiated or added or `NULL` + * on success. + * + * This function automatically handles [Commands and + * Reactors](https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi) + * instantiation and initialization. + * + * For more information see the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.module). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module( + wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *name, + size_t name_len, + const wasmtime_module_t *module +); + +/** + * \brief Acquires the "default export" of the named module in this linker. + * + * \param linker the linker to load from + * \param name the name of the module to get the default export for + * \param func where to store the extracted default function. + * + * \return An error is returned if the default export could not be found, or + * `NULL` is returned and `func` is filled in otherwise. + * + * For more information see the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.get_default). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default( + const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *name, + size_t name_len, + wasmtime_func_t *func +); + +/** + * \brief Loads an item by name from this linker. + * + * \param linker the linker to load from + * \param module the name of the module to get + * \param name the name of the field to get + * \param item where to store the extracted item + * + * \return An error is returned if the item isn't defined or has more than one + * definition, or `NULL` is returned and `item` is filled in otherwise. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_one_by_name( + const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *module, + size_t module_len, + const char *name, + size_t name_len, + wasmtime_extern_t *item +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_LINKER_H diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h new file mode 100644 index 000000000000..87674b06d9c5 --- /dev/null +++ b/crates/c-api/include/wasmtime/memory.h @@ -0,0 +1,47 @@ +#ifndef WASMTIME_MEMORY_H +#define WASMTIME_MEMORY_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( + wasmtime_context_t *store, + const wasm_memorytype_t* ty, + wasmtime_memory_t *ret +); + +WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type( + const wasmtime_context_t *store, + wasmtime_memory_t memory +); + +WASM_API_EXTERN uint8_t *wasmtime_memory_data( + const wasmtime_context_t *store, + wasmtime_memory_t memory +); +WASM_API_EXTERN size_t *wasmtime_memory_data_size( + const wasmtime_context_t *store, + wasmtime_memory_t memory +); +WASM_API_EXTERN uint32_t *wasmtime_memory_size( + const wasmtime_context_t *store, + wasmtime_memory_t memory +); +WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow( + wasmtime_context_t *store, + wasmtime_memory_t memory, + uint32_t delta, + uint32_t *prev_size +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_MEMORY_H diff --git a/crates/c-api/include/wasmtime/module.h b/crates/c-api/include/wasmtime/module.h new file mode 100644 index 000000000000..dc7d9c4a927f --- /dev/null +++ b/crates/c-api/include/wasmtime/module.h @@ -0,0 +1,160 @@ +#ifndef WASMTIME_MODULE_H +#define WASMTIME_MODULE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief An opaque object representing the type of a module. + */ +typedef struct wasmtime_moduletype wasmtime_moduletype_t; + +/** + * \brief Deletes a module type. + */ +WASM_API_EXTERN void wasmtime_moduletype_delete(wasmtime_moduletype_t *ty); + +/** + * \brief Returns the list of imports that this module type requires. + * + * This function does not take ownership of the provided module type but + * ownership of `out` is passed to the caller. Note that `out` is treated as + * uninitialized when passed to this function. + */ +WASM_API_EXTERN void wasmtime_moduletype_imports(const wasmtime_moduletype_t*, wasm_importtype_vec_t* out); + +/** + * \brief Returns the list of exports that this module type provides. + * + * This function does not take ownership of the provided module type but + * ownership of `out` is passed to the caller. Note that `out` is treated as + * uninitialized when passed to this function. + */ +WASM_API_EXTERN void wasmtime_moduletype_exports(const wasmtime_moduletype_t*, wasm_exporttype_vec_t* out); + +/** + * \brief Converts a #wasm_moduletype_t to a #wasm_externtype_t + * + * The returned value is owned by the #wasm_moduletype_t argument and should not + * be deleted. + */ +WASM_API_EXTERN wasm_externtype_t* wasmtime_moduletype_as_externtype(wasmtime_moduletype_t*); + +/** + * \brief Attempts to convert a #wasm_externtype_t to a #wasm_moduletype_t + * + * The returned value is owned by the #wasm_moduletype_t argument and should not + * be deleted. Returns `NULL` if the provided argument is not a + * #wasm_moduletype_t. + */ +WASM_API_EXTERN wasmtime_moduletype_t* wasm_externtype_as_moduletype(wasm_externtype_t*); + +/** + * \typedef wasmtime_module_t + * \brief Convenience alias for #wasmtime_module + * + * \struct wasmtime_module + * \brief A compiled Wasmtime module. + * + * This type represents a compiled WebAssembly module. The compiled module is + * ready to be instantiated and can be inspected for imports/exports. It is safe + * to use a #wasmtime_module_t across multiple threads simultaneously. + */ +typedef struct wasmtime_module wasmtime_module_t; + +/** + * \brief Compiles a WebAssembly binary into a #wasmtime_module_t + * + * This function will compile a WebAssembly binary into an owned #wasm_module_t. + * This performs the same as #wasm_module_new except that it returns a + * #wasmtime_error_t type to get richer error information. + * + * On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is + * filled in with a #wasm_module_t. On failure the #wasmtime_error_t is + * non-`NULL` and the `ret` pointer is unmodified. + * + * This function does not take ownership of any of its arguments, but the + * returned error and module are owned by the caller. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( + wasm_engine_t *engine, + const uint8_t *wasm, + size_t wasm_len, + wasmtime_module_t **ret +); + +/* + * \brief Deletes a module. + */ +WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m); + +/** + * \brief Validate a WebAssembly binary. + * + * This function will validate the provided byte sequence to determine if it is + * a valid WebAssembly binary within the context of the engine provided. + * + * This function does not take ownership of its arguments but the caller is + * expected to deallocate the returned error if it is non-`NULL`. + * + * If the binary validates then `NULL` is returned, otherwise the error returned + * describes why the binary did not validate. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate( + wasm_engine_t *engine, + const uint8_t *wasm, + size_t wasm_len +); + +/** + * \brief Returns the type of this module. + * + * The returned #wasmtime_moduletype_t is expected to be deallocated by the + * caller. + */ +WASM_API_EXTERN wasmtime_moduletype_t* wasmtime_module_type(const wasmtime_module_t*); + +/** + * \brief This function serializes compiled module artifacts as blob data. + * + * \param module the module + * \param ret if the conversion is successful, this byte vector is filled in with + * the serialized compiled module. + * + * \return a non-null error if parsing fails, or returns `NULL`. If parsing + * fails then `ret` isn't touched. + * + * This function does not take ownership of `module`, and the caller is + * expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_module_serialize( + wasmtime_module_t* module, + wasm_byte_vec_t *ret +); + +/** + * \brief Build a module from serialized data. + * + * This function does not take ownership of any of its arguments, but the + * returned error and module are owned by the caller. + * + * This function is not safe to receive arbitrary user input. See the Rust + * documentation for more information on what inputs are safe to pass in here + * (e.g. only that of #wasmtime_module_serialize) + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize( + wasm_engine_t *engine, + const uint8_t *bytes, + size_t bytes_len, + wasmtime_module_t **ret +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_MODULE_H diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h new file mode 100644 index 000000000000..835d7fd2af60 --- /dev/null +++ b/crates/c-api/include/wasmtime/store.h @@ -0,0 +1,129 @@ +#ifndef WASMTIME_STORE_H +#define WASMTIME_STORE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_store_t + * \brief Convenience alias for #wasmtime_store_t + * + * \struct wasmtime_store + * \brief TODO + * + * TODO + */ +typedef struct wasmtime_store wasmtime_store_t; + +typedef struct wasmtime_context wasmtime_context_t; + +WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new( + wasm_engine_t *engine, + void *data, + void(*finalizer)(void*) +); + +WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *store); + +/* + * \brief Deletes a store. + */ +WASM_API_EXTERN void wasmtime_store_delete(wasmtime_store_t *store); + +WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* context); +WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void *data); + +/** + * \brief Perform garbage collection within the given context. + * + * Garbage collects `externref`s that are used within this store. Any + * `externref`s that are discovered to be unreachable by other code or objects + * will have their finalizers run. + * + * The `store` argument must not be NULL. + */ +WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context); + +/** + * \brief Adds fuel to this context's store for wasm to consume while executing. + * + * For this method to work fuel consumption must be enabled via + * #wasmtime_config_consume_fuel_set. By default a store starts with 0 fuel + * for wasm to execute with (meaning it will immediately trap). + * This function must be called for the store to have + * some fuel to allow WebAssembly to execute. + * + * Note that at this time when fuel is entirely consumed it will cause + * wasm to trap. More usages of fuel are planned for the future. + * + * If fuel is not enabled within this store then an error is returned. If fuel + * is successfully added then NULL is returned. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t *store, uint64_t fuel); + +/** + * \brief Returns the amount of fuel consumed by this context's store execution + * so far. + * + * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set + * then this function will return false. Otherwise true is returned and the + * fuel parameter is filled in with fuel consuemd so far. + * + * Also note that fuel, if enabled, must be originally configured via + * #wasmtime_store_add_fuel. + */ +WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel); + +/** + * \typedef wasmtime_interrupt_handle_t + * \brief Convenience alias for #wasmtime_interrupt_handle_t + * + * \struct wasmtime_interrupt_handle_t + * \brief A handle used to interrupt executing WebAssembly code. + * + * This structure is an opaque handle that represents a handle to a store. This + * handle can be used to remotely (from another thread) interrupt currently + * executing WebAssembly code. + * + * This structure is safe to share from multiple threads. + */ +typedef struct wasmtime_interrupt_handle wasmtime_interrupt_handle_t; + +/** + * \brief Creates a new interrupt handle to interrupt executing WebAssembly from + * the provided store. + * + * There are a number of caveats about how interrupt is handled in Wasmtime. For + * more information see the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Store.html#method.interrupt_handle). + * + * This function returns `NULL` if the store's configuration does not have + * interrupts enabled. See #wasmtime_config_interruptable_set. + */ +WASM_API_EXTERN wasmtime_interrupt_handle_t *wasmtime_interrupt_handle_new(wasmtime_context_t *context); + +/** + * \brief Requests that WebAssembly code running in the store attached to this + * interrupt handle is interrupted. + * + * For more information about interrupts see #wasmtime_interrupt_handle_new. + * + * Note that this is safe to call from any thread. + */ +WASM_API_EXTERN void wasmtime_interrupt_handle_interrupt(wasmtime_interrupt_handle_t *handle); + +/* + * \brief Deletes an interrupt handle. + */ +WASM_API_EXTERN void wasmtime_interrupt_handle_delete(wasmtime_interrupt_handle_t *handle); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_STORE_H + diff --git a/crates/c-api/include/wasmtime/table.h b/crates/c-api/include/wasmtime/table.h new file mode 100644 index 000000000000..af21fafba3ea --- /dev/null +++ b/crates/c-api/include/wasmtime/table.h @@ -0,0 +1,103 @@ +#ifndef WASMTIME_TABLE_H +#define WASMTIME_TABLE_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Creates a new host-defined wasm table. + * + * This function is the same as #wasm_table_new except that it's specialized for + * funcref tables by taking a `wasm_func_t` initialization value. Additionally + * it returns errors via #wasmtime_error_t. + * + * This function does not take ownership of any of its parameters, but yields + * ownership of returned values (the table and error). + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new( + wasmtime_context_t *store, + const wasm_tabletype_t *element_ty, + wasmtime_val_t *init, + wasmtime_table_t *table +); + +WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( + const wasmtime_context_t *store, + wasmtime_table_t table +); + +/** + * \brief Gets a value in a table. + * + * This function is the same as #wasm_table_get except that it's specialized for + * funcref tables by returning a `wasm_func_t` value. Additionally a `bool` + * return value indicates whether the `index` provided was in bounds. + * + * This function does not take ownership of any of its parameters, but yields + * ownership of returned #wasm_func_t. + */ +WASM_API_EXTERN bool wasmtime_table_get( + wasmtime_context_t *store, + wasmtime_table_t table, + uint32_t index, + wasmtime_val_t *val +); + +/** + * \brief Sets a value in a table. + * + * This function is similar to #wasm_table_set, but has a few differences: + * + * * An error is returned through #wasmtime_error_t describing erroneous + * situations. + * * The value being set is specialized to #wasm_func_t. + * + * This function does not take ownership of any of its parameters, but yields + * ownership of returned #wasmtime_error_t. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( + wasmtime_context_t *store, + wasmtime_table_t table, + uint32_t index, + const wasmtime_val_t *value +); + +WASM_API_EXTERN uint32_t wasmtime_table_size( + const wasmtime_context_t *store, + wasmtime_table_t table +); + +/** + * \brief Grows a table. + * + * This function is similar to #wasm_table_grow, but has a few differences: + * + * * An error is returned through #wasmtime_error_t describing erroneous + * situations. + * * The initialization value is specialized to #wasm_func_t. + * * The previous size of the table is returned through `prev_size`. + * + * This function does not take ownership of any of its parameters, but yields + * ownership of returned #wasmtime_error_t. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_table_grow( + wasmtime_context_t *store, + wasmtime_table_t table, + uint32_t delta, + const wasmtime_val_t *init, + wasm_table_size_t *prev_size +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_TABLE_H + diff --git a/crates/c-api/include/wasmtime/trap.h b/crates/c-api/include/wasmtime/trap.h new file mode 100644 index 000000000000..f87534c89846 --- /dev/null +++ b/crates/c-api/include/wasmtime/trap.h @@ -0,0 +1,46 @@ +#ifndef WASMTIME_TRAP_H +#define WASMTIME_TRAP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(char *msg); + +/** + * \brief Attempts to extract a WASI-specific exit status from this trap. + * + * Returns `true` if the trap is a WASI "exit" trap and has a return status. If + * `true` is returned then the exit status is returned through the `status` + * pointer. If `false` is returned then this is not a wasi exit trap. + */ +WASM_API_EXTERN bool wasmtime_trap_exit_status(const wasm_trap_t*, int *status); + +/** + * \brief Returns a human-readable name for this frame's function. + * + * This function will attempt to load a human-readable name for function this + * frame points to. This function may return `NULL`. + * + * The lifetime of the returned name is the same as the #wasm_frame_t itself. + */ +WASM_API_EXTERN const wasm_name_t *wasmtime_frame_func_name(const wasm_frame_t*); + +/** + * \brief Returns a human-readable name for this frame's module. + * + * This function will attempt to load a human-readable name for module this + * frame points to. This function may return `NULL`. + * + * The lifetime of the returned name is the same as the #wasm_frame_t itself. + */ +WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t*); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_TRAP_H diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h new file mode 100644 index 000000000000..1cfce705e935 --- /dev/null +++ b/crates/c-api/include/wasmtime/val.h @@ -0,0 +1,97 @@ +#ifndef WASMTIME_VAL_H +#define WASMTIME_VAL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct wasmtime_externref wasmtime_externref_t; + +/** + * \brief Create a new `externref` value with a finalizer. + * + * TODO + * + * Creates a new `externref` value wrapping the provided data, and writes it to + * `valp`. + * + * When the reference is reclaimed, the wrapped data is cleaned up with the + * provided finalizer. If you do not need to clean up the wrapped data, then use + * #wasmtime_externref_new. + * + * Gives ownership of the newly created `externref` value. + */ +WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (*finalizer)(void*)); + +/** + * \brief Get an `externref`'s wrapped data + * + * TODO + * + * If the given value is a reference to a non-null `externref`, writes the + * wrapped data that was passed into #wasmtime_externref_new or + * #wasmtime_externref_new_with_finalizer when creating the given `externref` to + * `datap`, and returns `true`. + * + * If the value is a reference to a null `externref`, writes `NULL` to `datap` + * and returns `true`. + * + * If the given value is not an `externref`, returns `false` and leaves `datap` + * unmodified. + * + * Does not take ownership of `val`. Does not give up ownership of the `void*` + * data written to `datap`. + * + * Both `val` and `datap` must not be `NULL`. + */ +WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_externref_t *data); + +WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externref_t *ref); + +/** + * TODO + */ +WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref); + +typedef enum wasmtime_valkind { + WASMTIME_I32, + WASMTIME_I64, + WASMTIME_F32, + WASMTIME_F64, + WASMTIME_V128, + WASMTIME_FUNCREF, + WASMTIME_EXTERNREF, +} wasmtime_valkind_t; + +typedef uint8_t wasmtime_v128[16]; + +typedef union wasmtime_valunion { + int32_t i32; + int64_t i64; + float32_t f32; + float64_t f64; + wasmtime_func_t funcref; + wasmtime_externref_t *externref; + wasmtime_v128 v128; +} wasmtime_valunion_t; + +typedef struct wasmtime_val { + wasmtime_valkind_t kind; + wasmtime_valunion_t of; +} wasmtime_val_t; + +#define WASMTIME_FUNCREF_NULL ((uint64_t) -1) +/** + * TODO + */ +WASM_API_EXTERN void wasmtime_val_delete(wasmtime_val_t *val); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_VAL_H + diff --git a/crates/c-api/src/error.rs b/crates/c-api/src/error.rs index 20a7b9bfadcc..073c158c3b4d 100644 --- a/crates/c-api/src/error.rs +++ b/crates/c-api/src/error.rs @@ -8,12 +8,6 @@ pub struct wasmtime_error_t { wasmtime_c_api_macros::declare_own!(wasmtime_error_t); -// impl wasmtime_error_t { -// pub(crate) fn to_trap(self) -> Box { -// Box::new(wasm_trap_t::new(Trap::from(self.error))) -// } -// } - impl From for wasmtime_error_t { fn from(error: Error) -> wasmtime_error_t { wasmtime_error_t { error } diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs index 1bca0ca4ed12..15809290d998 100644 --- a/crates/c-api/src/extern.rs +++ b/crates/c-api/src/extern.rs @@ -1,8 +1,9 @@ use crate::{ wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_instance_t, - wasm_memory_t, wasm_module_t, wasm_table_t, StoreRef, + wasm_memory_t, wasm_module_t, wasm_table_t, wasmtime_module_t, StoreRef, }; -use wasmtime::Extern; +use std::mem::ManuallyDrop; +use wasmtime::{Extern, Func, Global, Instance, Memory, Table}; #[derive(Clone)] pub struct wasm_extern_t { @@ -88,3 +89,88 @@ pub extern "C" fn wasm_extern_as_instance(e: &wasm_extern_t) -> Option<&wasm_ins pub extern "C" fn wasm_extern_as_instance_const(e: &wasm_extern_t) -> Option<&wasm_instance_t> { wasm_extern_as_instance(e) } + +#[repr(C)] +pub struct wasmtime_extern_t { + pub kind: wasmtime_extern_kind_t, + pub of: wasmtime_extern_union, +} + +pub type wasmtime_extern_kind_t = u8; +pub const WASMTIME_EXTERN_FUNC: wasmtime_extern_kind_t = 0; +pub const WASMTIME_EXTERN_GLOBAL: wasmtime_extern_kind_t = 1; +pub const WASMTIME_EXTERN_TABLE: wasmtime_extern_kind_t = 2; +pub const WASMTIME_EXTERN_MEMORY: wasmtime_extern_kind_t = 3; +pub const WASMTIME_EXTERN_INSTANCE: wasmtime_extern_kind_t = 4; +pub const WASMTIME_EXTERN_MODULE: wasmtime_extern_kind_t = 5; + +pub union wasmtime_extern_union { + pub func: Func, + pub table: Table, + pub global: Global, + pub instance: Instance, + pub memory: Memory, + pub module: ManuallyDrop>, +} + +impl wasmtime_extern_t { + pub unsafe fn to_extern(&self) -> Extern { + match self.kind { + WASMTIME_EXTERN_FUNC => Extern::Func(self.of.func), + WASMTIME_EXTERN_GLOBAL => Extern::Global(self.of.global), + WASMTIME_EXTERN_TABLE => Extern::Table(self.of.table), + WASMTIME_EXTERN_MEMORY => Extern::Memory(self.of.memory), + WASMTIME_EXTERN_INSTANCE => Extern::Instance(self.of.instance), + WASMTIME_EXTERN_MODULE => Extern::Module(self.of.module.module.clone()), + other => panic!("unknown wasm_extern_kind_t: {}", other), + } + } +} + +impl From for wasmtime_extern_t { + fn from(item: Extern) -> wasmtime_extern_t { + match item { + Extern::Func(func) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_FUNC, + of: wasmtime_extern_union { func }, + }, + Extern::Global(global) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_GLOBAL, + of: wasmtime_extern_union { global }, + }, + Extern::Table(table) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_TABLE, + of: wasmtime_extern_union { table }, + }, + Extern::Memory(memory) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_MEMORY, + of: wasmtime_extern_union { memory }, + }, + Extern::Instance(instance) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_INSTANCE, + of: wasmtime_extern_union { instance }, + }, + Extern::Module(module) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_MODULE, + of: wasmtime_extern_union { + module: ManuallyDrop::new(Box::new(wasmtime_module_t { module })), + }, + }, + } + } +} + +impl Drop for wasmtime_extern_t { + fn drop(&mut self) { + if self.kind == WASMTIME_EXTERN_MODULE { + unsafe { + ManuallyDrop::drop(&mut self.of.module); + } + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_extern_delete(e: &mut ManuallyDrop) { + ManuallyDrop::drop(e); +} diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index f726760d3175..8a9d3edb28c1 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -1,11 +1,15 @@ use crate::wasm_trap_t; -use crate::{wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t}; +use crate::{ + wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t, wasmtime_error_t, + wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, CStoreContext, CStoreContextMut, +}; use anyhow::anyhow; use std::ffi::c_void; +use std::mem::MaybeUninit; use std::panic::{self, AssertUnwindSafe}; use std::ptr; use std::str; -use wasmtime::{Extern, Func, Trap}; +use wasmtime::{AsContextMut, Caller, Extern, Func, Trap}; #[derive(Clone)] #[repr(transparent)] @@ -15,11 +19,6 @@ pub struct wasm_func_t { wasmtime_c_api_macros::declare_ref!(wasm_func_t); -// #[repr(C)] -// pub struct wasmtime_caller_t<'a> { -// caller: Caller<'a>, -// } - pub type wasm_func_callback_t = extern "C" fn( args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t, @@ -31,19 +30,6 @@ pub type wasm_func_callback_with_env_t = extern "C" fn( results: *mut wasm_val_vec_t, ) -> Option>; -// pub type wasmtime_func_callback_t = extern "C" fn( -// caller: *const wasmtime_caller_t, -// args: *const wasm_val_vec_t, -// results: *mut wasm_val_vec_t, -// ) -> Option>; - -// pub type wasmtime_func_callback_with_env_t = extern "C" fn( -// caller: *const wasmtime_caller_t, -// env: *mut std::ffi::c_void, -// args: *const wasm_val_vec_t, -// results: *mut wasm_val_vec_t, -// ) -> Option>; - struct Finalizer { env: *mut c_void, finalizer: Option, @@ -125,17 +111,6 @@ pub unsafe extern "C" fn wasm_func_new( create_function(store, ty, move |params, results| callback(params, results)) } -// #[no_mangle] -// pub unsafe extern "C" fn wasmtime_func_new( -// store: &wasm_store_t, -// ty: &wasm_functype_t, -// callback: wasmtime_func_callback_t, -// ) -> Box { -// create_function(store, ty, move |params, results| { -// callback(&wasmtime_caller_t { caller }, params, results) -// }) -// } - #[no_mangle] pub unsafe extern "C" fn wasm_func_new_with_env( store: &mut wasm_store_t, @@ -150,25 +125,6 @@ pub unsafe extern "C" fn wasm_func_new_with_env( }) } -// #[no_mangle] -// pub extern "C" fn wasmtime_func_new_with_env( -// store: &wasm_store_t, -// ty: &wasm_functype_t, -// callback: wasmtime_func_callback_with_env_t, -// env: *mut c_void, -// finalizer: Option, -// ) -> Box { -// let finalizer = Finalizer { env, finalizer }; -// create_function(store, ty, move |caller, params, results| { -// callback( -// &wasmtime_caller_t { caller }, -// finalizer.env, -// params, -// results, -// ) -// }) -// } - #[no_mangle] pub unsafe extern "C" fn wasm_func_call( func: &mut wasm_func_t, @@ -237,30 +193,141 @@ pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t &mut (*f).ext } -// #[no_mangle] -// pub extern "C" fn wasmtime_caller_export_get( -// caller: &wasmtime_caller_t, -// name: &wasm_name_t, -// ) -> Option> { -// let name = str::from_utf8(name.as_slice()).ok()?; -// let which = caller.caller.get_export(name)?; -// Some(Box::new(wasm_extern_t { which })) -// } +#[repr(C)] +pub struct wasmtime_caller_t<'a> { + caller: Caller<'a, crate::ForeignData>, +} -// #[no_mangle] -// pub extern "C" fn wasmtime_func_as_funcref( -// func: &wasm_func_t, -// funcrefp: &mut MaybeUninit, -// ) { -// let funcref = wasm_val_t::from_val(Val::FuncRef(Some(func.func().clone()))); -// crate::initialize(funcrefp, funcref); -// } +#[no_mangle] +pub unsafe extern "C" fn wasmtime_func_new_with_env( + store: CStoreContextMut<'_>, + ty: &wasm_functype_t, + callback: extern "C" fn( + *mut c_void, + *mut wasmtime_caller_t, + *const wasmtime_val_t, + usize, + *mut wasmtime_val_t, + usize, + ) -> Option>, + data: *mut c_void, + finalizer: Option, +) -> Func { + let foreign = crate::ForeignData { data, finalizer }; + let ty = ty.ty().ty.clone(); + Func::new(store, ty, move |caller, params, results| { + let params = params + .iter() + .cloned() + .map(|p| wasmtime_val_t::from_val(p)) + .collect::>(); + let mut out_results = (0..results.len()) + .map(|_| wasmtime_val_t { + kind: crate::WASMTIME_I32, + of: wasmtime_val_union { i32: 0 }, + }) + .collect::>(); + let mut caller = wasmtime_caller_t { caller }; + let out = callback( + foreign.data, + &mut caller, + params.as_ptr(), + params.len(), + out_results.as_mut_ptr(), + out_results.len(), + ); + if let Some(trap) = out { + return Err(trap.trap); + } -// #[no_mangle] -// pub extern "C" fn wasmtime_funcref_as_func(val: &wasm_val_t) -> Option> { -// if let Val::FuncRef(Some(f)) = val.val() { -// Some(Box::new(f.into())) -// } else { -// None -// } -// } + for (i, result) in out_results.iter().enumerate() { + results[i] = unsafe { result.to_val() }; + } + Ok(()) + }) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_func_call( + store: CStoreContextMut<'_>, + func: Func, + args: *const wasmtime_val_t, + nargs: usize, + results: *mut MaybeUninit, + nresults: usize, + trap_ret: &mut *mut wasm_trap_t, +) -> Option> { + if nresults != func.ty(&store).results().len() { + return Some(Box::new(wasmtime_error_t::from(anyhow!( + "wrong number of results provided" + )))); + } + let params = std::slice::from_raw_parts(args, nargs) + .iter() + .map(|i| i.to_val()) + .collect::>(); + + // We're calling arbitrary code here most of the time, and we in general + // want to try to insulate callers against bugs in wasmtime/wasi/etc if we + // can. As a result we catch panics here and transform them to traps to + // allow the caller to have any insulation possible against Rust panics. + let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(store, ¶ms))); + match result { + Ok(Ok(out)) => { + let results = std::slice::from_raw_parts_mut(results, nresults); + for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) { + crate::initialize(slot, wasmtime_val_t::from_val(val)); + } + None + } + Ok(Err(trap)) => match trap.downcast::() { + Ok(trap) => { + *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap))); + None + } + Err(err) => Some(Box::new(wasmtime_error_t::from(err))), + }, + Err(panic) => { + let trap = if let Some(msg) = panic.downcast_ref::() { + Trap::new(msg) + } else if let Some(msg) = panic.downcast_ref::<&'static str>() { + Trap::new(*msg) + } else { + Trap::new("rust panic happened") + }; + *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap))); + None + } + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_func_type(store: CStoreContext<'_>, func: Func) -> Box { + Box::new(wasm_functype_t::new(func.ty(store))) +} + +#[no_mangle] +pub extern "C" fn wasmtime_caller_context<'a>( + caller: &'a mut wasmtime_caller_t, +) -> CStoreContextMut<'a> { + caller.caller.as_context_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_caller_export_get( + caller: &mut wasmtime_caller_t, + name: *const u8, + name_len: usize, + item: &mut MaybeUninit, +) -> bool { + let name = match str::from_utf8(std::slice::from_raw_parts(name, name_len)) { + Ok(name) => name, + Err(_) => return false, + }; + let which = match caller.caller.get_export(name) { + Some(item) => item, + None => return false, + }; + crate::initialize(item, which.into()); + true +} diff --git a/crates/c-api/src/global.rs b/crates/c-api/src/global.rs index 8c6809f0a7a3..ebb48a3f2b90 100644 --- a/crates/c-api/src/global.rs +++ b/crates/c-api/src/global.rs @@ -1,4 +1,7 @@ -use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t}; +use crate::{ + handle_result, wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t, wasmtime_error_t, + wasmtime_val_t, CStoreContext, CStoreContextMut, +}; use std::mem::MaybeUninit; use wasmtime::{Extern, Global}; @@ -43,24 +46,6 @@ pub unsafe extern "C" fn wasm_global_new( } } -// #[no_mangle] -// pub extern "C" fn wasmtime_global_new( -// store: &mut wasm_store_t, -// gt: &wasm_globaltype_t, -// val: &wasm_val_t, -// ret: &mut *mut wasm_global_t, -// ) -> Option> { -// let global = Global::new(store.store.context_mut(), gt.ty().ty.clone(), val.val()); -// handle_result(global, |global| { -// *ret = Box::into_raw(Box::new(wasm_global_t { -// ext: wasm_extern_t { -// store: store.store.clone(), -// which: global.into(), -// }, -// })); -// }) -// } - #[no_mangle] pub extern "C" fn wasm_global_as_extern(g: &wasm_global_t) -> &wasm_extern_t { &g.ext @@ -87,10 +72,41 @@ pub unsafe extern "C" fn wasm_global_set(g: &mut wasm_global_t, val: &wasm_val_t drop(global.set(g.ext.store.context_mut(), val.val())); } -// #[no_mangle] -// pub extern "C" fn wasmtime_global_set( -// g: &wasm_global_t, -// val: &wasm_val_t, -// ) -> Option> { -// handle_result(g.global().set(val.val()), |()| {}) -// } +#[no_mangle] +pub unsafe extern "C" fn wasmtime_global_new( + store: CStoreContextMut<'_>, + gt: &wasm_globaltype_t, + val: &wasmtime_val_t, + ret: &mut Global, +) -> Option> { + let global = Global::new(store, gt.ty().ty.clone(), val.to_val()); + handle_result(global, |global| { + *ret = global; + }) +} + +#[no_mangle] +pub extern "C" fn wasmtime_global_type( + store: CStoreContext<'_>, + global: Global, +) -> Box { + Box::new(wasm_globaltype_t::new(global.ty(store))) +} + +#[no_mangle] +pub extern "C" fn wasmtime_global_get( + store: CStoreContextMut<'_>, + global: Global, + val: &mut MaybeUninit, +) { + crate::initialize(val, wasmtime_val_t::from_val(global.get(store))) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_global_set( + store: CStoreContextMut<'_>, + global: Global, + val: &wasmtime_val_t, +) -> Option> { + handle_result(global.set(store, val.to_val()), |()| {}) +} diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs index 4d820f031404..d443ee4971f1 100644 --- a/crates/c-api/src/instance.rs +++ b/crates/c-api/src/instance.rs @@ -1,6 +1,10 @@ -use crate::{wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_trap_t}; -use crate::{wasm_instancetype_t, wasm_store_t, StoreRef}; -use wasmtime::{Extern, Instance}; +use crate::{ + wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_store_t, wasm_trap_t, wasmtime_error_t, + wasmtime_extern_t, wasmtime_instancetype_t, wasmtime_module_t, CStoreContext, CStoreContextMut, + StoreRef, +}; +use std::mem::MaybeUninit; +use wasmtime::{Extern, Instance, Trap}; #[derive(Clone)] #[repr(transparent)] @@ -62,96 +66,8 @@ pub unsafe extern "C" fn wasm_instance_new( None } } - // let err = _wasmtime_instance_new( - // store, - // wasm_module, - // (*imports).as_slice(), - // &mut instance, - // &mut trap, - // ); - // match err { - // Some(err) => { - // assert!(trap.is_null()); - // assert!(instance.is_null()); - // if let Some(result) = result { - // *result = Box::into_raw(err.to_trap()); - // } - // None - // } - // None => { - // if instance.is_null() { - // assert!(!trap.is_null()); - // if let Some(result) = result { - // *result = trap; - // } else { - // drop(Box::from_raw(trap)) - // } - // None - // } else { - // assert!(trap.is_null()); - // Some(Box::from_raw(instance)) - // } - // } - // } } -// #[no_mangle] -// pub unsafe extern "C" fn wasmtime_instance_new( -// store: &wasm_store_t, -// module: &wasm_module_t, -// imports: *const wasm_extern_vec_t, -// instance_ptr: &mut *mut wasm_instance_t, -// trap_ptr: &mut *mut wasm_trap_t, -// ) -> Option> { -// _wasmtime_instance_new(store, module, (*imports).as_slice(), instance_ptr, trap_ptr) -// } - -// fn _wasmtime_instance_new( -// store: &wasm_store_t, -// module: &wasm_module_t, -// imports: &[Option>], -// instance_ptr: &mut *mut wasm_instance_t, -// trap_ptr: &mut *mut wasm_trap_t, -// ) -> Option> { -// let store = &store.store; -// let imports = imports -// .iter() -// .filter_map(|import| match import { -// Some(i) => Some(i.which.clone()), -// None => None, -// }) -// .collect::>(); -// handle_instantiate( -// Instance::new(store, module.module(), &imports), -// instance_ptr, -// trap_ptr, -// ) -// } - -// pub fn handle_instantiate( -// instance: Result, -// instance_ptr: &mut *mut wasm_instance_t, -// trap_ptr: &mut *mut wasm_trap_t, -// ) -> Option> { -// fn write(ptr: &mut *mut T, val: T) { -// *ptr = Box::into_raw(Box::new(val)) -// } - -// match instance { -// Ok(instance) => { -// write(instance_ptr, wasm_instance_t::new(instance)); -// None -// } -// Err(e) => match e.downcast::() { -// Ok(trap) => { -// write(trap_ptr, wasm_trap_t::new(trap)); -// None -// } -// Err(e) => Some(Box::new(e.into())), -// }, -// } -// } - #[no_mangle] pub extern "C" fn wasm_instance_as_extern(m: &wasm_instance_t) -> &wasm_extern_t { &m.ext @@ -178,8 +94,91 @@ pub unsafe extern "C" fn wasm_instance_exports( } #[no_mangle] -pub unsafe extern "C" fn wasm_instance_type(f: &wasm_instance_t) -> Box { - Box::new(wasm_instancetype_t::new( - f.instance().ty(f.ext.store.context()), - )) +pub unsafe extern "C" fn wasmtime_instance_new( + store: CStoreContextMut<'_>, + module: &wasmtime_module_t, + imports: *const wasmtime_extern_t, + nimports: usize, + instance: &mut Instance, + trap_ptr: &mut *mut wasm_trap_t, +) -> Option> { + let imports = std::slice::from_raw_parts(imports, nimports) + .iter() + .map(|i| i.to_extern()) + .collect::>(); + handle_instantiate( + Instance::new(store, &module.module, &imports), + instance, + trap_ptr, + ) +} + +pub(crate) fn handle_instantiate( + instance: anyhow::Result, + instance_ptr: &mut Instance, + trap_ptr: &mut *mut wasm_trap_t, +) -> Option> { + match instance { + Ok(i) => { + *instance_ptr = i; + None + } + Err(e) => match e.downcast::() { + Ok(trap) => { + *trap_ptr = Box::into_raw(Box::new(wasm_trap_t::new(trap))); + None + } + Err(e) => Some(Box::new(e.into())), + }, + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_instance_type( + store: CStoreContext<'_>, + instance: Instance, +) -> Box { + Box::new(wasmtime_instancetype_t::new(instance.ty(store))) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_instance_export_get( + store: CStoreContextMut<'_>, + instance: Instance, + name: *const u8, + name_len: usize, + item: &mut MaybeUninit, +) -> bool { + let name = std::slice::from_raw_parts(name, name_len); + let name = match std::str::from_utf8(name) { + Ok(name) => name, + Err(_) => return false, + }; + match instance.get_export(store, name) { + Some(e) => { + crate::initialize(item, e.into()); + true + } + None => false, + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_instance_export_nth( + store: CStoreContextMut<'_>, + instance: Instance, + index: usize, + name_ptr: &mut *const u8, + name_len: &mut usize, + item: &mut MaybeUninit, +) -> bool { + match instance.exports(store).nth(index) { + Some(e) => { + *name_ptr = e.name().as_ptr(); + *name_len = e.name().len(); + crate::initialize(item, e.into_extern().into()); + true + } + None => false, + } } diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 4f0553050f92..6ddb1660431c 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -1,6 +1,6 @@ #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] -#![allow(unknown_lints)] -#![allow(improper_ctypes_definitions)] +// #![allow(unknown_lints)] +// #![allow(improper_ctypes_definitions)] mod config; mod engine; @@ -9,7 +9,7 @@ mod r#extern; mod func; mod global; mod instance; -// mod linker; +mod linker; mod memory; mod module; mod r#ref; @@ -26,7 +26,7 @@ pub use crate::error::*; pub use crate::func::*; pub use crate::global::*; pub use crate::instance::*; -// pub use crate::linker::*; +pub use crate::linker::*; pub use crate::memory::*; pub use crate::module::*; pub use crate::r#extern::*; @@ -58,3 +58,19 @@ pub(crate) fn initialize(dst: &mut std::mem::MaybeUninit, val: T) { std::ptr::write(dst.as_mut_ptr(), val); } } + +pub struct ForeignData { + data: *mut std::ffi::c_void, + finalizer: Option, +} + +unsafe impl Send for ForeignData {} +unsafe impl Sync for ForeignData {} + +impl Drop for ForeignData { + fn drop(&mut self) { + if let Some(f) = self.finalizer { + f(self.data); + } + } +} diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 80aa3fa94507..1273832e622f 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -1,18 +1,20 @@ -use crate::{bad_utf8, handle_result, wasmtime_error_t}; -use crate::{wasm_extern_t, wasm_store_t}; -use crate::{wasm_func_t, wasm_instance_t, wasm_module_t, wasm_name_t, wasm_trap_t}; +use crate::{ + bad_utf8, handle_result, wasm_engine_t, wasm_trap_t, wasmtime_error_t, wasmtime_extern_t, + wasmtime_module_t, CStoreContextMut, +}; +use std::mem::MaybeUninit; use std::str; -use wasmtime::Linker; +use wasmtime::{Func, Instance, Linker}; #[repr(C)] pub struct wasmtime_linker_t { - linker: Linker, + linker: Linker, } #[no_mangle] -pub extern "C" fn wasmtime_linker_new(store: &wasm_store_t) -> Box { +pub extern "C" fn wasmtime_linker_new(engine: &wasm_engine_t) -> Box { Box::new(wasmtime_linker_t { - linker: Linker::new(&store.store), + linker: Linker::new(&engine.engine), }) } @@ -27,23 +29,28 @@ pub extern "C" fn wasmtime_linker_allow_shadowing( #[no_mangle] pub extern "C" fn wasmtime_linker_delete(_linker: Box) {} +macro_rules! to_str { + ($ptr:expr, $len:expr) => { + match str::from_utf8(std::slice::from_raw_parts($ptr, $len)) { + Ok(s) => s, + Err(_) => return bad_utf8(), + } + }; +} + #[no_mangle] -pub extern "C" fn wasmtime_linker_define( +pub unsafe extern "C" fn wasmtime_linker_define( linker: &mut wasmtime_linker_t, - module: &wasm_name_t, - name: &wasm_name_t, - item: &wasm_extern_t, + module: *const u8, + module_len: usize, + name: *const u8, + name_len: usize, + item: &wasmtime_extern_t, ) -> Option> { let linker = &mut linker.linker; - let module = match str::from_utf8(module.as_slice()) { - Ok(s) => s, - Err(_) => return bad_utf8(), - }; - let name = match str::from_utf8(name.as_slice()) { - Ok(s) => s, - Err(_) => return bad_utf8(), - }; - let item = item.which.clone(); + let module = to_str!(module, module_len); + let name = to_str!(name, name_len); + let item = item.to_extern(); handle_result(linker.define(module, name, item), |_linker| ()) } @@ -58,80 +65,74 @@ pub extern "C" fn wasmtime_linker_define_wasi( } #[no_mangle] -pub extern "C" fn wasmtime_linker_define_instance( +pub unsafe extern "C" fn wasmtime_linker_define_instance( linker: &mut wasmtime_linker_t, - name: &wasm_name_t, - instance: &wasm_instance_t, + store: CStoreContextMut<'_>, + name: *const u8, + name_len: usize, + instance: Instance, ) -> Option> { let linker = &mut linker.linker; - let name = match str::from_utf8(name.as_slice()) { - Ok(s) => s, - Err(_) => return bad_utf8(), - }; - handle_result(linker.instance(name, instance.instance()), |_linker| ()) + let name = to_str!(name, name_len); + handle_result(linker.instance(store, name, instance), |_linker| ()) } #[no_mangle] pub extern "C" fn wasmtime_linker_instantiate( linker: &wasmtime_linker_t, - module: &wasm_module_t, - instance_ptr: &mut *mut wasm_instance_t, + store: CStoreContextMut<'_>, + module: &wasmtime_module_t, + instance_ptr: &mut Instance, trap_ptr: &mut *mut wasm_trap_t, ) -> Option> { - let result = linker.linker.instantiate(module.module()); + let result = linker.linker.instantiate(store, &module.module); super::instance::handle_instantiate(result, instance_ptr, trap_ptr) } #[no_mangle] -pub extern "C" fn wasmtime_linker_module( +pub unsafe extern "C" fn wasmtime_linker_module( linker: &mut wasmtime_linker_t, - name: &wasm_name_t, - module: &wasm_module_t, + store: CStoreContextMut<'_>, + name: *const u8, + name_len: usize, + module: &wasmtime_module_t, ) -> Option> { let linker = &mut linker.linker; - let name = match str::from_utf8(name.as_slice()) { - Ok(s) => s, - Err(_) => return bad_utf8(), - }; - handle_result(linker.module(name, module.module()), |_linker| ()) + let name = to_str!(name, name_len); + handle_result(linker.module(store, name, &module.module), |_linker| ()) } #[no_mangle] -pub extern "C" fn wasmtime_linker_get_default( +pub unsafe extern "C" fn wasmtime_linker_get_default( linker: &wasmtime_linker_t, - name: &wasm_name_t, - func: &mut *mut wasm_func_t, + store: CStoreContextMut<'_>, + name: *const u8, + name_len: usize, + func: &mut Func, ) -> Option> { let linker = &linker.linker; - let name = match str::from_utf8(name.as_slice()) { - Ok(s) => s, - Err(_) => return bad_utf8(), - }; - handle_result(linker.get_default(name), |f| { - *func = Box::into_raw(Box::new(f.into())) - }) + let name = to_str!(name, name_len); + handle_result(linker.get_default(store, name), |f| *func = f) } #[no_mangle] -pub extern "C" fn wasmtime_linker_get_one_by_name( +pub unsafe extern "C" fn wasmtime_linker_get_one_by_name( linker: &wasmtime_linker_t, - module: &wasm_name_t, - name: Option<&wasm_name_t>, - item_ptr: &mut *mut wasm_extern_t, + store: CStoreContextMut<'_>, + module: *const u8, + module_len: usize, + name: *const u8, + name_len: usize, + item_ptr: &mut MaybeUninit, ) -> Option> { let linker = &linker.linker; - let module = match str::from_utf8(module.as_slice()) { - Ok(s) => s, - Err(_) => return bad_utf8(), - }; - let name = match name { - Some(name) => match str::from_utf8(name.as_slice()) { - Ok(s) => Some(s), - Err(_) => return bad_utf8(), - }, - None => None, + let module = to_str!(module, module_len); + let name = if name.is_null() { + None + } else { + Some(to_str!(name, name_len)) }; - handle_result(linker.get_one_by_name(module, name), |which| { - *item_ptr = Box::into_raw(Box::new(wasm_extern_t { which })) + handle_result(linker.get_one_by_name(store, module, name), |which| { + crate::initialize(item_ptr, which.into()) }) } diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs index 9edc1aa5df81..e763dcb8bf0c 100644 --- a/crates/c-api/src/memory.rs +++ b/crates/c-api/src/memory.rs @@ -1,4 +1,7 @@ -use crate::{wasm_extern_t, wasm_memorytype_t, wasm_store_t}; +use crate::{ + handle_result, wasm_extern_t, wasm_memorytype_t, wasm_store_t, wasmtime_error_t, CStoreContext, + CStoreContextMut, +}; use wasmtime::{Extern, Memory}; #[derive(Clone)] @@ -76,3 +79,50 @@ pub unsafe extern "C" fn wasm_memory_grow( let mut store = m.ext.store.context_mut(); memory.grow(&mut store, delta).is_ok() } + +// WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( +// wasmtime_context_t *store, +// const wasm_memorytype_t* +// ); + +#[no_mangle] +pub extern "C" fn wasmtime_memory_new( + store: CStoreContextMut<'_>, + ty: &wasm_memorytype_t, + ret: &mut Memory, +) -> Option> { + handle_result(Memory::new(store, ty.ty().ty.clone()), |mem| *ret = mem) +} + +#[no_mangle] +pub extern "C" fn wasmtime_memory_type( + store: CStoreContext<'_>, + mem: Memory, +) -> Box { + Box::new(wasm_memorytype_t::new(mem.ty(store))) +} + +#[no_mangle] +pub extern "C" fn wasmtime_memory_data(store: CStoreContext<'_>, mem: Memory) -> *const u8 { + mem.data(store).as_ptr() +} + +#[no_mangle] +pub extern "C" fn wasmtime_memory_data_size(store: CStoreContext<'_>, mem: Memory) -> usize { + mem.data(store).len() +} + +#[no_mangle] +pub extern "C" fn wasmtime_memory_size(store: CStoreContext<'_>, mem: Memory) -> u32 { + mem.size(store) +} + +#[no_mangle] +pub extern "C" fn wasmtime_memory_grow( + store: CStoreContextMut<'_>, + mem: Memory, + delta: u32, + prev_size: &mut u32, +) -> Option> { + handle_result(mem.grow(store, delta), |prev| *prev_size = prev) +} diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 31b15e1461d4..6c34056883ea 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -1,7 +1,7 @@ use crate::{ handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t, - wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_moduletype_t, wasm_store_t, - wasmtime_error_t, StoreRef, + wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t, + wasmtime_moduletype_t, StoreRef, }; use wasmtime::{Engine, Extern, Module}; @@ -140,17 +140,18 @@ pub unsafe extern "C" fn wasm_module_deserialize( } pub struct wasmtime_module_t { - module: Module, + pub(crate) module: Module, } #[no_mangle] -pub extern "C" fn wasmtime_module_new( +pub unsafe extern "C" fn wasmtime_module_new( engine: &wasm_engine_t, - binary: &wasm_byte_vec_t, + wasm: *const u8, + len: usize, out: &mut *mut wasmtime_module_t, ) -> Option> { handle_result( - Module::from_binary(&engine.engine, binary.as_slice()), + Module::from_binary(&engine.engine, std::slice::from_raw_parts(wasm, len)), |module| { *out = Box::into_raw(Box::new(wasmtime_module_t { module })); }, @@ -161,43 +162,19 @@ pub extern "C" fn wasmtime_module_new( pub extern "C" fn wasmtime_module_delete(_module: Box) {} #[no_mangle] -pub extern "C" fn wasmtime_module_validate( +pub unsafe extern "C" fn wasmtime_module_validate( engine: &wasm_engine_t, - binary: &wasm_byte_vec_t, + wasm: *const u8, + len: usize, ) -> Option> { - handle_result(Module::validate(&engine.engine, binary.as_slice()), |()| {}) -} - -// #[no_mangle] -// pub extern "C" fn wasm_module_exports(module: &wasm_module_t, out: &mut wasm_exporttype_vec_t) { -// let exports = module -// .module() -// .exports() -// .map(|e| { -// Some(Box::new(wasm_exporttype_t::new( -// e.name().to_owned(), -// e.ty(), -// ))) -// }) -// .collect::>(); -// out.set_buffer(exports); -// } - -// #[no_mangle] -// pub extern "C" fn wasm_module_imports(module: &wasm_module_t, out: &mut wasm_importtype_vec_t) { -// let imports = module -// .module() -// .imports() -// .map(|i| { -// Some(Box::new(wasm_importtype_t::new( -// i.module().to_owned(), -// i.name().map(|s| s.to_owned()), -// i.ty(), -// ))) -// }) -// .collect::>(); -// out.set_buffer(imports); -// } + let binary = std::slice::from_raw_parts(wasm, len); + handle_result(Module::validate(&engine.engine, binary), |()| {}) +} + +#[no_mangle] +pub extern "C" fn wasmtime_module_type(m: &wasmtime_module_t) -> Box { + Box::new(wasmtime_moduletype_t::new(m.module.ty())) +} #[no_mangle] pub extern "C" fn wasmtime_module_serialize( @@ -210,18 +187,12 @@ pub extern "C" fn wasmtime_module_serialize( #[no_mangle] pub unsafe extern "C" fn wasmtime_module_deserialize( engine: &wasm_engine_t, - binary: &wasm_byte_vec_t, + bytes: *const u8, + len: usize, out: &mut *mut wasmtime_module_t, ) -> Option> { - handle_result( - Module::deserialize(&engine.engine, binary.as_slice()), - |module| { - *out = Box::into_raw(Box::new(wasmtime_module_t { module })); - }, - ) -} - -#[no_mangle] -pub extern "C" fn wasmtime_module_type(m: &wasmtime_module_t) -> Box { - Box::new(wasm_moduletype_t::new(m.module.ty())) + let bytes = std::slice::from_raw_parts(bytes, len); + handle_result(Module::deserialize(&engine.engine, bytes), |module| { + *out = Box::into_raw(Box::new(wasmtime_module_t { module })); + }) } diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs index 87f749ad5037..5e966ce95cee 100644 --- a/crates/c-api/src/ref.rs +++ b/crates/c-api/src/ref.rs @@ -1,8 +1,4 @@ -use crate::wasm_val_t; -use std::any::Any; -use std::mem::MaybeUninit; use std::os::raw::c_void; -use std::ptr; use wasmtime::{ExternRef, Func, Val}; /// `*mut wasm_ref_t` is a reference type (`externref` or `funcref`), as seen by @@ -85,63 +81,3 @@ pub extern "C" fn wasm_ref_set_host_info_with_finalizer( eprintln!("`wasm_ref_set_host_info_with_finalizer` is not implemented"); std::process::abort(); } - -type wasmtime_externref_finalizer_t = extern "C" fn(*mut c_void); - -struct CExternRef { - data: *mut c_void, - finalizer: Option, -} - -unsafe impl Send for CExternRef {} -unsafe impl Sync for CExternRef {} - -impl Drop for CExternRef { - fn drop(&mut self) { - if let Some(f) = self.finalizer { - f(self.data); - } - } -} - -#[no_mangle] -pub extern "C" fn wasmtime_externref_new(data: *mut c_void, valp: &mut MaybeUninit) { - wasmtime_externref_new_with_finalizer(data, None, valp) -} - -#[no_mangle] -pub extern "C" fn wasmtime_externref_new_with_finalizer( - data: *mut c_void, - finalizer: Option, - valp: &mut MaybeUninit, -) { - crate::initialize( - valp, - wasm_val_t::from_val(Val::ExternRef(Some(ExternRef::new(CExternRef { - data, - finalizer, - })))), - ); -} - -#[no_mangle] -pub extern "C" fn wasmtime_externref_data( - val: &wasm_val_t, - datap: &mut MaybeUninit<*mut c_void>, -) -> bool { - match val.val() { - Val::ExternRef(None) => { - crate::initialize(datap, ptr::null_mut()); - true - } - Val::ExternRef(Some(x)) => { - let data = match x.data().downcast_ref::() { - Some(r) => r.data, - None => x.data() as *const dyn Any as *mut c_void, - }; - crate::initialize(datap, data); - true - } - _ => false, - } -} diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index 9963c2734b6a..c28d09a3a0e7 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -1,7 +1,8 @@ -use crate::wasm_engine_t; +use crate::{wasm_engine_t, wasmtime_error_t, ForeignData}; use std::cell::UnsafeCell; +use std::ffi::c_void; use std::sync::Arc; -use wasmtime::{AsContext, AsContextMut, Store, StoreContext, StoreContextMut}; +use wasmtime::{AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut}; #[derive(Clone)] pub struct StoreRef { @@ -37,47 +38,85 @@ pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box { }) } -// #[no_mangle] -// pub extern "C" fn wasmtime_store_gc(store: &wasm_store_t) { -// store.store.gc(); -// } - -// #[repr(C)] -// pub struct wasmtime_interrupt_handle_t { -// handle: InterruptHandle, -// } - -// wasmtime_c_api_macros::declare_own!(wasmtime_interrupt_handle_t); - -// #[no_mangle] -// pub extern "C" fn wasmtime_interrupt_handle_new( -// store: &wasm_store_t, -// ) -> Option> { -// Some(Box::new(wasmtime_interrupt_handle_t { -// handle: store.store.interrupt_handle().ok()?, -// })) -// } - -// #[no_mangle] -// pub extern "C" fn wasmtime_interrupt_handle_interrupt(handle: &wasmtime_interrupt_handle_t) { -// handle.handle.interrupt(); -// } - -// #[no_mangle] -// pub extern "C" fn wasmtime_store_add_fuel( -// store: &wasm_store_t, -// fuel: u64, -// ) -> Option> { -// crate::handle_result(store.store.add_fuel(fuel), |()| {}) -// } - -// #[no_mangle] -// pub extern "C" fn wasmtime_store_fuel_consumed(store: &wasm_store_t, fuel: &mut u64) -> bool { -// match store.store.fuel_consumed() { -// Some(amt) => { -// *fuel = amt; -// true -// } -// None => false, -// } -// } +#[repr(C)] +pub struct wasmtime_store_t { + pub(crate) store: Store, +} + +pub type CStoreContext<'a> = StoreContext<'a, crate::ForeignData>; +pub type CStoreContextMut<'a> = StoreContextMut<'a, crate::ForeignData>; + +#[no_mangle] +pub extern "C" fn wasmtime_store_delete(_: Box) {} + +#[no_mangle] +pub extern "C" fn wasmtime_store_new( + engine: &wasm_engine_t, + data: *mut c_void, + finalizer: Option, +) -> Box { + Box::new(wasmtime_store_t { + store: Store::new(&engine.engine, ForeignData { data, finalizer }), + }) +} + +#[no_mangle] +pub extern "C" fn wasmtime_store_context(store: &mut wasmtime_store_t) -> CStoreContextMut<'_> { + store.store.as_context_mut() +} + +#[no_mangle] +pub extern "C" fn wasmtime_context_get_data(store: CStoreContext<'_>) -> *mut c_void { + store.data().data +} + +#[no_mangle] +pub extern "C" fn wasmtime_context_set_data(mut store: CStoreContextMut<'_>, data: *mut c_void) { + store.data_mut().data = data; +} + +#[no_mangle] +pub extern "C" fn wasmtime_context_gc(mut context: CStoreContextMut<'_>) { + context.gc(); +} + +#[no_mangle] +pub extern "C" fn wasmtime_context_add_fuel( + mut store: CStoreContextMut<'_>, + fuel: u64, +) -> Option> { + crate::handle_result(store.add_fuel(fuel), |()| {}) +} + +#[no_mangle] +pub extern "C" fn wasmtime_context_fuel_consumed(store: CStoreContext<'_>, fuel: &mut u64) -> bool { + match store.fuel_consumed() { + Some(amt) => { + *fuel = amt; + true + } + None => false, + } +} + +#[repr(C)] +pub struct wasmtime_interrupt_handle_t { + handle: InterruptHandle, +} + +#[no_mangle] +pub extern "C" fn wasmtime_interrupt_handle_new( + store: CStoreContext<'_>, +) -> Option> { + Some(Box::new(wasmtime_interrupt_handle_t { + handle: store.interrupt_handle().ok()?, + })) +} + +#[no_mangle] +pub extern "C" fn wasmtime_interrupt_handle_interrupt(handle: &wasmtime_interrupt_handle_t) { + handle.handle.interrupt(); +} + +#[no_mangle] +pub extern "C" fn wasmtime_interrupt_handle_delete(_: Box) {} diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs index d9a987dc8be5..4853cbde7070 100644 --- a/crates/c-api/src/table.rs +++ b/crates/c-api/src/table.rs @@ -1,6 +1,9 @@ use crate::r#ref::{ref_to_val, val_into_ref}; -use crate::wasm_ref_t; -use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t}; +use crate::{ + handle_result, wasm_extern_t, wasm_ref_t, wasm_store_t, wasm_tabletype_t, wasmtime_error_t, + wasmtime_val_t, CStoreContext, CStoreContextMut, +}; +use std::mem::MaybeUninit; use wasmtime::{Extern, Table, TableType, Val, ValType}; #[derive(Clone)] @@ -56,30 +59,6 @@ pub unsafe extern "C" fn wasm_table_new( })) } -// #[no_mangle] -// pub extern "C" fn wasmtime_funcref_table_new( -// store: &wasm_store_t, -// tt: &wasm_tabletype_t, -// init: Option<&wasm_func_t>, -// out: &mut *mut wasm_table_t, -// ) -> Option> { -// let init: Val = match init { -// Some(val) => Val::FuncRef(Some(val.func().clone())), -// None => Val::FuncRef(None), -// }; -// handle_result( -// Table::new(store.store.context(), tt.ty().ty.clone(), init), -// |table| { -// *out = Box::into_raw(Box::new(wasm_table_t { -// ext: wasm_extern_t { -// store: store.store.clone(), -// which: table.into(), -// }, -// })); -// }, -// ) -// } - #[no_mangle] pub unsafe extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box { let table = t.table(); @@ -97,28 +76,6 @@ pub unsafe extern "C" fn wasm_table_get( val_into_ref(val) } -// #[no_mangle] -// pub extern "C" fn wasmtime_funcref_table_get( -// t: &wasm_table_t, -// index: wasm_table_size_t, -// ptr: &mut *mut wasm_func_t, -// ) -> bool { -// let table = t.table(); -// let mut store = t.ext.store.context(); -// match table.get(&mut store, index) { -// Some(val) => { -// *ptr = match val { -// Val::FuncRef(None) => ptr::null_mut(), -// Val::FuncRef(Some(f)) => Box::into_raw(Box::new(f.into())), -// _ => return false, -// }; -// } - -// _ => return false, -// } -// true -// } - #[no_mangle] pub unsafe extern "C" fn wasm_table_set( t: &mut wasm_table_t, @@ -130,19 +87,6 @@ pub unsafe extern "C" fn wasm_table_set( table.set(t.ext.store.context_mut(), index, val).is_ok() } -// #[no_mangle] -// pub extern "C" fn wasmtime_funcref_table_set( -// t: &wasm_table_t, -// index: wasm_table_size_t, -// val: Option<&wasm_func_t>, -// ) -> Option> { -// let val = match val { -// Some(val) => Val::FuncRef(Some(val.func().clone())), -// None => Val::FuncRef(None), -// }; -// handle_result(t.table().set(index, val), |()| {}) -// } - #[no_mangle] pub unsafe extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t { let table = t.table(); @@ -161,25 +105,72 @@ pub unsafe extern "C" fn wasm_table_grow( table.grow(t.ext.store.context_mut(), delta, init).is_ok() } -// #[no_mangle] -// pub extern "C" fn wasmtime_funcref_table_grow( -// t: &wasm_table_t, -// delta: wasm_table_size_t, -// init: Option<&wasm_func_t>, -// prev_size: Option<&mut wasm_table_size_t>, -// ) -> Option> { -// let val = match init { -// Some(val) => Val::FuncRef(Some(val.func().clone())), -// None => Val::FuncRef(None), -// }; -// andle_result(t.table().grow(delta, val), |prev| { -// if let Some(ptr) = prev_size { -// *ptr = prev; -// } -// }) -// } - #[no_mangle] pub extern "C" fn wasm_table_as_extern(t: &wasm_table_t) -> &wasm_extern_t { &t.ext } + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_table_new( + store: CStoreContextMut<'_>, + tt: &wasm_tabletype_t, + init: &wasmtime_val_t, + out: &mut Table, +) -> Option> { + handle_result( + Table::new(store, tt.ty().ty.clone(), init.to_val()), + |table| *out = table, + ) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_table_type( + store: CStoreContext<'_>, + table: Table, +) -> Box { + Box::new(wasm_tabletype_t::new(table.ty(store))) +} + +#[no_mangle] +pub extern "C" fn wasmtime_table_get( + store: CStoreContextMut<'_>, + table: Table, + index: u32, + ret: &mut MaybeUninit, +) -> bool { + match table.get(store, index) { + Some(val) => { + crate::initialize(ret, wasmtime_val_t::from_val(val)); + true + } + None => false, + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_table_set( + store: CStoreContextMut<'_>, + table: Table, + index: u32, + val: &wasmtime_val_t, +) -> Option> { + handle_result(table.set(store, index, val.to_val()), |()| {}) +} + +#[no_mangle] +pub extern "C" fn wasmtime_table_size(store: CStoreContext<'_>, table: Table) -> u32 { + table.size(store) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_table_grow( + store: CStoreContextMut<'_>, + table: Table, + delta: u32, + val: &wasmtime_val_t, + prev_size: &mut u32, +) -> Option> { + handle_result(table.grow(store, delta, val.to_val()), |prev| { + *prev_size = prev + }) +} diff --git a/crates/c-api/src/trap.rs b/crates/c-api/src/trap.rs index 8822a4ba9e7d..6a4834c71d03 100644 --- a/crates/c-api/src/trap.rs +++ b/crates/c-api/src/trap.rs @@ -1,5 +1,7 @@ use crate::{wasm_frame_vec_t, wasm_instance_t, wasm_name_t, wasm_store_t}; use once_cell::unsync::OnceCell; +use std::ffi::CStr; +use std::os::raw::c_char; use wasmtime::Trap; #[repr(C)] @@ -44,6 +46,15 @@ pub extern "C" fn wasm_trap_new( }) } +#[no_mangle] +pub unsafe extern "C" fn wasmtime_trap_new(message: *const c_char) -> Box { + let bytes = CStr::from_ptr(message).to_bytes(); + let message = String::from_utf8_lossy(&bytes); + Box::new(wasm_trap_t { + trap: Trap::new(message), + }) +} + #[no_mangle] pub extern "C" fn wasm_trap_message(trap: &wasm_trap_t, out: &mut wasm_message_t) { let mut buffer = Vec::new(); diff --git a/crates/c-api/src/types/extern.rs b/crates/c-api/src/types/extern.rs index d86a9e081030..99b279b6b98c 100644 --- a/crates/c-api/src/types/extern.rs +++ b/crates/c-api/src/types/extern.rs @@ -1,5 +1,5 @@ use crate::{wasm_functype_t, wasm_globaltype_t, wasm_memorytype_t, wasm_tabletype_t}; -use crate::{wasm_instancetype_t, wasm_moduletype_t}; +use crate::{wasmtime_instancetype_t, wasmtime_moduletype_t}; use crate::{CFuncType, CGlobalType, CInstanceType, CMemoryType, CModuleType, CTableType}; use wasmtime::ExternType; @@ -125,27 +125,13 @@ pub extern "C" fn wasm_externtype_as_memorytype_const( #[no_mangle] pub extern "C" fn wasm_externtype_as_moduletype( et: &wasm_externtype_t, -) -> Option<&wasm_moduletype_t> { - wasm_externtype_as_moduletype_const(et) -} - -#[no_mangle] -pub extern "C" fn wasm_externtype_as_moduletype_const( - et: &wasm_externtype_t, -) -> Option<&wasm_moduletype_t> { - wasm_moduletype_t::try_from(et) +) -> Option<&wasmtime_moduletype_t> { + wasmtime_moduletype_t::try_from(et) } #[no_mangle] pub extern "C" fn wasm_externtype_as_instancetype( et: &wasm_externtype_t, -) -> Option<&wasm_instancetype_t> { - wasm_externtype_as_instancetype_const(et) -} - -#[no_mangle] -pub extern "C" fn wasm_externtype_as_instancetype_const( - et: &wasm_externtype_t, -) -> Option<&wasm_instancetype_t> { - wasm_instancetype_t::try_from(et) +) -> Option<&wasmtime_instancetype_t> { + wasmtime_instancetype_t::try_from(et) } diff --git a/crates/c-api/src/types/instance.rs b/crates/c-api/src/types/instance.rs index 373431c3624c..b4a18d295818 100644 --- a/crates/c-api/src/types/instance.rs +++ b/crates/c-api/src/types/instance.rs @@ -3,25 +3,25 @@ use wasmtime::InstanceType; #[repr(transparent)] #[derive(Clone)] -pub struct wasm_instancetype_t { +pub struct wasmtime_instancetype_t { ext: wasm_externtype_t, } -wasmtime_c_api_macros::declare_ty!(wasm_instancetype_t); +wasmtime_c_api_macros::declare_ty!(wasmtime_instancetype_t); #[derive(Clone)] pub(crate) struct CInstanceType { pub(crate) ty: InstanceType, } -impl wasm_instancetype_t { - pub(crate) fn new(ty: InstanceType) -> wasm_instancetype_t { - wasm_instancetype_t { +impl wasmtime_instancetype_t { + pub(crate) fn new(ty: InstanceType) -> wasmtime_instancetype_t { + wasmtime_instancetype_t { ext: wasm_externtype_t::new(ty.into()), } } - pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_instancetype_t> { + pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasmtime_instancetype_t> { match &e.which { CExternType::Instance(_) => Some(unsafe { &*(e as *const _ as *const _) }), _ => None, @@ -42,20 +42,15 @@ impl CInstanceType { } } #[no_mangle] -pub extern "C" fn wasm_instancetype_as_externtype(ty: &wasm_instancetype_t) -> &wasm_externtype_t { - &ty.ext -} - -#[no_mangle] -pub extern "C" fn wasm_instancetype_as_externtype_const( - ty: &wasm_instancetype_t, +pub extern "C" fn wasmtime_instancetype_as_externtype( + ty: &wasmtime_instancetype_t, ) -> &wasm_externtype_t { &ty.ext } #[no_mangle] -pub extern "C" fn wasm_instancetype_exports( - instance: &wasm_instancetype_t, +pub extern "C" fn wasmtime_instancetype_exports( + instance: &wasmtime_instancetype_t, out: &mut wasm_exporttype_vec_t, ) { let exports = instance diff --git a/crates/c-api/src/types/module.rs b/crates/c-api/src/types/module.rs index eaa681c6089f..3af98b6f6948 100644 --- a/crates/c-api/src/types/module.rs +++ b/crates/c-api/src/types/module.rs @@ -6,25 +6,25 @@ use wasmtime::ModuleType; #[repr(transparent)] #[derive(Clone)] -pub struct wasm_moduletype_t { +pub struct wasmtime_moduletype_t { ext: wasm_externtype_t, } -wasmtime_c_api_macros::declare_ty!(wasm_moduletype_t); +wasmtime_c_api_macros::declare_ty!(wasmtime_moduletype_t); #[derive(Clone)] pub(crate) struct CModuleType { pub(crate) ty: ModuleType, } -impl wasm_moduletype_t { - pub(crate) fn new(ty: ModuleType) -> wasm_moduletype_t { - wasm_moduletype_t { +impl wasmtime_moduletype_t { + pub(crate) fn new(ty: ModuleType) -> wasmtime_moduletype_t { + wasmtime_moduletype_t { ext: wasm_externtype_t::new(ty.into()), } } - pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_moduletype_t> { + pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasmtime_moduletype_t> { match &e.which { CExternType::Module(_) => Some(unsafe { &*(e as *const _ as *const _) }), _ => None, @@ -46,20 +46,15 @@ impl CModuleType { } #[no_mangle] -pub extern "C" fn wasm_moduletype_as_externtype(ty: &wasm_moduletype_t) -> &wasm_externtype_t { - &ty.ext -} - -#[no_mangle] -pub extern "C" fn wasm_moduletype_as_externtype_const( - ty: &wasm_moduletype_t, +pub extern "C" fn wasmtime_moduletype_as_externtype( + ty: &wasmtime_moduletype_t, ) -> &wasm_externtype_t { &ty.ext } #[no_mangle] -pub extern "C" fn wasm_moduletype_exports( - module: &wasm_moduletype_t, +pub extern "C" fn wasmtime_moduletype_exports( + module: &wasmtime_moduletype_t, out: &mut wasm_exporttype_vec_t, ) { let exports = module @@ -77,8 +72,8 @@ pub extern "C" fn wasm_moduletype_exports( } #[no_mangle] -pub extern "C" fn wasm_moduletype_imports( - module: &wasm_moduletype_t, +pub extern "C" fn wasmtime_moduletype_imports( + module: &wasmtime_moduletype_t, out: &mut wasm_importtype_vec_t, ) { let imports = module diff --git a/crates/c-api/src/types/val.rs b/crates/c-api/src/types/val.rs index a421516b867f..ab372df4d6a6 100644 --- a/crates/c-api/src/types/val.rs +++ b/crates/c-api/src/types/val.rs @@ -51,3 +51,12 @@ pub(crate) fn from_valtype(ty: &ValType) -> wasm_valkind_t { _ => panic!("wasm_valkind_t has no known conversion for {:?}", ty), } } + +pub type wasmtime_valkind_t = u8; +pub const WASMTIME_I32: wasmtime_valkind_t = 0; +pub const WASMTIME_I64: wasmtime_valkind_t = 1; +pub const WASMTIME_F32: wasmtime_valkind_t = 2; +pub const WASMTIME_F64: wasmtime_valkind_t = 3; +pub const WASMTIME_V128: wasmtime_valkind_t = 4; +pub const WASMTIME_FUNCREF: wasmtime_valkind_t = 5; +pub const WASMTIME_EXTERNREF: wasmtime_valkind_t = 6; diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs index b92dd7b655ca..89513103b7fc 100644 --- a/crates/c-api/src/val.rs +++ b/crates/c-api/src/val.rs @@ -1,8 +1,9 @@ use crate::r#ref::{ref_to_val, WasmRefInner}; -use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, WASM_I32}; -use std::mem::MaybeUninit; +use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, wasmtime_valkind_t, WASM_I32}; +use std::ffi::c_void; +use std::mem::{self, ManuallyDrop, MaybeUninit}; use std::ptr; -use wasmtime::{Val, ValType}; +use wasmtime::{ExternRef, Func, Val, ValType}; #[repr(C)] pub struct wasm_val_t { @@ -147,3 +148,120 @@ pub unsafe extern "C" fn wasm_val_copy(out: &mut MaybeUninit, source pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) { ptr::drop_in_place(val); } + +#[repr(C)] +pub struct wasmtime_val_t { + pub kind: wasmtime_valkind_t, + pub of: wasmtime_val_union, +} + +#[repr(C)] +pub union wasmtime_val_union { + pub i32: i32, + pub i64: i64, + pub f32: u32, + pub f64: u64, + pub funcref: u64, + pub externref: ManuallyDrop>, + pub v128: [u8; 16], +} + +impl wasmtime_val_t { + pub fn from_val(val: Val) -> wasmtime_val_t { + match val { + Val::I32(i) => wasmtime_val_t { + kind: crate::WASMTIME_I32, + of: wasmtime_val_union { i32: i }, + }, + Val::I64(i) => wasmtime_val_t { + kind: crate::WASMTIME_I64, + of: wasmtime_val_union { i64: i }, + }, + Val::F32(i) => wasmtime_val_t { + kind: crate::WASMTIME_F32, + of: wasmtime_val_union { f32: i }, + }, + Val::F64(i) => wasmtime_val_t { + kind: crate::WASMTIME_F64, + of: wasmtime_val_union { f64: i }, + }, + Val::ExternRef(i) => wasmtime_val_t { + kind: crate::WASMTIME_EXTERNREF, + of: wasmtime_val_union { + externref: ManuallyDrop::new(i), + }, + }, + Val::FuncRef(i) => wasmtime_val_t { + kind: crate::WASMTIME_FUNCREF, + of: wasmtime_val_union { + funcref: match i { + Some(func) => unsafe { mem::transmute::(func) }, + None => u64::max_value(), + }, + }, + }, + Val::V128(val) => wasmtime_val_t { + kind: crate::WASMTIME_V128, + of: wasmtime_val_union { + v128: val.to_le_bytes(), + }, + }, + } + } + + pub unsafe fn to_val(&self) -> Val { + match self.kind { + crate::WASMTIME_I32 => Val::I32(self.of.i32), + crate::WASMTIME_I64 => Val::I64(self.of.i64), + crate::WASMTIME_F32 => Val::F32(self.of.f32), + crate::WASMTIME_F64 => Val::F64(self.of.f64), + crate::WASMTIME_V128 => Val::V128(u128::from_le_bytes(self.of.v128)), + crate::WASMTIME_FUNCREF => Val::FuncRef(if self.of.funcref == u64::max_value() { + None + } else { + Some(mem::transmute::(self.of.funcref)) + }), + crate::WASMTIME_EXTERNREF => Val::ExternRef((*self.of.externref).clone()), + other => panic!("unknown wasmtime_valkind_t: {}", other), + } + } +} + +impl Drop for wasmtime_val_t { + fn drop(&mut self) { + if self.kind == crate::WASMTIME_EXTERNREF { + unsafe { + ManuallyDrop::drop(&mut self.of.externref); + } + } + } +} + +pub unsafe extern "C" fn wasmtime_val_delete(val: &mut ManuallyDrop) { + ManuallyDrop::drop(val) +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_new( + data: *mut c_void, + finalizer: Option, +) -> ExternRef { + ExternRef::new(crate::ForeignData { data, finalizer }) +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_data(externref: ManuallyDrop) -> *mut c_void { + externref + .data() + .downcast_ref::() + .unwrap() + .data +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_clone(externref: ManuallyDrop) -> ExternRef { + (*externref).clone() +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_delete(_val: Option) {} diff --git a/crates/c-api/src/vec.rs b/crates/c-api/src/vec.rs index fec8c7dd0d1b..1b45aeff22d6 100644 --- a/crates/c-api/src/vec.rs +++ b/crates/c-api/src/vec.rs @@ -1,7 +1,7 @@ use crate::{ wasm_exporttype_t, wasm_extern_t, wasm_externtype_t, wasm_frame_t, wasm_functype_t, - wasm_globaltype_t, wasm_importtype_t, wasm_instancetype_t, wasm_memorytype_t, - wasm_moduletype_t, wasm_tabletype_t, wasm_val_t, wasm_valtype_t, + wasm_globaltype_t, wasm_importtype_t, wasm_memorytype_t, wasm_tabletype_t, wasm_val_t, + wasm_valtype_t, }; use std::mem; use std::mem::MaybeUninit; @@ -186,24 +186,6 @@ declare_vecs! { copy: wasm_memorytype_vec_copy, delete: wasm_memorytype_vec_delete, ) - ( - name: wasm_instancetype_vec_t, - ty: Option>, - new: wasm_instancetype_vec_new, - empty: wasm_instancetype_vec_new_empty, - uninit: wasm_instancetype_vec_new_uninitialized, - copy: wasm_instancetype_vec_copy, - delete: wasm_instancetype_vec_delete, - ) - ( - name: wasm_moduletype_vec_t, - ty: Option>, - new: wasm_moduletype_vec_new, - empty: wasm_moduletype_vec_new_empty, - uninit: wasm_moduletype_vec_new_uninitialized, - copy: wasm_moduletype_vec_copy, - delete: wasm_moduletype_vec_delete, - ) ( name: wasm_externtype_vec_t, ty: Option>, diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 3d825ff501c9..b992650cc292 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -1,7 +1,7 @@ use crate::store::{StoreData, StoreOpaque, StoreOpaqueSend, Stored}; use crate::{ - AsContext, AsContextMut, Engine, Extern, FuncType, StoreContext, StoreContextMut, Trap, Val, - ValType, + AsContext, AsContextMut, Engine, Extern, FuncType, InterruptHandle, StoreContext, + StoreContextMut, Trap, Val, ValType, }; use anyhow::{bail, Context as _, Result}; use smallvec::{smallvec, SmallVec}; @@ -1500,6 +1500,18 @@ impl Caller<'_, T> { self.store.data_mut() } + /// Returns the underlying [`Engine`] this store is connected to. + pub fn engine(&self) -> &Engine { + self.store.engine() + } + + /// Returns an [`InterruptHandle`] to interrupt wasm execution. + /// + /// See [`Store::interrupt_handle`] for more information. + pub fn interrupt_handle(&self) -> Result { + self.store.interrupt_handle() + } + /// Perform garbage collection of `ExternRef`s. /// /// Same as [`Store::gc`]. diff --git a/crates/wasmtime/src/ref.rs b/crates/wasmtime/src/ref.rs index 7bd380b796e9..32aefc54d0ae 100644 --- a/crates/wasmtime/src/ref.rs +++ b/crates/wasmtime/src/ref.rs @@ -5,6 +5,7 @@ use wasmtime_runtime::VMExternRef; /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone, Debug)] +#[repr(transparent)] pub struct ExternRef { pub(crate) inner: VMExternRef, } diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 74df1790a697..680962558f39 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -405,6 +405,13 @@ impl<'a, T> StoreContext<'a, T> { self.0.engine() } + /// Returns an [`InterruptHandle`] to interrupt wasm execution. + /// + /// See [`Store::interrupt_handle`] for more information. + pub fn interrupt_handle(&self) -> Result { + self.0.interrupt_handle() + } + /// Access the underlying data owned by this `Store`. /// /// Same as [`Store::data`]. @@ -435,6 +442,18 @@ impl<'a, T> StoreContextMut<'a, T> { self.0.data_mut() } + /// Returns the underlying [`Engine`] this store is connected to. + pub fn engine(&self) -> &Engine { + self.0.engine() + } + + /// Returns an [`InterruptHandle`] to interrupt wasm execution. + /// + /// See [`Store::interrupt_handle`] for more information. + pub fn interrupt_handle(&self) -> Result { + self.0.interrupt_handle() + } + /// Perform garbage collection of `ExternRef`s. /// /// Same as [`Store::gc`]. From c800ab93cb354c9d5a69c7d007806880f3fb5113 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 11:54:04 -0700 Subject: [PATCH 06/90] Update the bench-api crate --- crates/bench-api/src/lib.rs | 115 +++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 36 deletions(-) diff --git a/crates/bench-api/src/lib.rs b/crates/bench-api/src/lib.rs index 6cf6104279f7..5291b0270b9e 100644 --- a/crates/bench-api/src/lib.rs +++ b/crates/bench-api/src/lib.rs @@ -79,12 +79,13 @@ //! ``` use anyhow::{anyhow, Context, Result}; +use std::borrow::{Borrow, BorrowMut}; use std::env; use std::os::raw::{c_int, c_void}; use std::path::Path; use std::slice; use wasmtime::{Config, Engine, Instance, Linker, Module, Store}; -use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder}; +use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx}; pub type ExitCode = c_int; pub const OK: ExitCode = 0; @@ -190,10 +191,58 @@ fn to_exit_code(result: impl Into>) -> ExitCode { /// to manage the Wasmtime engine between calls. struct BenchState { engine: Engine, - linker: Linker, + linker: Linker, module: Option, instance: Option, did_execute: bool, + store: Store, +} + +struct StoreState { + wasi: WasiCtx, + #[cfg(feature = "wasi-nn")] + wasi_nn: wasmtime_wasi_nn::WasiNnCtx, + #[cfg(feature = "wasi-crypto")] + wasi_crypto: wasmtime_wasi_crypto::WasiCryptoCtx, +} + +impl Borrow for StoreState { + fn borrow(&self) -> &WasiCtx { + &self.wasi + } +} +impl BorrowMut for StoreState { + fn borrow_mut(&mut self) -> &mut WasiCtx { + &mut self.wasi + } +} + +#[cfg(feature = "wasi-nn")] +impl Borrow for StoreState { + fn borrow(&self) -> &wasmtime_wasi_nn::WasiNnCtx { + &self.wasi_nn + } +} + +#[cfg(feature = "wasi-nn")] +impl BorrowMut for StoreState { + fn borrow_mut(&mut self) -> &mut wasmtime_wasi_nn::WasiNnCtx { + &mut self.wasi_nn + } +} + +#[cfg(feature = "wasi-crypto")] +impl Borrow for StoreState { + fn borrow(&self) -> &wasmtime_wasi_crypto::WasiCryptoCtx { + &self.wasi_crypto + } +} + +#[cfg(feature = "wasi-crypto")] +impl BorrowMut for StoreState { + fn borrow_mut(&mut self) -> &mut wasmtime_wasi_crypto::WasiCryptoCtx { + &mut self.wasi_crypto + } } impl BenchState { @@ -203,9 +252,7 @@ impl BenchState { // NB: do not configure a code cache. let engine = Engine::new(&config)?; - let store = Store::new(&engine); - - let mut linker = Linker::new(&store); + let mut linker = Linker::new(&engine); // Create a WASI environment. @@ -221,39 +268,34 @@ impl BenchState { if let Ok(val) = env::var("WASM_BENCH_USE_SMALL_WORKLOAD") { cx = cx.env("WASM_BENCH_USE_SMALL_WORKLOAD", &val)?; } - - Wasi::new(linker.store(), cx.build()?).add_to_linker(&mut linker)?; + let wasi = cx.build()?; + wasmtime_wasi::add_to_linker(&mut linker)?; #[cfg(feature = "wasi-nn")] - { - use std::cell::RefCell; - use std::rc::Rc; - use wasmtime_wasi_nn::{WasiNn, WasiNnCtx}; - - let wasi_nn = WasiNn::new(linker.store(), Rc::new(RefCell::new(WasiNnCtx::new()?))); - wasi_nn.add_to_linker(&mut linker)?; - } + let wasi_nn = { + wasmtime_wasi_nn::add_wasi_nn_to_linker(&mut linker)?; + wasmtime_wasi_nn::WasiNnCtx::new()? + }; #[cfg(feature = "wasi-crypto")] - { - use std::cell::RefCell; - use std::rc::Rc; - use wasmtime_wasi_crypto::{ - WasiCryptoAsymmetricCommon, WasiCryptoCommon, WasiCryptoCtx, WasiCryptoSignatures, - WasiCryptoSymmetric, - }; - - let cx_crypto = Rc::new(RefCell::new(WasiCryptoCtx::new())); - WasiCryptoCommon::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?; - WasiCryptoAsymmetricCommon::new(linker.store(), cx_crypto.clone()) - .add_to_linker(linker)?; - WasiCryptoSignatures::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?; - WasiCryptoSymmetric::new(linker.store(), cx_crypto).add_to_linker(linker)?; - } + let wasi_crypto = { + wasmtime_wasi_crypto::add_to_linker(&mut linker)?; + wasmtime_wasi_crypto::WasiCryptoCtx::new() + }; Ok(Self { - engine, linker, + store: Store::new( + &engine, + StoreState { + wasi, + #[cfg(feature = "wasi-nn")] + wasi_nn, + #[cfg(feature = "wasi-crypto")] + wasi_crypto, + }, + ), + engine, module: None, instance: None, did_execute: false, @@ -284,10 +326,11 @@ impl BenchState { .expect("compile the module before instantiating it"); // Import the specialized benchmarking functions. - self.linker.func("bench", "start", move || bench_start())?; - self.linker.func("bench", "end", move || bench_end())?; + self.linker + .func_wrap("bench", "start", move || bench_start())?; + self.linker.func_wrap("bench", "end", move || bench_end())?; - self.instance = Some(self.linker.instantiate(&module)?); + self.instance = Some(self.linker.instantiate(&mut self.store, &module)?); Ok(()) } @@ -300,8 +343,8 @@ impl BenchState { .as_ref() .expect("instantiate the module before executing it"); - let start_func = instance.get_typed_func::<(), ()>("_start")?; - match start_func.call(()) { + let start_func = instance.get_typed_func::<(), (), _>(&mut self.store, "_start")?; + match start_func.call(&mut self.store, ()) { Ok(_) => Ok(()), Err(trap) => { // Since _start will likely return by using the system `exit` call, we must From 1756c6d39ee13e420deaf33c037d1bfa7da5df7e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 11:54:34 -0700 Subject: [PATCH 07/90] Comment out wasi c api for now --- crates/c-api/src/linker.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 1273832e622f..7fbf141c2412 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -54,15 +54,15 @@ pub unsafe extern "C" fn wasmtime_linker_define( handle_result(linker.define(module, name, item), |_linker| ()) } -#[cfg(feature = "wasi")] -#[no_mangle] -pub extern "C" fn wasmtime_linker_define_wasi( - linker: &mut wasmtime_linker_t, - instance: &crate::wasi_instance_t, -) -> Option> { - let linker = &mut linker.linker; - handle_result(instance.add_to_linker(linker), |_linker| ()) -} +// #[cfg(feature = "wasi")] +// #[no_mangle] +// pub extern "C" fn wasmtime_linker_define_wasi( +// linker: &mut wasmtime_linker_t, +// instance: &crate::wasi_instance_t, +// ) -> Option> { +// let linker = &mut linker.linker; +// handle_result(instance.add_to_linker(linker), |_linker| ()) +// } #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_define_instance( From c42c35525a22b2f7f33edd882b0f16c93c2011a1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 12:14:44 -0700 Subject: [PATCH 08/90] More test updates --- crates/wiggle/wasmtime/tests/atoms_async.rs | 111 ++++++-------------- crates/wiggle/wasmtime/tests/atoms_sync.rs | 106 ++++++------------- 2 files changed, 63 insertions(+), 154 deletions(-) diff --git a/crates/wiggle/wasmtime/tests/atoms_async.rs b/crates/wiggle/wasmtime/tests/atoms_async.rs index 7dd5b6f5c267..fbb86388faa9 100644 --- a/crates/wiggle/wasmtime/tests/atoms_async.rs +++ b/crates/wiggle/wasmtime/tests/atoms_async.rs @@ -1,8 +1,7 @@ -use std::cell::RefCell; use std::future::Future; use std::pin::Pin; -use std::rc::Rc; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; +use wasmtime::{Config, Engine, Linker, Module, Store}; wasmtime_wiggle::from_witx!({ witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], @@ -15,7 +14,7 @@ wasmtime_wiggle::wasmtime_integration!({ target: crate, witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], ctx: Ctx, - modules: { atoms => { name: Atoms } }, + modules: { atoms => { name: atoms } }, async: { atoms::double_int_return_float } @@ -30,26 +29,30 @@ impl wiggle::GuestErrorType for types::Errno { #[wasmtime_wiggle::async_trait] impl atoms::Atoms for Ctx { - fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } async fn double_int_return_float( - &self, + &mut self, an_int: u32, ) -> Result { Ok((an_int as f32) * 2.0) } } -fn run_sync_func(linker: &wasmtime::Linker) { - let shim_mod = shim_module(linker.store()); - let shim_inst = run(linker.instantiate_async(&shim_mod)).unwrap(); +#[test] +fn test_sync_host_func() { + let mut store = async_store(); + let mut linker = Linker::new(store.engine()); + add_atoms_to_linker(&mut linker).unwrap(); + let shim_mod = shim_module(linker.engine()); + let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); let results = run(shim_inst - .get_func("int_float_args_shim") + .get_func(&mut store, "int_float_args_shim") .unwrap() - .call_async(&[0i32.into(), 123.45f32.into()])) + .call_async(&mut store, &[0i32.into(), 123.45f32.into()])) .unwrap(); assert_eq!(results.len(), 1, "one return value"); @@ -60,17 +63,22 @@ fn run_sync_func(linker: &wasmtime::Linker) { ); } -fn run_async_func(linker: &wasmtime::Linker) { - let shim_mod = shim_module(linker.store()); - let shim_inst = run(linker.instantiate_async(&shim_mod)).unwrap(); +#[test] +fn test_async_host_func() { + let mut store = async_store(); + let mut linker = Linker::new(store.engine()); + add_atoms_to_linker(&mut linker).unwrap(); + + let shim_mod = shim_module(linker.engine()); + let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); let input: i32 = 123; let result_location: i32 = 0; let results = run(shim_inst - .get_func("double_int_return_float_shim") + .get_func(&mut store, "double_int_return_float_shim") .unwrap() - .call_async(&[input.into(), result_location.into()])) + .call_async(&mut store, &[input.into(), result_location.into()])) .unwrap(); assert_eq!(results.len(), 1, "one return value"); @@ -81,70 +89,14 @@ fn run_async_func(linker: &wasmtime::Linker) { ); // The actual result is in memory: - let mem = shim_inst.get_memory("memory").unwrap(); + let mem = shim_inst.get_memory(&mut store, "memory").unwrap(); let mut result_bytes: [u8; 4] = [0, 0, 0, 0]; - mem.read(result_location as usize, &mut result_bytes) + mem.read(&store, result_location as usize, &mut result_bytes) .unwrap(); let result = f32::from_le_bytes(result_bytes); assert_eq!((input * 2) as f32, result); } -#[test] -fn test_sync_host_func() { - let store = async_store(); - - let ctx = Rc::new(RefCell::new(Ctx)); - let atoms = Atoms::new(&store, ctx.clone()); - - let mut linker = wasmtime::Linker::new(&store); - atoms.add_to_linker(&mut linker).unwrap(); - - run_sync_func(&linker); -} - -#[test] -fn test_async_host_func() { - let store = async_store(); - - let ctx = Rc::new(RefCell::new(Ctx)); - let atoms = Atoms::new(&store, ctx.clone()); - - let mut linker = wasmtime::Linker::new(&store); - atoms.add_to_linker(&mut linker).unwrap(); - - run_async_func(&linker); -} - -#[test] -fn test_sync_config_host_func() { - let mut config = wasmtime::Config::new(); - config.async_support(true); - Atoms::add_to_config(&mut config); - - let engine = wasmtime::Engine::new(&config).unwrap(); - let store = wasmtime::Store::new(&engine); - - assert!(Atoms::set_context(&store, Ctx).is_ok()); - - let linker = wasmtime::Linker::new(&store); - run_sync_func(&linker); -} - -#[test] -fn test_async_config_host_func() { - let mut config = wasmtime::Config::new(); - config.async_support(true); - Atoms::add_to_config(&mut config); - - let engine = wasmtime::Engine::new(&config).unwrap(); - let store = wasmtime::Store::new(&engine); - - assert!(Atoms::set_context(&store, Ctx).is_ok()); - - let linker = wasmtime::Linker::new(&store); - run_async_func(&linker); -} - fn run(future: F) -> F::Output { let mut f = Pin::from(Box::new(future)); let waker = dummy_waker(); @@ -179,18 +131,19 @@ fn dummy_waker() -> Waker { } } -fn async_store() -> wasmtime::Store { - wasmtime::Store::new( - &wasmtime::Engine::new(wasmtime::Config::new().async_support(true)).unwrap(), +fn async_store() -> Store { + Store::new( + &Engine::new(Config::new().async_support(true)).unwrap(), + Ctx, ) } // Wiggle expects the caller to have an exported memory. Wasmtime can only // provide this if the caller is a WebAssembly module, so we need to write // a shim module: -fn shim_module(store: &wasmtime::Store) -> wasmtime::Module { - wasmtime::Module::new( - store.engine(), +fn shim_module(engine: &Engine) -> Module { + Module::new( + engine, r#" (module (memory 1) diff --git a/crates/wiggle/wasmtime/tests/atoms_sync.rs b/crates/wiggle/wasmtime/tests/atoms_sync.rs index eee48f5338e4..2d6a1a579f4b 100644 --- a/crates/wiggle/wasmtime/tests/atoms_sync.rs +++ b/crates/wiggle/wasmtime/tests/atoms_sync.rs @@ -1,5 +1,4 @@ -use std::cell::RefCell; -use std::rc::Rc; +use wasmtime::{Engine, Linker, Store}; wasmtime_wiggle::from_witx!({ witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], @@ -12,7 +11,7 @@ wasmtime_wiggle::wasmtime_integration!({ target: crate, witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], ctx: Ctx, - modules: { atoms => { name: Atoms } }, + modules: { atoms => { name: atoms } }, block_on: { atoms::double_int_return_float } @@ -27,26 +26,31 @@ impl wiggle::GuestErrorType for types::Errno { #[wasmtime_wiggle::async_trait] impl atoms::Atoms for Ctx { - fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } async fn double_int_return_float( - &self, + &mut self, an_int: u32, ) -> Result { Ok((an_int as f32) * 2.0) } } -fn run_int_float_args(linker: &wasmtime::Linker) { - let shim_mod = shim_module(linker.store()); - let shim_inst = linker.instantiate(&shim_mod).unwrap(); +#[test] +fn test_sync_host_func() { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + add_atoms_to_linker(&mut linker).unwrap(); + let mut store = store(&engine); + let shim_mod = shim_module(&engine); + let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); let results = shim_inst - .get_func("int_float_args_shim") + .get_func(&mut store, "int_float_args_shim") .unwrap() - .call(&[0i32.into(), 123.45f32.into()]) + .call(&mut store, &[0i32.into(), 123.45f32.into()]) .unwrap(); assert_eq!(results.len(), 1, "one return value"); @@ -57,17 +61,23 @@ fn run_int_float_args(linker: &wasmtime::Linker) { ); } -fn run_double_int_return_float(linker: &wasmtime::Linker) { - let shim_mod = shim_module(linker.store()); - let shim_inst = linker.instantiate(&shim_mod).unwrap(); +#[test] +fn test_async_host_func() { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + add_atoms_to_linker(&mut linker).unwrap(); + let mut store = store(&engine); + + let shim_mod = shim_module(&engine); + let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); let input: i32 = 123; let result_location: i32 = 0; let results = shim_inst - .get_func("double_int_return_float_shim") + .get_func(&mut store, "double_int_return_float_shim") .unwrap() - .call(&[input.into(), result_location.into()]) + .call(&mut store, &[input.into(), result_location.into()]) .unwrap(); assert_eq!(results.len(), 1, "one return value"); @@ -78,78 +88,24 @@ fn run_double_int_return_float(linker: &wasmtime::Linker) { ); // The actual result is in memory: - let mem = shim_inst.get_memory("memory").unwrap(); + let mem = shim_inst.get_memory(&mut store, "memory").unwrap(); let mut result_bytes: [u8; 4] = [0, 0, 0, 0]; - mem.read(result_location as usize, &mut result_bytes) + mem.read(&store, result_location as usize, &mut result_bytes) .unwrap(); let result = f32::from_le_bytes(result_bytes); assert_eq!((input * 2) as f32, result); } -#[test] -fn test_sync_host_func() { - let store = store(); - - let ctx = Rc::new(RefCell::new(Ctx)); - let atoms = Atoms::new(&store, ctx.clone()); - - let mut linker = wasmtime::Linker::new(&store); - atoms.add_to_linker(&mut linker).unwrap(); - - run_int_float_args(&linker); -} - -#[test] -fn test_async_host_func() { - let store = store(); - - let ctx = Rc::new(RefCell::new(Ctx)); - let atoms = Atoms::new(&store, ctx.clone()); - - let mut linker = wasmtime::Linker::new(&store); - atoms.add_to_linker(&mut linker).unwrap(); - - run_double_int_return_float(&linker); -} - -#[test] -fn test_sync_config_host_func() { - let mut config = wasmtime::Config::new(); - Atoms::add_to_config(&mut config); - - let engine = wasmtime::Engine::new(&config).unwrap(); - let store = wasmtime::Store::new(&engine); - - assert!(Atoms::set_context(&store, Ctx).is_ok()); - - let linker = wasmtime::Linker::new(&store); - run_int_float_args(&linker); -} - -#[test] -fn test_async_config_host_func() { - let mut config = wasmtime::Config::new(); - Atoms::add_to_config(&mut config); - - let engine = wasmtime::Engine::new(&config).unwrap(); - let store = wasmtime::Store::new(&engine); - - assert!(Atoms::set_context(&store, Ctx).is_ok()); - - let linker = wasmtime::Linker::new(&store); - run_double_int_return_float(&linker); -} - -fn store() -> wasmtime::Store { - wasmtime::Store::new(&wasmtime::Engine::new(&wasmtime::Config::new()).unwrap()) +fn store(engine: &Engine) -> Store { + Store::new(engine, Ctx) } // Wiggle expects the caller to have an exported memory. Wasmtime can only // provide this if the caller is a WebAssembly module, so we need to write // a shim module: -fn shim_module(store: &wasmtime::Store) -> wasmtime::Module { +fn shim_module(engine: &Engine) -> wasmtime::Module { wasmtime::Module::new( - store.engine(), + engine, r#" (module (memory 1) From 2cfc3963aff45d7530d78ecdf68be856713915ce Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 13:08:56 -0700 Subject: [PATCH 09/90] Get more C API working --- crates/c-api/include/wasmtime.h | 3 +- crates/c-api/include/wasmtime/extern.h | 15 ++-- crates/c-api/include/wasmtime/func.h | 4 +- crates/c-api/include/wasmtime/val.h | 17 ++-- crates/c-api/src/extern.rs | 1 + crates/c-api/src/func.rs | 2 +- crates/c-api/src/val.rs | 1 + crates/c-api/src/wat2wasm.rs | 8 +- examples/externref.c | 108 ++++++++++++------------- examples/fib-debug/main.c | 69 +++++----------- examples/fuel.c | 53 ++++++------ examples/gcd.c | 46 +++++------ examples/hello.c | 56 +++++++------ examples/interrupt.c | 42 +++++----- 14 files changed, 196 insertions(+), 229 deletions(-) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 53c4d9a917e2..edf05d63c4b4 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -34,7 +34,8 @@ extern "C" { * deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. */ WASM_API_EXTERN wasmtime_error_t* wasmtime_wat2wasm( - const wasm_byte_vec_t *wat, + const char *wat, + size_t wat_len, wasm_byte_vec_t *ret ); diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index 82ac133923da..eb80ac2ad0e4 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -13,14 +13,13 @@ typedef uint64_t wasmtime_memory_t; typedef uint64_t wasmtime_instance_t; typedef uint64_t wasmtime_global_t; -typedef enum wasmtime_extern_kind { - WASMTIME_EXTERN_FUNC, - WASMTIME_EXTERN_GLOBAL, - WASMTIME_EXTERN_TABLE, - WASMTIME_EXTERN_MEMORY, - WASMTIME_EXTERN_INSTANCE, - WASMTIME_EXTERN_MODULE, -} wasmtime_extern_kind_t; +typedef uint8_t wasmtime_extern_kind_t; +#define WASMTIME_EXTERN_FUNC 0 +#define WASMTIME_EXTERN_GLOBAL 1 +#define WASMTIME_EXTERN_TABLE 2 +#define WASMTIME_EXTERN_MEMORY 3 +#define WASMTIME_EXTERN_INSTANCE 4 +#define WASMTIME_EXTERN_MODULE 5 typedef union wasmtime_extern_union { wasmtime_func_t func; diff --git a/crates/c-api/include/wasmtime/func.h b/crates/c-api/include/wasmtime/func.h index 8aa90b0bbdf7..96026fb5a0f8 100644 --- a/crates/c-api/include/wasmtime/func.h +++ b/crates/c-api/include/wasmtime/func.h @@ -33,7 +33,7 @@ typedef struct wasmtime_caller wasmtime_caller_t; */ typedef wasm_trap_t* (*wasmtime_func_callback_t)( void *env, - const wasmtime_caller_t* caller, + wasmtime_caller_t* caller, const wasmtime_val_t *args, size_t nargs, wasmtime_val_t *results, @@ -48,7 +48,7 @@ typedef wasm_trap_t* (*wasmtime_func_callback_t)( * signature #wasmtime_func_callback_t which gives a #wasmtime_caller_t as its * first argument. */ -WASM_API_EXTERN wasm_func_t wasmtime_func_new( +WASM_API_EXTERN wasmtime_func_t wasmtime_func_new( wasmtime_context_t *store, const wasm_functype_t* type, wasmtime_func_callback_t callback, diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h index 1cfce705e935..56052059a08a 100644 --- a/crates/c-api/include/wasmtime/val.h +++ b/crates/c-api/include/wasmtime/val.h @@ -56,15 +56,14 @@ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externre */ WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref); -typedef enum wasmtime_valkind { - WASMTIME_I32, - WASMTIME_I64, - WASMTIME_F32, - WASMTIME_F64, - WASMTIME_V128, - WASMTIME_FUNCREF, - WASMTIME_EXTERNREF, -} wasmtime_valkind_t; +typedef uint8_t wasmtime_valkind_t; +#define WASMTIME_I32 0 +#define WASMTIME_I64 1 +#define WASMTIME_F32 2 +#define WASMTIME_F64 3 +#define WASMTIME_V128 4 +#define WASMTIME_FUNCREF 5 +#define WASMTIME_EXTERNREF 6 typedef uint8_t wasmtime_v128[16]; diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs index 15809290d998..f531c567e975 100644 --- a/crates/c-api/src/extern.rs +++ b/crates/c-api/src/extern.rs @@ -104,6 +104,7 @@ pub const WASMTIME_EXTERN_MEMORY: wasmtime_extern_kind_t = 3; pub const WASMTIME_EXTERN_INSTANCE: wasmtime_extern_kind_t = 4; pub const WASMTIME_EXTERN_MODULE: wasmtime_extern_kind_t = 5; +#[repr(C)] pub union wasmtime_extern_union { pub func: Func, pub table: Table, diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 8a9d3edb28c1..4218d9aba1d0 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -199,7 +199,7 @@ pub struct wasmtime_caller_t<'a> { } #[no_mangle] -pub unsafe extern "C" fn wasmtime_func_new_with_env( +pub unsafe extern "C" fn wasmtime_func_new( store: CStoreContextMut<'_>, ty: &wasm_functype_t, callback: extern "C" fn( diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs index 89513103b7fc..b503d835c0d5 100644 --- a/crates/c-api/src/val.rs +++ b/crates/c-api/src/val.rs @@ -237,6 +237,7 @@ impl Drop for wasmtime_val_t { } } +#[no_mangle] pub unsafe extern "C" fn wasmtime_val_delete(val: &mut ManuallyDrop) { ManuallyDrop::drop(val) } diff --git a/crates/c-api/src/wat2wasm.rs b/crates/c-api/src/wat2wasm.rs index 4223da67da15..d008cc28a652 100644 --- a/crates/c-api/src/wat2wasm.rs +++ b/crates/c-api/src/wat2wasm.rs @@ -1,11 +1,13 @@ use crate::{bad_utf8, handle_result, wasm_byte_vec_t, wasmtime_error_t}; #[no_mangle] -pub extern "C" fn wasmtime_wat2wasm( - wat: &wasm_byte_vec_t, +pub unsafe extern "C" fn wasmtime_wat2wasm( + wat: *const u8, + wat_len: usize, ret: &mut wasm_byte_vec_t, ) -> Option> { - let wat = match std::str::from_utf8(wat.as_slice()) { + let wat = std::slice::from_raw_parts(wat, wat_len); + let wat = match std::str::from_utf8(wat) { Ok(s) => s, Err(_) => return bad_utf8(), }; diff --git a/examples/externref.c b/examples/externref.c index 725e2e0e2f4c..bf3611389a23 100644 --- a/examples/externref.c +++ b/examples/externref.c @@ -42,8 +42,9 @@ int main() { // With an engine we can create a *store* which is a long-lived group of wasm // modules. - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); + wasmtime_context_t *context = wasmtime_store_context(store); // Read our input file, which in this case is a wasm text file. FILE* file = fopen("examples/externref.wat", "r"); @@ -58,15 +59,15 @@ int main() { // Parse the wat into the binary wasm format wasm_byte_vec_t wasm; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); // Now that we've got our binary webassembly we can compile our module. printf("Compiling module...\n"); - wasm_module_t *module = NULL; - error = wasmtime_module_new(engine, &wasm, &module); + wasmtime_module_t *module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module); wasm_byte_vec_delete(&wasm); if (error != NULL) exit_with_error("failed to compile module", error, NULL); @@ -74,104 +75,99 @@ int main() { // Instantiate the module. printf("Instantiating module...\n"); wasm_trap_t *trap = NULL; - wasm_instance_t *instance = NULL; - wasm_extern_vec_t imports = WASM_EMPTY_VEC; - error = wasmtime_instance_new(store, module, &imports, &instance, &trap); - if (instance == NULL) + wasmtime_instance_t instance; + error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); printf("Creating new `externref`...\n"); // Create a new `externref` value. // - // Note that if you need clean up for after the externref is reclaimed, you - // can use `wasmtime_externref_new_with_finalizer`. - wasm_val_t externref; - wasmtime_externref_new("Hello, World!", &externref); - assert(externref.kind == WASM_ANYREF); + // Note that the NULL here is a finalizer callback, but we don't need one for + // this example. + wasmtime_externref_t *externref = wasmtime_externref_new("Hello, World!", NULL); // The `externref`'s wrapped data should be the string "Hello, World!". - void* data = NULL; - ok = wasmtime_externref_data(&externref, &data); - assert(ok); + void* data = wasmtime_externref_data(externref); assert(strcmp((char*)data, "Hello, World!") == 0); printf("Touching `externref` table...\n"); + wasmtime_extern_t item; + // Lookup the `table` export. - wasm_extern_vec_t externs; - wasm_instance_exports(instance, &externs); - assert(externs.size == 3); - wasm_table_t *table = wasm_extern_as_table(externs.data[0]); - assert(table != NULL); + ok = wasmtime_instance_export_get(context, instance, "table", strlen("table"), &item); + assert(ok); + assert(item.kind == WASMTIME_EXTERN_TABLE); // Set `table[3]` to our `externref`. - wasm_val_t elem; - wasm_val_copy(&elem, &externref); - assert(elem.kind == WASM_ANYREF); - ok = wasm_table_set(table, 3, elem.of.ref); - assert(ok); + wasmtime_val_t externref_val; + externref_val.kind = WASMTIME_EXTERNREF; + externref_val.of.externref = externref; + error = wasmtime_table_set(context, item.of.table, 3, &externref_val); + if (error != NULL) + exit_with_error("failed to set table", error, NULL); // `table[3]` should now be our `externref`. - wasm_ref_delete(elem.of.ref); - elem.of.ref = wasm_table_get(table, 3); - assert(elem.of.ref != NULL); - assert(wasm_ref_same(elem.of.ref, externref.of.ref)); + wasmtime_val_t elem; + ok = wasmtime_table_get(context, item.of.table, 3, &elem); + assert(ok); + assert(elem.kind == WASMTIME_EXTERNREF); + assert(strcmp((char*)wasmtime_externref_data(elem.of.externref), "Hello, World!") == 0); + wasmtime_val_delete(&elem); printf("Touching `externref` global...\n"); // Lookup the `global` export. - wasm_global_t *global = wasm_extern_as_global(externs.data[1]); - assert(global != NULL); + ok = wasmtime_instance_export_get(context, instance, "global", strlen("global"), &item); + assert(ok); + assert(item.kind == WASMTIME_EXTERN_GLOBAL); // Set the global to our `externref`. - wasm_global_set(global, &externref); + error = wasmtime_global_set(context, item.of.global, &externref_val); + if (error != NULL) + exit_with_error("failed to set global", error, NULL); // Get the global, and it should return our `externref` again. - wasm_val_t global_val; - wasm_global_get(global, &global_val); - assert(global_val.kind == WASM_ANYREF); - assert(wasm_ref_same(global_val.of.ref, externref.of.ref)); + wasmtime_val_t global_val; + wasmtime_global_get(context, item.of.global, &global_val); + assert(global_val.kind == WASMTIME_EXTERNREF); + assert(strcmp((char*)wasmtime_externref_data(elem.of.externref), "Hello, World!") == 0); + wasmtime_val_delete(&global_val); printf("Calling `externref` func...\n"); // Lookup the `func` export. - wasm_func_t *func = wasm_extern_as_func(externs.data[2]); - assert(func != NULL); + ok = wasmtime_instance_export_get(context, instance, "func", strlen("func"), &item); + assert(ok); + assert(item.kind == WASMTIME_EXTERN_FUNC); // And call it! - wasm_val_t args[1] = { externref }; - wasm_val_t results[1]; - wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); - wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results); - error = wasmtime_func_call(func, &args_vec, &results_vec, &trap); + wasmtime_val_t results[1]; + error = wasmtime_func_call(context, item.of.func, &externref_val, 1, results, 1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); // `func` returns the same reference we gave it, so `results[0]` should be our // `externref`. - assert(results[0].kind == WASM_ANYREF); - assert(wasm_ref_same(results[0].of.ref, externref.of.ref)); + assert(results[0].kind == WASMTIME_EXTERNREF); + assert(strcmp((char*)wasmtime_externref_data(results[0].of.externref), "Hello, World!") == 0); + wasmtime_val_delete(&results[0]); // We can GC any now-unused references to our externref that the store is // holding. printf("GCing within the store...\n"); - wasmtime_store_gc(store); + wasmtime_context_gc(context); // Clean up after ourselves at this point printf("All finished!\n"); ret = 0; - wasm_val_delete(&results[0]); - wasm_val_delete(&global_val); - wasm_val_delete(&elem); - wasm_extern_vec_delete(&externs); - wasm_val_delete(&externref); - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); + wasmtime_store_delete(store); + wasmtime_module_delete(module); wasm_engine_delete(engine); - return ret; + return 0; } static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) { diff --git a/examples/fib-debug/main.c b/examples/fib-debug/main.c index 722ddb01d920..a8cd38879d76 100644 --- a/examples/fib-debug/main.c +++ b/examples/fib-debug/main.c @@ -2,9 +2,8 @@ #include #include #include - #include -#include "wasmtime.h" +#include #define own @@ -20,7 +19,8 @@ int main(int argc, const char* argv[]) { // Initialize. printf("Initializing...\n"); wasm_engine_t* engine = wasm_engine_new_with_config(config); - wasm_store_t* store = wasm_store_new(engine); + wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL); + wasmtime_context_t* context = wasmtime_store_context(store); // Load binary. printf("Loading binary...\n"); @@ -42,75 +42,42 @@ int main(int argc, const char* argv[]) { // Compile. printf("Compiling module...\n"); - wasm_module_t *module = NULL; - wasmtime_error_t* error = wasmtime_module_new(engine, &binary, &module); + wasmtime_module_t *module = NULL; + wasmtime_error_t* error = wasmtime_module_new(engine, (uint8_t*) binary.data, binary.size, &module); if (!module) exit_with_error("failed to compile module", error, NULL); wasm_byte_vec_delete(&binary); - // Figure out which export is the `fib` export - wasm_exporttype_vec_t module_exports; - wasm_module_exports(module, &module_exports); - int fib_idx = -1; - for (int i = 0; i < module_exports.size; i++) { - const wasm_name_t *name = wasm_exporttype_name(module_exports.data[i]); - if (name->size != 3) - continue; - if (strncmp("fib", name->data, 3) != 0) - continue; - fib_idx = i; - break; - } - wasm_exporttype_vec_delete(&module_exports); - if (fib_idx == -1) { - printf("> Error finding `fib` export!\n"); - return 1; - } - // Instantiate. printf("Instantiating module...\n"); - wasm_instance_t* instance = NULL; + wasmtime_instance_t instance; wasm_trap_t *trap = NULL; - wasm_extern_vec_t imports = WASM_EMPTY_VEC; - error = wasmtime_instance_new(store, module, &imports, &instance, &trap); + error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); - wasm_module_delete(module); + wasmtime_module_delete(module); // Extract export. - printf("Extracting export...\n"); - own wasm_extern_vec_t exports; - wasm_instance_exports(instance, &exports); - if (exports.size == 0) { - printf("> Error accessing exports!\n"); - return 1; - } - // Getting second export (first is memory). - wasm_func_t* run_func = wasm_extern_as_func(exports.data[fib_idx]); - if (run_func == NULL) { - printf("> Error accessing export!\n"); - return 1; - } - - wasm_instance_delete(instance); + wasmtime_extern_t fib; + bool ok = wasmtime_instance_export_get(context, instance, "fib", 3, &fib); + assert(ok); // Call. printf("Calling fib...\n"); - wasm_val_t params[1] = { WASM_I32_VAL(6) }; - wasm_val_t results[1]; - wasm_val_vec_t params_vec = WASM_ARRAY_VEC(params); - wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results); - error = wasmtime_func_call(run_func, ¶ms_vec, &results_vec, &trap); + wasmtime_val_t params[1]; + params[0].kind = WASMTIME_I32; + params[0].of.i32 = 6; + wasmtime_val_t results[1]; + error = wasmtime_func_call(context, fib.of.func, params, 1, results, 1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); - wasm_extern_vec_delete(&exports); - + assert(results[0].kind == WASMTIME_I32); printf("> fib(6) = %d\n", results[0].of.i32); // Shut down. printf("Shutting down...\n"); - wasm_store_delete(store); + wasmtime_store_delete(store); wasm_engine_delete(engine); // All done. diff --git a/examples/fuel.c b/examples/fuel.c index af3b51782dc9..e45368d3b163 100644 --- a/examples/fuel.c +++ b/examples/fuel.c @@ -35,9 +35,11 @@ int main() { // Create an *engine*, which is a compilation context, with our configured options. wasm_engine_t *engine = wasm_engine_new_with_config(config); assert(engine != NULL); - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); - error = wasmtime_store_add_fuel(store, 10000); + wasmtime_context_t *context = wasmtime_store_context(store); + + error = wasmtime_context_add_fuel(context, 10000); if (error != NULL) exit_with_error("failed to add fuel", error, NULL); @@ -60,60 +62,57 @@ int main() { // Parse the wat into the binary wasm format wasm_byte_vec_t wasm; - error = wasmtime_wat2wasm(&wat, &wasm); + error = wasmtime_wat2wasm(wat.data, wat.size, &wasm); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); // Compile and instantiate our module - wasm_module_t *module = NULL; - error = wasmtime_module_new(engine, &wasm, &module); + wasmtime_module_t *module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module); if (module == NULL) exit_with_error("failed to compile module", error, NULL); wasm_byte_vec_delete(&wasm); + wasm_trap_t *trap = NULL; - wasm_instance_t *instance = NULL; - wasm_extern_vec_t imports = WASM_EMPTY_VEC; - error = wasmtime_instance_new(store, module, &imports, &instance, &trap); - if (instance == NULL) + wasmtime_instance_t instance; + error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); // Lookup our `fibonacci` export function - wasm_extern_vec_t externs; - wasm_instance_exports(instance, &externs); - assert(externs.size == 1); - wasm_func_t *fibonacci = wasm_extern_as_func(externs.data[0]); - assert(fibonacci != NULL); + wasmtime_extern_t fib; + bool ok = wasmtime_instance_export_get(context, instance, "fibonacci", strlen("fibonacci"), &fib); + assert(ok); + assert(fib.kind == WASMTIME_EXTERN_FUNC); // Call it repeatedly until it fails for (int n = 1; ; n++) { uint64_t fuel_before; - wasmtime_store_fuel_consumed(store, &fuel_before); - wasm_val_t params[1] = { WASM_I32_VAL(n) }; - wasm_val_t results[1]; - wasm_val_vec_t params_vec = WASM_ARRAY_VEC(params); - wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results); - error = wasmtime_func_call(fibonacci, ¶ms_vec, &results_vec, &trap); + wasmtime_context_fuel_consumed(context, &fuel_before); + wasmtime_val_t params[1]; + params[0].kind = WASMTIME_I32; + params[0].of.i32 = n; + wasmtime_val_t results[1]; + error = wasmtime_func_call(context, fib.of.func, params, 1, results, 1, &trap); if (error != NULL || trap != NULL) { printf("Exhausted fuel computing fib(%d)\n", n); break; } uint64_t fuel_after; - wasmtime_store_fuel_consumed(store, &fuel_after); - assert(results[0].kind == WASM_I32); + wasmtime_context_fuel_consumed(context, &fuel_after); + assert(results[0].kind == WASMTIME_I32); printf("fib(%d) = %d [consumed %lld fuel]\n", n, results[0].of.i32, fuel_after - fuel_before); - error = wasmtime_store_add_fuel(store, fuel_after - fuel_before); + error = wasmtime_context_add_fuel(context, fuel_after - fuel_before); if (error != NULL) exit_with_error("failed to add fuel", error, NULL); } // Clean up after ourselves at this point - wasm_extern_vec_delete(&externs); - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); + wasmtime_module_delete(module); + wasmtime_store_delete(store); wasm_engine_delete(engine); return 0; } diff --git a/examples/gcd.c b/examples/gcd.c index d811458bb422..eccdc3acab0b 100644 --- a/examples/gcd.c +++ b/examples/gcd.c @@ -30,8 +30,9 @@ int main() { // Set up our context wasm_engine_t *engine = wasm_engine_new(); assert(engine != NULL); - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); + wasmtime_context_t *context = wasmtime_store_context(store); // Load our input file to parse it next FILE* file = fopen("examples/gcd.wat", "r"); @@ -52,52 +53,51 @@ int main() { // Parse the wat into the binary wasm format wasm_byte_vec_t wasm; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); // Compile and instantiate our module - wasm_module_t *module = NULL; - error = wasmtime_module_new(engine, &wasm, &module); + wasmtime_module_t *module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module); if (module == NULL) exit_with_error("failed to compile module", error, NULL); wasm_byte_vec_delete(&wasm); + wasm_trap_t *trap = NULL; - wasm_instance_t *instance = NULL; - wasm_extern_vec_t imports = WASM_EMPTY_VEC; - error = wasmtime_instance_new(store, module, &imports, &instance, &trap); - if (instance == NULL) + wasmtime_instance_t instance; + error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); // Lookup our `gcd` export function - wasm_extern_vec_t externs; - wasm_instance_exports(instance, &externs); - assert(externs.size == 1); - wasm_func_t *gcd = wasm_extern_as_func(externs.data[0]); - assert(gcd != NULL); + wasmtime_extern_t gcd; + bool ok = wasmtime_instance_export_get(context, instance, "gcd", 3, &gcd); + assert(ok); + assert(gcd.kind == WASMTIME_EXTERN_FUNC); // And call it! int a = 6; int b = 27; - wasm_val_t params[2] = { WASM_I32_VAL(a), WASM_I32_VAL(b) }; - wasm_val_t results[1]; - wasm_val_vec_t params_vec = WASM_ARRAY_VEC(params); - wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results); - error = wasmtime_func_call(gcd, ¶ms_vec, &results_vec, &trap); + wasmtime_val_t params[2]; + params[0].kind = WASMTIME_I32; + params[0].of.i32 = a; + params[1].kind = WASMTIME_I32; + params[1].of.i32 = b; + wasmtime_val_t results[1]; + error = wasmtime_func_call(context, gcd.of.func, params, 2, results, 1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call gcd", error, trap); - assert(results[0].kind == WASM_I32); + assert(results[0].kind == WASMTIME_I32); printf("gcd(%d, %d) = %d\n", a, b, results[0].of.i32); // Clean up after ourselves at this point ret = 0; - wasm_extern_vec_delete(&externs); - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); + wasmtime_module_delete(module); + wasmtime_store_delete(store); wasm_engine_delete(engine); return ret; } diff --git a/examples/hello.c b/examples/hello.c index 7c14612945e1..ba1411a82928 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -26,7 +26,14 @@ to tweak the `-lpthread` and such annotations as well as the name of the static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); -static wasm_trap_t* hello_callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) { +static wasm_trap_t* hello_callback( + void *env, + wasmtime_caller_t *caller, + const wasmtime_val_t *args, + size_t nargs, + wasmtime_val_t *results, + size_t nresults +) { printf("Calling back...\n"); printf("> Hello World!\n"); return NULL; @@ -42,9 +49,11 @@ int main() { assert(engine != NULL); // With an engine we can create a *store* which is a long-lived group of wasm - // modules. - wasm_store_t *store = wasm_store_new(engine); + // modules. Note that we allocate some custom data here to live in the store, + // but here we skip that and specify NULL. + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); + wasmtime_context_t *context = wasmtime_store_context(store); // Read our input file, which in this case is a wasm text file. FILE* file = fopen("examples/hello.wat", "r"); @@ -59,25 +68,26 @@ int main() { // Parse the wat into the binary wasm format wasm_byte_vec_t wasm; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); // Now that we've got our binary webassembly we can compile our module. printf("Compiling module...\n"); - wasm_module_t *module = NULL; - error = wasmtime_module_new(engine, &wasm, &module); + wasmtime_module_t *module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module); wasm_byte_vec_delete(&wasm); if (error != NULL) exit_with_error("failed to compile module", error, NULL); // Next up we need to create the function that the wasm module imports. Here // we'll be hooking up a thunk function to the `hello_callback` native - // function above. + // function above. Note that we can assign custom data, but we just use NULL + // for now). printf("Creating callback...\n"); wasm_functype_t *hello_ty = wasm_functype_new_0_0(); - wasm_func_t *hello = wasm_func_new(store, hello_ty, hello_callback); + wasmtime_func_t hello = wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL); // With our callback function we can now instantiate the compiled module, // giving us an instance we can then execute exports from. Note that @@ -85,26 +95,24 @@ int main() { // to handle that here too. printf("Instantiating module...\n"); wasm_trap_t *trap = NULL; - wasm_instance_t *instance = NULL; - wasm_extern_t* imports[] = { wasm_func_as_extern(hello) }; - wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports); - error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap); - if (instance == NULL) + wasmtime_instance_t instance; + wasmtime_extern_t import; + import.kind = WASMTIME_EXTERN_FUNC; + import.of.func = hello; + error = wasmtime_instance_new(context, module, &import, 1, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); // Lookup our `run` export function printf("Extracting export...\n"); - wasm_extern_vec_t externs; - wasm_instance_exports(instance, &externs); - assert(externs.size == 1); - wasm_func_t *run = wasm_extern_as_func(externs.data[0]); - assert(run != NULL); + wasmtime_extern_t run; + bool ok = wasmtime_instance_export_get(context, instance, "run", 3, &run); + assert(ok); + assert(run.kind == WASMTIME_EXTERN_FUNC); // And call it! printf("Calling export...\n"); - wasm_val_vec_t args_vec = WASM_EMPTY_VEC; - wasm_val_vec_t results_vec = WASM_EMPTY_VEC; - error = wasmtime_func_call(run, &args_vec, &results_vec, &trap); + error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); @@ -112,10 +120,8 @@ int main() { printf("All finished!\n"); ret = 0; - wasm_extern_vec_delete(&externs); - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); + wasmtime_module_delete(module); + wasmtime_store_delete(store); wasm_engine_delete(engine); return ret; } diff --git a/examples/interrupt.c b/examples/interrupt.c index e6540a2d8dd7..69f4ca565eb7 100644 --- a/examples/interrupt.c +++ b/examples/interrupt.c @@ -61,11 +61,12 @@ int main() { wasmtime_config_interruptable_set(config, true); wasm_engine_t *engine = wasm_engine_new_with_config(config); assert(engine != NULL); - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); + wasmtime_context_t *context = wasmtime_store_context(store); // Create our interrupt handle we'll use later - wasmtime_interrupt_handle_t *handle = wasmtime_interrupt_handle_new(store); + wasmtime_interrupt_handle_t *handle = wasmtime_interrupt_handle_new(context); assert(handle != NULL); // Read our input file, which in this case is a wasm text file. @@ -81,50 +82,45 @@ int main() { // Parse the wat into the binary wasm format wasm_byte_vec_t wasm; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); // Now that we've got our binary webassembly we can compile our module. - wasm_module_t *module = NULL; - wasm_trap_t *trap = NULL; - wasm_instance_t *instance = NULL; - wasm_extern_vec_t imports = WASM_EMPTY_VEC; - error = wasmtime_module_new(engine, &wasm, &module); + wasmtime_module_t *module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module); wasm_byte_vec_delete(&wasm); if (error != NULL) exit_with_error("failed to compile module", error, NULL); - error = wasmtime_instance_new(store, module, &imports, &instance, &trap); - if (instance == NULL) + + wasm_trap_t *trap = NULL; + wasmtime_instance_t instance; + error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); + wasmtime_module_delete(module); // Lookup our `run` export function - wasm_extern_vec_t externs; - wasm_instance_exports(instance, &externs); - assert(externs.size == 1); - wasm_func_t *run = wasm_extern_as_func(externs.data[0]); - assert(run != NULL); + wasmtime_extern_t run; + bool ok = wasmtime_instance_export_get(context, instance, "run", 3, &run); + assert(ok); + assert(run.kind == WASMTIME_EXTERN_FUNC); // Spawn a thread to send us an interrupt after a period of time. spawn_interrupt(handle); // And call it! printf("Entering infinite loop...\n"); - wasm_val_vec_t args_vec = WASM_EMPTY_VEC; - wasm_val_vec_t results_vec = WASM_EMPTY_VEC; - error = wasmtime_func_call(run, &args_vec, &results_vec, &trap); + error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); assert(error == NULL); assert(trap != NULL); printf("Got a trap!...\n"); // `trap` can be inspected here to see the trap message has an interrupt in it - wasm_trap_delete(trap); - wasm_extern_vec_delete(&externs); - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); + + wasmtime_store_delete(store); wasm_engine_delete(engine); return 0; } From 2a5b3d48694f7617e1be698a0a0980bd24e59c54 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 14:57:39 -0700 Subject: [PATCH 10/90] More misc test fixes --- crates/fuzzing/src/oracles/dummy.rs | 40 ++++++++++--------- .../tests/wasm_tests/runtime/cap_std_sync.rs | 23 +++++------ .../tests/wasm_tests/runtime/tokio.rs | 22 +++++----- 3 files changed, 43 insertions(+), 42 deletions(-) diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs index 7a3099ff031d..00be85d0d747 100644 --- a/crates/fuzzing/src/oracles/dummy.rs +++ b/crates/fuzzing/src/oracles/dummy.rs @@ -388,43 +388,47 @@ mod tests { #[test] fn dummy_table_import() { - let store = store(); + let mut store = store(); let table = dummy_table( - &store, + &mut store, TableType::new(ValType::ExternRef, Limits::at_least(10)), ); - assert_eq!(table.size(), 10); + assert_eq!(table.size(&store), 10); for i in 0..10 { - assert!(table.get(i).unwrap().unwrap_externref().is_none()); + assert!(table + .get(&mut store, i) + .unwrap() + .unwrap_externref() + .is_none()); } } #[test] fn dummy_global_import() { - let store = store(); - let global = dummy_global(&store, GlobalType::new(ValType::I32, Mutability::Const)); - assert_eq!(global.val_type(), ValType::I32); - assert_eq!(global.mutability(), Mutability::Const); + let mut store = store(); + let global = dummy_global(&mut store, GlobalType::new(ValType::I32, Mutability::Const)); + assert_eq!(*global.ty(&store).content(), ValType::I32); + assert_eq!(global.ty(&store).mutability(), Mutability::Const); } #[test] fn dummy_memory_import() { - let store = store(); - let memory = dummy_memory(&store, MemoryType::new(Limits::at_least(1))); - assert_eq!(memory.size(), 1); + let mut store = store(); + let memory = dummy_memory(&mut store, MemoryType::new(Limits::at_least(1))); + assert_eq!(memory.size(&store), 1); } #[test] fn dummy_function_import() { - let store = store(); + let mut store = store(); let func_ty = FuncType::new(vec![ValType::I32], vec![ValType::I64]); - let func = dummy_func(&store, func_ty.clone()); - assert_eq!(func.ty(), func_ty); + let func = dummy_func(&mut store, func_ty.clone()); + assert_eq!(func.ty(&store), func_ty); } #[test] fn dummy_instance_import() { - let store = store(); + let mut store = store(); let mut instance_ty = InstanceType::new(); @@ -464,7 +468,7 @@ mod tests { instance_ty.add_named_export("instance0", InstanceType::new().into()); instance_ty.add_named_export("instance1", InstanceType::new().into()); - let instance = dummy_instance(&store, instance_ty.clone()); + let instance = dummy_instance(&mut store, instance_ty.clone()); let mut expected_exports = vec![ "func0", @@ -482,7 +486,7 @@ mod tests { ] .into_iter() .collect::>(); - for exp in instance.ty().exports() { + for exp in instance.ty(&store).exports() { let was_expected = expected_exports.remove(exp.name()); assert!(was_expected); } @@ -564,7 +568,7 @@ mod tests { module_ty.add_named_import("instance1", None, InstanceType::new().into()); // Create the module. - let module = dummy_module(&store, module_ty); + let module = dummy_module(store.engine(), module_ty); // Check that we have the expected exports. assert!(module.get_export("func0").is_some()); diff --git a/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs b/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs index 32aa3cbedb6c..a802d25a7a0d 100644 --- a/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs +++ b/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs @@ -1,8 +1,8 @@ use anyhow::Context; use std::path::Path; use wasi_common::pipe::WritePipe; -use wasmtime::{Linker, Module, Store}; -use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder}; +use wasmtime::{Engine, Linker, Module, Store}; +use wasmtime_wasi::sync::{add_to_linker, WasiCtxBuilder}; pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> { run(data, bin_name, workspace, false) @@ -25,7 +25,10 @@ fn run( let stderr = WritePipe::new_in_memory(); let r = { - let store = Store::default(); + let engine = Engine::default(); + let module = Module::new(&engine, &data).context("failed to create wasm module")?; + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker)?; // Create our wasi context. // Additionally register any preopened directories if we have them. @@ -53,16 +56,10 @@ fn run( // cap-std-sync does not yet support the sync family of fdflags builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?; - let wasi = Wasi::new(&store, builder.build()?); - - let mut linker = Linker::new(&store); - - wasi.add_to_linker(&mut linker)?; - - let module = Module::new(store.engine(), &data).context("failed to create wasm module")?; - let instance = linker.instantiate(&module)?; - let start = instance.get_typed_func::<(), ()>("_start")?; - start.call(()).map_err(anyhow::Error::from) + let mut store = Store::new(&engine, builder.build()?); + let instance = linker.instantiate(&mut store, &module)?; + let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?; + start.call(&mut store, ()).map_err(anyhow::Error::from) }; match r { diff --git a/crates/test-programs/tests/wasm_tests/runtime/tokio.rs b/crates/test-programs/tests/wasm_tests/runtime/tokio.rs index 28577015bcb0..994e80bad007 100644 --- a/crates/test-programs/tests/wasm_tests/runtime/tokio.rs +++ b/crates/test-programs/tests/wasm_tests/runtime/tokio.rs @@ -2,7 +2,7 @@ use anyhow::Context; use std::path::Path; use wasi_common::pipe::WritePipe; use wasmtime::{Config, Engine, Linker, Module, Store}; -use wasmtime_wasi::tokio::{Wasi, WasiCtxBuilder}; +use wasmtime_wasi::tokio::{add_to_linker, WasiCtxBuilder}; pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> { run(data, bin_name, workspace, false) @@ -31,9 +31,10 @@ fn run( .block_on(async move { let mut config = Config::new(); config.async_support(true); - Wasi::add_to_config(&mut config); let engine = Engine::new(&config)?; - let store = Store::new(&engine); + let module = Module::new(&engine, &data).context("failed to create wasm module")?; + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker)?; // Create our wasi context. let mut builder = WasiCtxBuilder::new(); @@ -62,15 +63,14 @@ fn run( // does not. builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?; - Wasi::set_context(&store, builder.build()?) - .map_err(|_| anyhow::anyhow!("wasi set_context failed"))?; + let mut store = Store::new(&engine, builder.build()?); - let module = - Module::new(store.engine(), &data).context("failed to create wasm module")?; - let linker = Linker::new(&store); - let instance = linker.instantiate_async(&module).await?; - let start = instance.get_typed_func::<(), ()>("_start")?; - start.call_async(()).await.map_err(anyhow::Error::from) + let instance = linker.instantiate_async(&mut store, &module).await?; + let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?; + start + .call_async(&mut store, ()) + .await + .map_err(anyhow::Error::from) }); match r { From 7f636b0e2072bc2b6f8aeac60962277d3e034a58 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 15:03:11 -0700 Subject: [PATCH 11/90] doctest fixes --- crates/wasi-common/src/pipe.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/wasi-common/src/pipe.rs b/crates/wasi-common/src/pipe.rs index ca83e2d5cd2a..d4281d8329b6 100644 --- a/crates/wasi-common/src/pipe.rs +++ b/crates/wasi-common/src/pipe.rs @@ -23,15 +23,13 @@ use std::sync::{Arc, RwLock}; /// A variety of `From` impls are provided so that common pipe types are easy to create. For example: /// /// ```no_run -/// # use std::rc::Rc; -/// # use std::cell::RefCell; /// use wasi_common::{pipe::ReadPipe, WasiCtx, Table}; /// let stdin = ReadPipe::from("hello from stdin!"); /// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync): /// let random = todo!(); /// let clocks = todo!(); /// let sched = todo!(); -/// let table = Rc::new(RefCell::new(Table::new())); +/// let table = Table::new(); /// let ctx = WasiCtx::builder(random, clocks, sched, table) /// .stdin(Box::new(stdin.clone())) /// .build(); @@ -194,15 +192,13 @@ impl WasiFile for ReadPipe { /// A virtual pipe write end. /// /// ```no_run -/// # use std::rc::Rc; -/// # use std::cell::RefCell; /// use wasi_common::{pipe::WritePipe, WasiCtx, Table}; /// let stdout = WritePipe::new_in_memory(); /// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync): /// let random = todo!(); /// let clocks = todo!(); /// let sched = todo!(); -/// let table = Rc::new(RefCell::new(Table::new())); +/// let table = Table::new(); /// let ctx = WasiCtx::builder(random, clocks, sched, table) /// .stdout(Box::new(stdout.clone())) /// .build(); From 49cd22506e34be9e7955e5a1b711d10aeb769f8f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 16:53:00 -0700 Subject: [PATCH 12/90] More C working --- crates/c-api/include/wasmtime/memory.h | 4 +- crates/c-api/src/func.rs | 6 +- crates/c-api/src/instance.rs | 4 +- crates/c-api/src/lib.rs | 16 ++ crates/c-api/src/linker.rs | 2 +- crates/c-api/src/module.rs | 6 +- crates/c-api/src/wat2wasm.rs | 2 +- examples/memory.c | 233 +++++++++++++------------ examples/serialize.c | 56 +++--- 9 files changed, 176 insertions(+), 153 deletions(-) diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h index 87674b06d9c5..b23cf04c65e4 100644 --- a/crates/c-api/include/wasmtime/memory.h +++ b/crates/c-api/include/wasmtime/memory.h @@ -25,11 +25,11 @@ WASM_API_EXTERN uint8_t *wasmtime_memory_data( const wasmtime_context_t *store, wasmtime_memory_t memory ); -WASM_API_EXTERN size_t *wasmtime_memory_data_size( +WASM_API_EXTERN size_t wasmtime_memory_data_size( const wasmtime_context_t *store, wasmtime_memory_t memory ); -WASM_API_EXTERN uint32_t *wasmtime_memory_size( +WASM_API_EXTERN uint32_t wasmtime_memory_size( const wasmtime_context_t *store, wasmtime_memory_t memory ); diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 4218d9aba1d0..27e851118a54 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -262,7 +262,7 @@ pub unsafe extern "C" fn wasmtime_func_call( "wrong number of results provided" )))); } - let params = std::slice::from_raw_parts(args, nargs) + let params = crate::slice_from_raw_parts(args, nargs) .iter() .map(|i| i.to_val()) .collect::>(); @@ -274,7 +274,7 @@ pub unsafe extern "C" fn wasmtime_func_call( let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(store, ¶ms))); match result { Ok(Ok(out)) => { - let results = std::slice::from_raw_parts_mut(results, nresults); + let results = crate::slice_from_raw_parts_mut(results, nresults); for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) { crate::initialize(slot, wasmtime_val_t::from_val(val)); } @@ -320,7 +320,7 @@ pub unsafe extern "C" fn wasmtime_caller_export_get( name_len: usize, item: &mut MaybeUninit, ) -> bool { - let name = match str::from_utf8(std::slice::from_raw_parts(name, name_len)) { + let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) { Ok(name) => name, Err(_) => return false, }; diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs index d443ee4971f1..e1bbf064e21d 100644 --- a/crates/c-api/src/instance.rs +++ b/crates/c-api/src/instance.rs @@ -102,7 +102,7 @@ pub unsafe extern "C" fn wasmtime_instance_new( instance: &mut Instance, trap_ptr: &mut *mut wasm_trap_t, ) -> Option> { - let imports = std::slice::from_raw_parts(imports, nimports) + let imports = crate::slice_from_raw_parts(imports, nimports) .iter() .map(|i| i.to_extern()) .collect::>(); @@ -149,7 +149,7 @@ pub unsafe extern "C" fn wasmtime_instance_export_get( name_len: usize, item: &mut MaybeUninit, ) -> bool { - let name = std::slice::from_raw_parts(name, name_len); + let name = crate::slice_from_raw_parts(name, name_len); let name = match std::str::from_utf8(name) { Ok(name) => name, Err(_) => return false, diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 6ddb1660431c..c4b189418d3e 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -74,3 +74,19 @@ impl Drop for ForeignData { } } } + +unsafe fn slice_from_raw_parts<'a, T>(ptr: *const T, len: usize) -> &'a [T] { + if len == 0 { + &[] + } else { + std::slice::from_raw_parts(ptr, len) + } +} + +unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a [T] { + if len == 0 { + &[] + } else { + std::slice::from_raw_parts_mut(ptr, len) + } +} diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 7fbf141c2412..82dfdcfc4ba7 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -31,7 +31,7 @@ pub extern "C" fn wasmtime_linker_delete(_linker: Box) {} macro_rules! to_str { ($ptr:expr, $len:expr) => { - match str::from_utf8(std::slice::from_raw_parts($ptr, $len)) { + match str::from_utf8(crate::slice_from_raw_parts($ptr, $len)) { Ok(s) => s, Err(_) => return bad_utf8(), } diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 6c34056883ea..10397a16bb09 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -151,7 +151,7 @@ pub unsafe extern "C" fn wasmtime_module_new( out: &mut *mut wasmtime_module_t, ) -> Option> { handle_result( - Module::from_binary(&engine.engine, std::slice::from_raw_parts(wasm, len)), + Module::from_binary(&engine.engine, crate::slice_from_raw_parts(wasm, len)), |module| { *out = Box::into_raw(Box::new(wasmtime_module_t { module })); }, @@ -167,7 +167,7 @@ pub unsafe extern "C" fn wasmtime_module_validate( wasm: *const u8, len: usize, ) -> Option> { - let binary = std::slice::from_raw_parts(wasm, len); + let binary = crate::slice_from_raw_parts(wasm, len); handle_result(Module::validate(&engine.engine, binary), |()| {}) } @@ -191,7 +191,7 @@ pub unsafe extern "C" fn wasmtime_module_deserialize( len: usize, out: &mut *mut wasmtime_module_t, ) -> Option> { - let bytes = std::slice::from_raw_parts(bytes, len); + let bytes = crate::slice_from_raw_parts(bytes, len); handle_result(Module::deserialize(&engine.engine, bytes), |module| { *out = Box::into_raw(Box::new(wasmtime_module_t { module })); }) diff --git a/crates/c-api/src/wat2wasm.rs b/crates/c-api/src/wat2wasm.rs index d008cc28a652..3d0b7ae5ab1f 100644 --- a/crates/c-api/src/wat2wasm.rs +++ b/crates/c-api/src/wat2wasm.rs @@ -6,7 +6,7 @@ pub unsafe extern "C" fn wasmtime_wat2wasm( wat_len: usize, ret: &mut wasm_byte_vec_t, ) -> Option> { - let wat = std::slice::from_raw_parts(wat, wat_len); + let wat = crate::slice_from_raw_parts(wat, wat_len); let wat = match std::str::from_utf8(wat) { Ok(s) => s, Err(_) => return bad_utf8(), diff --git a/examples/memory.c b/examples/memory.c index 24e26dbd4c0d..2fd906ca5dde 100644 --- a/examples/memory.c +++ b/examples/memory.c @@ -30,23 +30,6 @@ originally static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); -wasm_memory_t* get_export_memory(const wasm_extern_vec_t* exports, size_t i) { - if (exports->size <= i || !wasm_extern_as_memory(exports->data[i])) { - printf("> Error accessing memory export %zu!\n", i); - exit(1); - } - return wasm_extern_as_memory(exports->data[i]); -} - -wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { - if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { - printf("> Error accessing function export %zu!\n", i); - exit(1); - } - return wasm_extern_as_func(exports->data[i]); -} - - void check(bool success) { if (!success) { printf("> Error, expected success\n"); @@ -54,11 +37,16 @@ void check(bool success) { } } -void check_call(wasm_func_t* func, const wasm_val_vec_t* args_vec, int32_t expected) { - wasm_val_t results[1]; - wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results); +void check_call(wasmtime_context_t *store, + wasmtime_func_t func, + const wasmtime_val_t* args, + size_t nargs, + int32_t expected) { + wasmtime_val_t results[1]; wasm_trap_t *trap = NULL; - wasmtime_error_t *error = wasmtime_func_call(func, args_vec, &results_vec, &trap); + wasmtime_error_t *error = wasmtime_func_call( + store, func, args, nargs, results, 1, &trap + ); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); if (results[0].of.i32 != expected) { @@ -67,45 +55,51 @@ void check_call(wasm_func_t* func, const wasm_val_vec_t* args_vec, int32_t expec } } -void check_call0(wasm_func_t* func, int32_t expected) { - wasm_val_vec_t args_vec = WASM_EMPTY_VEC; - check_call(func, &args_vec, expected); +void check_call0(wasmtime_context_t *store, wasmtime_func_t func, int32_t expected) { + check_call(store, func, NULL, 0, expected); } -void check_call1(wasm_func_t* func, int32_t arg, int32_t expected) { - wasm_val_t args[] = { WASM_I32_VAL(arg) }; - wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); - check_call(func, &args_vec, expected); +void check_call1(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg, int32_t expected) { + wasmtime_val_t args[1]; + args[0].kind = WASMTIME_I32; + args[0].of.i32 = arg; + check_call(store, func, args, 1, expected); } -void check_call2(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) { - wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; - wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); - check_call(func, &args_vec, expected); +void check_call2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, int32_t arg2, int32_t expected) { + wasmtime_val_t args[2]; + args[0].kind = WASMTIME_I32; + args[0].of.i32 = arg1; + args[1].kind = WASMTIME_I32; + args[1].of.i32 = arg2; + check_call(store, func, args, 2, expected); } -void check_ok(wasm_func_t* func, const wasm_val_vec_t* args_vec) { +void check_ok(wasmtime_context_t *store, wasmtime_func_t func, const wasmtime_val_t* args, size_t nargs) { wasm_trap_t *trap = NULL; - wasm_val_vec_t results_vec = WASM_EMPTY_VEC; - wasmtime_error_t *error = wasmtime_func_call(func, args_vec, &results_vec, &trap); + wasmtime_error_t *error = wasmtime_func_call(store, func, args, nargs, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); } -void check_ok2(wasm_func_t* func, int32_t arg1, int32_t arg2) { - wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; - wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); - check_ok(func, &args_vec); +void check_ok2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, int32_t arg2) { + wasmtime_val_t args[2]; + args[0].kind = WASMTIME_I32; + args[0].of.i32 = arg1; + args[1].kind = WASMTIME_I32; + args[1].of.i32 = arg2; + check_ok(store, func, args, 2); } -void check_trap(wasm_func_t* func, const wasm_val_vec_t* args_vec, size_t num_results) { +void check_trap(wasmtime_context_t *store, + wasmtime_func_t func, + const wasmtime_val_t *args, + size_t nargs, + size_t num_results) { assert(num_results <= 1); - wasm_val_t results[1]; - wasm_val_vec_t results_vec; - results_vec.data = results; - results_vec.size = num_results; + wasmtime_val_t results[1]; wasm_trap_t *trap = NULL; - wasmtime_error_t *error = wasmtime_func_call(func, args_vec, &results_vec, &trap); + wasmtime_error_t *error = wasmtime_func_call(store, func, args, nargs, results, num_results, &trap); if (error != NULL) exit_with_error("failed to call function", error, NULL); if (trap == NULL) { @@ -115,23 +109,28 @@ void check_trap(wasm_func_t* func, const wasm_val_vec_t* args_vec, size_t num_re wasm_trap_delete(trap); } -void check_trap1(wasm_func_t* func, int32_t arg) { - wasm_val_t args[] = { WASM_I32_VAL(arg) }; - wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); - check_trap(func, &args_vec, 1); +void check_trap1(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg) { + wasmtime_val_t args[1]; + args[0].kind = WASMTIME_I32; + args[0].of.i32 = arg; + check_trap(store, func, args, 1, 1); } -void check_trap2(wasm_func_t* func, int32_t arg1, int32_t arg2) { - wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; - wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); - check_trap(func, &args_vec, 0); +void check_trap2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, int32_t arg2) { + wasmtime_val_t args[2]; + args[0].kind = WASMTIME_I32; + args[0].of.i32 = arg1; + args[1].kind = WASMTIME_I32; + args[1].of.i32 = arg2; + check_trap(store, func, args, 2, 0); } int main(int argc, const char* argv[]) { // Initialize. printf("Initializing...\n"); wasm_engine_t* engine = wasm_engine_new(); - wasm_store_t* store = wasm_store_new(engine); + wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL); + wasmtime_context_t *context = wasmtime_store_context(store); // Load our input file to parse it next FILE* file = fopen("examples/memory.wat", "r"); @@ -152,102 +151,108 @@ int main(int argc, const char* argv[]) { // Parse the wat into the binary wasm format wasm_byte_vec_t binary; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); // Compile. printf("Compiling module...\n"); - wasm_module_t* module = NULL; - error = wasmtime_module_new(engine, &binary, &module); + wasmtime_module_t* module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) binary.data, binary.size, &module); if (error) exit_with_error("failed to compile module", error, NULL); wasm_byte_vec_delete(&binary); // Instantiate. printf("Instantiating module...\n"); - wasm_instance_t* instance = NULL; + wasmtime_instance_t instance; wasm_trap_t *trap = NULL; - wasm_extern_vec_t imports = WASM_EMPTY_VEC; - error = wasmtime_instance_new(store, module, &imports, &instance, &trap); - if (!instance) + error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); + wasmtime_module_delete(module); // Extract export. printf("Extracting exports...\n"); - wasm_extern_vec_t exports; - wasm_instance_exports(instance, &exports); - size_t i = 0; - wasm_memory_t* memory = get_export_memory(&exports, i++); - wasm_func_t* size_func = get_export_func(&exports, i++); - wasm_func_t* load_func = get_export_func(&exports, i++); - wasm_func_t* store_func = get_export_func(&exports, i++); - - wasm_module_delete(module); - - // Try cloning. - wasm_memory_t* copy = wasm_memory_copy(memory); - wasm_memory_delete(copy); + wasmtime_memory_t memory; + wasmtime_func_t size_func, load_func, store_func; + wasmtime_extern_t item; + bool ok; + ok = wasmtime_instance_export_get(context, instance, "memory", strlen("memory"), &item); + assert(ok && item.kind == WASMTIME_EXTERN_MEMORY); + memory = item.of.memory; + ok = wasmtime_instance_export_get(context, instance, "size", strlen("size"), &item); + assert(ok && item.kind == WASMTIME_EXTERN_FUNC); + size_func = item.of.func; + ok = wasmtime_instance_export_get(context, instance, "load", strlen("load"), &item); + assert(ok && item.kind == WASMTIME_EXTERN_FUNC); + load_func = item.of.func; + ok = wasmtime_instance_export_get(context, instance, "store", strlen("store"), &item); + assert(ok && item.kind == WASMTIME_EXTERN_FUNC); + store_func = item.of.func; // Check initial memory. printf("Checking memory...\n"); - check(wasm_memory_size(memory) == 2); - check(wasm_memory_data_size(memory) == 0x20000); - check(wasm_memory_data(memory)[0] == 0); - check(wasm_memory_data(memory)[0x1000] == 1); - check(wasm_memory_data(memory)[0x1003] == 4); - - check_call0(size_func, 2); - check_call1(load_func, 0, 0); - check_call1(load_func, 0x1000, 1); - check_call1(load_func, 0x1003, 4); - check_call1(load_func, 0x1ffff, 0); - check_trap1(load_func, 0x20000); + check(wasmtime_memory_size(context, memory) == 2); + check(wasmtime_memory_data_size(context, memory) == 0x20000); + check(wasmtime_memory_data(context, memory)[0] == 0); + check(wasmtime_memory_data(context, memory)[0x1000] == 1); + check(wasmtime_memory_data(context, memory)[0x1003] == 4); + + check_call0(context, size_func, 2); + check_call1(context, load_func, 0, 0); + check_call1(context, load_func, 0x1000, 1); + check_call1(context, load_func, 0x1003, 4); + check_call1(context, load_func, 0x1ffff, 0); + check_trap1(context, load_func, 0x20000); // Mutate memory. printf("Mutating memory...\n"); - wasm_memory_data(memory)[0x1003] = 5; - check_ok2(store_func, 0x1002, 6); - check_trap2(store_func, 0x20000, 0); + wasmtime_memory_data(context, memory)[0x1003] = 5; + check_ok2(context, store_func, 0x1002, 6); + check_trap2(context, store_func, 0x20000, 0); - check(wasm_memory_data(memory)[0x1002] == 6); - check(wasm_memory_data(memory)[0x1003] == 5); - check_call1(load_func, 0x1002, 6); - check_call1(load_func, 0x1003, 5); + check(wasmtime_memory_data(context, memory)[0x1002] == 6); + check(wasmtime_memory_data(context, memory)[0x1003] == 5); + check_call1(context, load_func, 0x1002, 6); + check_call1(context, load_func, 0x1003, 5); // Grow memory. printf("Growing memory...\n"); - check(wasm_memory_grow(memory, 1)); - check(wasm_memory_size(memory) == 3); - check(wasm_memory_data_size(memory) == 0x30000); - - check_call1(load_func, 0x20000, 0); - check_ok2(store_func, 0x20000, 0); - check_trap1(load_func, 0x30000); - check_trap2(store_func, 0x30000, 0); - - check(! wasm_memory_grow(memory, 1)); - check(wasm_memory_grow(memory, 0)); - - wasm_extern_vec_delete(&exports); - wasm_instance_delete(instance); + uint32_t old_size; + error = wasmtime_memory_grow(context, memory, 1, &old_size); + if (error != NULL) + exit_with_error("failed to grow memory", error, trap); + check(wasmtime_memory_size(context, memory) == 3); + check(wasmtime_memory_data_size(context, memory) == 0x30000); + + check_call1(context, load_func, 0x20000, 0); + check_ok2(context, store_func, 0x20000, 0); + check_trap1(context, load_func, 0x30000); + check_trap2(context, store_func, 0x30000, 0); + + error = wasmtime_memory_grow(context, memory, 1, &old_size); + assert(error != NULL); + wasmtime_error_delete(error); + error = wasmtime_memory_grow(context, memory, 0, &old_size); + if (error != NULL) + exit_with_error("failed to grow memory", error, trap); // Create stand-alone memory. printf("Creating stand-alone memory...\n"); wasm_limits_t limits = {5, 5}; wasm_memorytype_t* memorytype = wasm_memorytype_new(&limits); - wasm_memory_t* memory2 = wasm_memory_new(store, memorytype); - check(wasm_memory_size(memory2) == 5); - check(! wasm_memory_grow(memory2, 1)); - check(wasm_memory_grow(memory2, 0)); - + wasmtime_memory_t memory2; + error = wasmtime_memory_new(context, memorytype, &memory2); + if (error != NULL) + exit_with_error("failed to create memory", error, trap); wasm_memorytype_delete(memorytype); - wasm_memory_delete(memory2); + check(wasmtime_memory_size(context, memory2) == 5); // Shut down. printf("Shutting down...\n"); - wasm_store_delete(store); + wasmtime_store_delete(store); wasm_engine_delete(engine); // All done. diff --git a/examples/serialize.c b/examples/serialize.c index 023e1db30b5e..7951731c6451 100644 --- a/examples/serialize.c +++ b/examples/serialize.c @@ -26,7 +26,13 @@ to tweak the `-lpthread` and such annotations as well as the name of the static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); -static wasm_trap_t* hello_callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) { +static wasm_trap_t* hello_callback( + void *env, + wasmtime_caller_t *caller, + const wasmtime_val_t* args, + size_t nargs, + wasmtime_val_t* results, + size_t nresults) { printf("Calling back...\n"); printf("> Hello World!\n"); return NULL; @@ -53,7 +59,7 @@ int serialize(wasm_byte_vec_t* buffer) { // Parse the wat into the binary wasm format wasm_byte_vec_t wasm; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); @@ -61,13 +67,13 @@ int serialize(wasm_byte_vec_t* buffer) { // Now that we've got our binary webassembly we can compile our module // and serialize into buffer. printf("Compiling and serializing module...\n"); - wasm_module_t *module = NULL; - error = wasmtime_module_new(engine, &wasm, &module); + wasmtime_module_t *module = NULL; + error = wasmtime_module_new(engine, (uint8_t*)wasm.data, wasm.size, &module); wasm_byte_vec_delete(&wasm); if (error != NULL) exit_with_error("failed to compile module", error, NULL); error = wasmtime_module_serialize(module, buffer); - wasm_module_delete(module); + wasmtime_module_delete(module); if (error != NULL) exit_with_error("failed to serialize module", error, NULL); @@ -87,13 +93,14 @@ int deserialize(wasm_byte_vec_t* buffer) { // With an engine we can create a *store* which is a long-lived group of wasm // modules. - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); + wasmtime_context_t *context = wasmtime_store_context(store); // Deserialize compiled module. printf("Deserialize module...\n"); - wasm_module_t *module = NULL; - wasmtime_error_t *error = wasmtime_module_deserialize(engine, buffer, &module); + wasmtime_module_t *module = NULL; + wasmtime_error_t *error = wasmtime_module_deserialize(engine, (uint8_t*) buffer->data, buffer->size, &module); if (error != NULL) exit_with_error("failed to compile module", error, NULL); @@ -102,7 +109,7 @@ int deserialize(wasm_byte_vec_t* buffer) { // function above. printf("Creating callback...\n"); wasm_functype_t *hello_ty = wasm_functype_new_0_0(); - wasm_func_t *hello = wasm_func_new(store, hello_ty, hello_callback); + wasmtime_func_t hello = wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL); // With our callback function we can now instantiate the compiled module, // giving us an instance we can then execute exports from. Note that @@ -110,36 +117,31 @@ int deserialize(wasm_byte_vec_t* buffer) { // to handle that here too. printf("Instantiating module...\n"); wasm_trap_t *trap = NULL; - wasm_instance_t *instance = NULL; - wasm_extern_t *imports[] = { wasm_func_as_extern(hello) }; - wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports); - error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap); - if (instance == NULL) + wasmtime_instance_t instance; + wasmtime_extern_t imports[1]; + imports[0].kind = WASMTIME_EXTERN_FUNC; + imports[0].of.func = hello; + error = wasmtime_instance_new(context, module, imports, 1, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); + wasmtime_module_delete(module); // Lookup our `run` export function - printf("Extracting export...\n"); - wasm_extern_vec_t externs; - wasm_instance_exports(instance, &externs); - assert(externs.size == 1); - wasm_func_t *run = wasm_extern_as_func(externs.data[0]); - assert(run != NULL); + wasmtime_extern_t run; + bool ok = wasmtime_instance_export_get(context, instance, "run", 3, &run); + assert(ok); + assert(run.kind == WASMTIME_EXTERN_FUNC); // And call it! printf("Calling export...\n"); - wasm_val_vec_t args_vec = WASM_EMPTY_VEC; - wasm_val_vec_t results_vec = WASM_EMPTY_VEC; - error = wasmtime_func_call(run, &args_vec, &results_vec, &trap); + error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); // Clean up after ourselves at this point printf("All finished!\n"); - wasm_extern_vec_delete(&externs); - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); + wasmtime_store_delete(store); wasm_engine_delete(engine); return 0; } From 00ee228703a002cd20c76f02a4e0ffa20f15176b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 17:01:56 -0700 Subject: [PATCH 13/90] Skip 4g test on ci --- tests/all/instance.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/all/instance.rs b/tests/all/instance.rs index 0a2b5562d074..84d2f97392b1 100644 --- a/tests/all/instance.rs +++ b/tests/all/instance.rs @@ -34,6 +34,11 @@ fn initializes_linear_memory() -> Result<()> { #[test] fn linear_memory_limits() -> Result<()> { + // this test will allocate 4GB of virtual memory space, and may not work in + // situations like CI QEMU emulation where it triggers SIGKILL. + if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() { + return Ok(()); + } test(&Engine::default())?; test(&Engine::new(Config::new().allocation_strategy( InstanceAllocationStrategy::Pooling { From 54dbd75bd1917c9c6b84d653d8ea91f5fcfbdd02 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 17:22:26 -0700 Subject: [PATCH 14/90] Hello, yes, this is dog --- crates/c-api/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index c4b189418d3e..0d5c598e02eb 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -83,9 +83,9 @@ unsafe fn slice_from_raw_parts<'a, T>(ptr: *const T, len: usize) -> &'a [T] { } } -unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a [T] { +unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T] { if len == 0 { - &[] + &mut [] } else { std::slice::from_raw_parts_mut(ptr, len) } From 19292f51730ae145ec25f2f2ad9471535b1906dc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 20:04:16 -0700 Subject: [PATCH 15/90] More updates --- crates/wasmtime/Cargo.toml | 4 ++ examples/multi.c | 89 +++++++++++++++++++------------------- examples/threads.c | 4 +- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index a7e5a914cb9f..a3d61a79d52e 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -13,6 +13,10 @@ edition = "2018" # [package.metadata.docs.rs] # rustdoc-args = ["--cfg", "nightlydoc"] +[lib] +# TODO +doctest = false + [dependencies] wasmtime-runtime = { path = "../runtime", version = "0.26.0" } wasmtime-environ = { path = "../environ", version = "0.26.0" } diff --git a/examples/multi.c b/examples/multi.c index 35537064470f..2cbf03fe7b6a 100644 --- a/examples/multi.c +++ b/examples/multi.c @@ -32,27 +32,37 @@ static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_t // A function to be called from Wasm code. wasm_trap_t* callback( - const wasm_val_vec_t* args, wasm_val_vec_t* results + void *env, + wasmtime_caller_t *caller, + const wasmtime_val_t* args, + size_t nargs, + wasmtime_val_t* results, + size_t nresults ) { printf("Calling back...\n"); - printf("> %"PRIu32" %"PRIu64"\n", args->data[0].of.i32, args->data[1].of.i64); + printf("> %"PRIu32" %"PRIu64"\n", args[0].of.i32, args[1].of.i64); printf("\n"); - wasm_val_copy(&results->data[0], &args->data[1]); - wasm_val_copy(&results->data[1], &args->data[0]); + results[0] = args[1]; + results[1] = args[0]; return NULL; } // A function closure. wasm_trap_t* closure_callback( - void* env, const wasm_val_t args[], wasm_val_t results[] + void* env, + wasmtime_caller_t *caller, + const wasmtime_val_t* args, + size_t nargs, + wasmtime_val_t* results, + size_t nresults ) { int i = *(int*)env; printf("Calling back closure...\n"); printf("> %d\n", i); - results[0].kind = WASM_I32; + results[0].kind = WASMTIME_I32; results[0].of.i32 = (int32_t)i; return NULL; } @@ -62,7 +72,8 @@ int main(int argc, const char* argv[]) { // Initialize. printf("Initializing...\n"); wasm_engine_t* engine = wasm_engine_new(); - wasm_store_t* store = wasm_store_new(engine); + wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL); + wasmtime_context_t *context = wasmtime_store_context(store); // Load our input file to parse it next FILE* file = fopen("examples/multi.wat", "r"); @@ -83,18 +94,17 @@ int main(int argc, const char* argv[]) { // Parse the wat into the binary wasm format wasm_byte_vec_t binary; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); // Compile. printf("Compiling module...\n"); - wasm_module_t* module = NULL; - error = wasmtime_module_new(engine, &binary, &module); + wasmtime_module_t* module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) binary.data, binary.size, &module); if (error) exit_with_error("failed to compile module", error, NULL); - wasm_byte_vec_delete(&binary); // Create external print functions. @@ -105,65 +115,54 @@ int main(int argc, const char* argv[]) { wasm_valtype_new_i64(), wasm_valtype_new_i32() ); - wasm_func_t* callback_func = - wasm_func_new(store, callback_type, callback); - + wasmtime_func_t callback_func = + wasmtime_func_new(context, callback_type, callback, NULL, NULL); wasm_functype_delete(callback_type); // Instantiate. printf("Instantiating module...\n"); - wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)}; - wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports); - wasm_instance_t* instance = NULL; + wasmtime_extern_t imports[1]; + imports[0].kind = WASMTIME_EXTERN_FUNC; + imports[0].of.func = callback_func; + wasmtime_instance_t instance; wasm_trap_t* trap = NULL; - error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap); - if (!instance) + error = wasmtime_instance_new(context, module, imports, 1, &instance, &trap); + if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); - - wasm_func_delete(callback_func); + wasmtime_module_delete(module); // Extract export. printf("Extracting export...\n"); - wasm_extern_vec_t exports; - wasm_instance_exports(instance, &exports); - if (exports.size == 0) { - printf("> Error accessing exports!\n"); - return 1; - } - wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); - if (run_func == NULL) { - printf("> Error accessing export!\n"); - return 1; - } - - wasm_module_delete(module); - wasm_instance_delete(instance); + wasmtime_extern_t run; + bool ok = wasmtime_instance_export_get(context, instance, "g", 1, &run); + assert(ok); + assert(run.kind == WASMTIME_EXTERN_FUNC); // Call. printf("Calling export...\n"); - wasm_val_t args[2] = { WASM_I32_VAL(1), WASM_I64_VAL(2) }; - wasm_val_t results[2]; - wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); - wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results); - error = wasmtime_func_call(run_func, &args_vec, &results_vec, &trap); + wasmtime_val_t args[2]; + args[0].kind = WASMTIME_I32; + args[0].of.i32 = 1; + args[1].kind = WASMTIME_I64; + args[1].of.i64 = 2; + wasmtime_val_t results[2]; + error = wasmtime_func_call(context, run.of.func, args, 2, results, 2, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call run", error, trap); - wasm_extern_vec_delete(&exports); - // Print result. printf("Printing result...\n"); printf("> %"PRIu64" %"PRIu32"\n", results[0].of.i64, results[1].of.i32); - assert(results[0].kind == WASM_I64); + assert(results[0].kind == WASMTIME_I64); assert(results[0].of.i64 == 2); - assert(results[1].kind == WASM_I32); + assert(results[1].kind == WASMTIME_I32); assert(results[1].of.i32 == 1); // Shut down. printf("Shutting down...\n"); - wasm_store_delete(store); + wasmtime_store_delete(store); wasm_engine_delete(engine); // All done. diff --git a/examples/threads.c b/examples/threads.c index 06aa1f0aa70a..0fbef5a4c3b3 100644 --- a/examples/threads.c +++ b/examples/threads.c @@ -124,11 +124,11 @@ int main(int argc, const char *argv[]) { // Parse the wat into the binary wasm format wasm_byte_vec_t binary; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); - + // Compile and share. own wasm_store_t* store = wasm_store_new(engine); own wasm_module_t* module = wasm_module_new(store, &binary); From 072415f444b6768f44d34399111a6401dadc1135 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 20:04:59 -0700 Subject: [PATCH 16/90] Remove duplicate example --- examples/hello.cc | 136 ---------------------------------------------- 1 file changed, 136 deletions(-) delete mode 100644 examples/hello.cc diff --git a/examples/hello.cc b/examples/hello.cc deleted file mode 100644 index f44b49c1fcb5..000000000000 --- a/examples/hello.cc +++ /dev/null @@ -1,136 +0,0 @@ -/* -Example of instantiating of the WebAssembly module and invoking its exported -function. - -You can compile and run this example on Linux with: - - cargo build --release -p wasmtime-c-api - cc examples/hello.cc \ - -I crates/c-api/include \ - -I crates/c-api/wasm-c-api/include \ - target/release/libwasmtime.a \ - -lpthread -ldl -lm \ - -o hello - ./hello - -Note that on Windows and macOS the command will be similar, but you'll need -to tweak the `-lpthread` and such annotations as well as the name of the -`libwasmtime.a` file on Windows. -*/ - -#include -#include -#include -#include -#include - -static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); - -static wasm_trap_t* hello_callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) { - printf("Calling back...\n"); - printf("> Hello World!\n"); - return NULL; -} - -int main() { - int ret = 0; - // Set up our compilation context. Note that we could also work with a - // `wasm_config_t` here to configure what feature are enabled and various - // compilation settings. - printf("Initializing...\n"); - wasm_engine_t *engine = wasm_engine_new(); - assert(engine != NULL); - - // With an engine we can create a *store* which is a long-lived group of wasm - // modules. - wasm_store_t *store = wasm_store_new(engine); - assert(store != NULL); - - // Read our input file, which in this case is a wasm text file. - FILE* file = fopen("examples/hello.wat", "r"); - assert(file != NULL); - fseek(file, 0L, SEEK_END); - size_t file_size = ftell(file); - fseek(file, 0L, SEEK_SET); - wasm_byte_vec_t wat; - wasm_byte_vec_new_uninitialized(&wat, file_size); - assert(fread(wat.data, file_size, 1, file) == 1); - fclose(file); - - // Parse the wat into the binary wasm format - wasm_byte_vec_t wasm; - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm); - if (error != NULL) - exit_with_error("failed to parse wat", error, NULL); - wasm_byte_vec_delete(&wat); - - // Now that we've got our binary webassembly we can compile our module. - printf("Compiling module...\n"); - wasm_module_t *module = NULL; - error = wasmtime_module_new(engine, &wasm, &module); - wasm_byte_vec_delete(&wasm); - if (error != NULL) - exit_with_error("failed to compile module", error, NULL); - - // Next up we need to create the function that the wasm module imports. Here - // we'll be hooking up a thunk function to the `hello_callback` native - // function above. - printf("Creating callback...\n"); - wasm_functype_t *hello_ty = wasm_functype_new_0_0(); - wasm_func_t *hello = wasm_func_new(store, hello_ty, hello_callback); - - // With our callback function we can now instantiate the compiled module, - // giving us an instance we can then execute exports from. Note that - // instantiation can trap due to execution of the `start` function, so we need - // to handle that here too. - printf("Instantiating module...\n"); - wasm_trap_t *trap = NULL; - wasm_instance_t *instance = NULL; - wasm_extern_t* imports[] = { wasm_func_as_extern(hello) }; - wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports); - error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap); - if (instance == NULL) - exit_with_error("failed to instantiate", error, trap); - - // Lookup our `run` export function - printf("Extracting export...\n"); - wasm_extern_vec_t externs; - wasm_instance_exports(instance, &externs); - assert(externs.size == 1); - wasm_func_t *run = wasm_extern_as_func(externs.data[0]); - assert(run != NULL); - - // And call it! - printf("Calling export...\n"); - wasm_val_vec_t args_vec = WASM_EMPTY_VEC; - wasm_val_vec_t results_vec = WASM_EMPTY_VEC; - error = wasmtime_func_call(run, &args_vec, &results_vec, &trap); - if (error != NULL || trap != NULL) - exit_with_error("failed to call function", error, trap); - - // Clean up after ourselves at this point - printf("All finished!\n"); - ret = 0; - - wasm_extern_vec_delete(&externs); - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); - wasm_engine_delete(engine); - return ret; -} - -static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) { - fprintf(stderr, "error: %s\n", message); - wasm_byte_vec_t error_message; - if (error != NULL) { - wasmtime_error_message(error, &error_message); - wasmtime_error_delete(error); - } else { - wasm_trap_message(trap, &error_message); - wasm_trap_delete(trap); - } - fprintf(stderr, "%.*s\n", (int) error_message.size, error_message.data); - wasm_byte_vec_delete(&error_message); - exit(1); -} From 6f78902867e855fed59fe7f1b0761492be065415 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 21:07:27 -0700 Subject: [PATCH 17/90] Polish off the C API --- crates/c-api/include/wasi.h | 58 ------- crates/c-api/include/wasmtime/linker.h | 7 +- crates/c-api/include/wasmtime/store.h | 4 + crates/c-api/src/func.rs | 2 +- crates/c-api/src/lib.rs | 8 +- crates/c-api/src/linker.rs | 21 +-- crates/c-api/src/store.rs | 54 +++++- crates/c-api/src/wasi.rs | 222 +++++++------------------ examples/linking.c | 59 +++---- examples/wasi/main.c | 43 +++-- 10 files changed, 172 insertions(+), 306 deletions(-) diff --git a/crates/c-api/include/wasi.h b/crates/c-api/include/wasi.h index 92271496c32c..9b98becab749 100644 --- a/crates/c-api/include/wasi.h +++ b/crates/c-api/include/wasi.h @@ -146,64 +146,6 @@ WASI_API_EXTERN void wasi_config_inherit_stderr(wasi_config_t* config); */ WASI_API_EXTERN bool wasi_config_preopen_dir(wasi_config_t* config, const char* path, const char* guest_path); -/** - * \typedef wasi_instance_t - * \brief Convenience alias for #wasi_instance_t - * - * \struct wasi_instance_t - * \brief Opaque type representing a WASI instance. - * - * \fn void wasi_instance_delete(own wasi_instance_t *); - * \brief Deletes an instance object. - */ -WASI_DECLARE_OWN(instance) - -/** - * \brief Creates a new WASI instance from the specified configuration. - * - * \param store the store which functions will be attached to - * \param name the WASI module name that is being instantiated, currently either - * `wasi_unstable` or `wasi_snapshot_preview`. - * \param config the configuration object which has settings for how WASI APIs - * will behave. - * \param trap a location, if `NULL` is returned, that contains information - * about why instantiation failed. - * - * \return a #wasi_instance_t owned by the caller on success or `NULL` on - * failure. - * - * Note that this function takes ownership of the `config` argument whether this - * function succeeds or not. Ownership of the #wasi_instance_t and #wasm_trap_t - * are transferred to the caller. - * - * With a #wasi_instance_t you'll likely call either - * #wasmtime_linker_define_wasi or #wasi_instance_bind_import afterwards. - */ -WASI_API_EXTERN own wasi_instance_t* wasi_instance_new( - wasm_store_t* store, - const char* name, - own wasi_config_t* config, - own wasm_trap_t** trap -); - -/** - * \brief Extracts a matching item for the given import from a #wasi_instance_t. - * - * \param instance the WASI instance an export is extracted from - * \param import the desired import type that is being extracted, typically - * acquired from #wasm_module_imports. - * - * \return a #wasm_extern_t which can be used to satisfy the `import` - * requested, or `NULL` if the provided `instance` cannot satisfy `import`. - * - * This function does not take ownership of its arguments, and the lifetime of - * the #wasm_extern_t is tied to the #wasi_instance_t argument. - */ -WASI_API_EXTERN const wasm_extern_t* wasi_instance_bind_import( - const wasi_instance_t* instance, - const wasm_importtype_t* import -); - #undef own #ifdef __cplusplus diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index 71b9dd1cf0a5..8bf6fbcec8c1 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -79,10 +79,9 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( * For more information about name resolution consult the [Rust * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). */ -/* WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( */ -/* wasmtime_linker_t *linker, */ -/* const wasi_instance_t *instance */ -/* ); */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( + wasmtime_linker_t *linker +); /** * \brief Defines an instance under the specified name in this linker. diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h index 835d7fd2af60..1aa527bdd1f3 100644 --- a/crates/c-api/include/wasmtime/store.h +++ b/crates/c-api/include/wasmtime/store.h @@ -78,6 +78,10 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t * */ WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel); +typedef struct wasi_config_t wasi_config_t; + +WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); + /** * \typedef wasmtime_interrupt_handle_t * \brief Convenience alias for #wasmtime_interrupt_handle_t diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 27e851118a54..bcc94db031a7 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -195,7 +195,7 @@ pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t #[repr(C)] pub struct wasmtime_caller_t<'a> { - caller: Caller<'a, crate::ForeignData>, + caller: Caller<'a, crate::StoreData>, } #[no_mangle] diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 0d5c598e02eb..955a257372b9 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -38,10 +38,10 @@ pub use crate::types::*; pub use crate::val::*; pub use crate::vec::*; -// #[cfg(feature = "wasi")] -// mod wasi; -// #[cfg(feature = "wasi")] -// pub use crate::wasi::*; +#[cfg(feature = "wasi")] +mod wasi; +#[cfg(feature = "wasi")] +pub use crate::wasi::*; #[cfg(feature = "wat")] mod wat2wasm; diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 82dfdcfc4ba7..9013b3828294 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -8,7 +8,7 @@ use wasmtime::{Func, Instance, Linker}; #[repr(C)] pub struct wasmtime_linker_t { - linker: Linker, + linker: Linker, } #[no_mangle] @@ -54,15 +54,16 @@ pub unsafe extern "C" fn wasmtime_linker_define( handle_result(linker.define(module, name, item), |_linker| ()) } -// #[cfg(feature = "wasi")] -// #[no_mangle] -// pub extern "C" fn wasmtime_linker_define_wasi( -// linker: &mut wasmtime_linker_t, -// instance: &crate::wasi_instance_t, -// ) -> Option> { -// let linker = &mut linker.linker; -// handle_result(instance.add_to_linker(linker), |_linker| ()) -// } +#[cfg(feature = "wasi")] +#[no_mangle] +pub extern "C" fn wasmtime_linker_define_wasi( + linker: &mut wasmtime_linker_t, +) -> Option> { + handle_result( + wasmtime_wasi::add_to_linker(&mut linker.linker), + |_linker| (), + ) +} #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_define_instance( diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index c28d09a3a0e7..5622e65c51f7 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -40,11 +40,35 @@ pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box { #[repr(C)] pub struct wasmtime_store_t { - pub(crate) store: Store, + pub(crate) store: Store, } -pub type CStoreContext<'a> = StoreContext<'a, crate::ForeignData>; -pub type CStoreContextMut<'a> = StoreContextMut<'a, crate::ForeignData>; +pub type CStoreContext<'a> = StoreContext<'a, StoreData>; +pub type CStoreContextMut<'a> = StoreContextMut<'a, StoreData>; + +pub struct StoreData { + foreign: crate::ForeignData, + #[cfg(feature = "wasi")] + wasi: Option, +} + +#[cfg(feature = "wasi")] +impl std::borrow::Borrow for StoreData { + fn borrow(&self) -> &wasmtime_wasi::WasiCtx { + self.wasi + .as_ref() + .expect("wasi not configured via `wasmtime_context_set_wasi` yet") + } +} + +#[cfg(feature = "wasi")] +impl std::borrow::BorrowMut for StoreData { + fn borrow_mut(&mut self) -> &mut wasmtime_wasi::WasiCtx { + self.wasi + .as_mut() + .expect("wasi not configured via `wasmtime_context_set_wasi` yet") + } +} #[no_mangle] pub extern "C" fn wasmtime_store_delete(_: Box) {} @@ -56,7 +80,14 @@ pub extern "C" fn wasmtime_store_new( finalizer: Option, ) -> Box { Box::new(wasmtime_store_t { - store: Store::new(&engine.engine, ForeignData { data, finalizer }), + store: Store::new( + &engine.engine, + StoreData { + foreign: ForeignData { data, finalizer }, + #[cfg(feature = "wasi")] + wasi: None, + }, + ), }) } @@ -67,12 +98,23 @@ pub extern "C" fn wasmtime_store_context(store: &mut wasmtime_store_t) -> CStore #[no_mangle] pub extern "C" fn wasmtime_context_get_data(store: CStoreContext<'_>) -> *mut c_void { - store.data().data + store.data().foreign.data } #[no_mangle] pub extern "C" fn wasmtime_context_set_data(mut store: CStoreContextMut<'_>, data: *mut c_void) { - store.data_mut().data = data; + store.data_mut().foreign.data = data; +} + +#[cfg(feature = "wasi")] +#[no_mangle] +pub extern "C" fn wasmtime_context_set_wasi( + mut context: CStoreContextMut<'_>, + wasi: Box, +) -> Option> { + crate::handle_result(wasi.into_wasi_ctx(), |wasi| { + context.data_mut().wasi = Some(wasi); + }) } #[no_mangle] diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 302fcde8b7e0..bcbc2840d29f 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -1,21 +1,13 @@ //! The WASI embedding API definitions for Wasmtime. -use crate::{wasm_extern_t, wasm_importtype_t, wasm_store_t, wasm_trap_t}; + use anyhow::Result; -use std::cell::RefCell; -use std::collections::HashMap; use std::ffi::CStr; use std::fs::File; use std::os::raw::{c_char, c_int}; use std::path::{Path, PathBuf}; -use std::rc::Rc; use std::slice; -use std::str; -use wasmtime::{Extern, Linker, Trap}; use wasmtime_wasi::{ - sync::{ - snapshots::preview_0::Wasi as WasiSnapshot0, snapshots::preview_1::Wasi as WasiPreview1, - Dir, WasiCtxBuilder, - }, + sync::{Dir, WasiCtxBuilder}, WasiCtx, }; @@ -31,13 +23,6 @@ unsafe fn create_file(path: *const c_char) -> Option { File::create(cstr_to_path(path)?).ok() } -pub enum WasiModule { - Snapshot0(WasiSnapshot0), - Preview1(WasiPreview1), -} - -impl WasiModule {} - #[repr(C)] #[derive(Default)] pub struct wasi_config_t { @@ -54,6 +39,61 @@ pub struct wasi_config_t { inherit_stderr: bool, } +impl wasi_config_t { + pub fn into_wasi_ctx(self) -> Result { + let mut builder = WasiCtxBuilder::new(); + if self.inherit_args { + builder = builder.inherit_args()?; + } else if !self.args.is_empty() { + let args = self + .args + .into_iter() + .map(|bytes| Ok(String::from_utf8(bytes)?)) + .collect::>>()?; + builder = builder.args(&args)?; + } + if self.inherit_env { + builder = builder.inherit_env()?; + } else if !self.env.is_empty() { + let env = self + .env + .into_iter() + .map(|(kbytes, vbytes)| { + let k = String::from_utf8(kbytes)?; + let v = String::from_utf8(vbytes)?; + Ok((k, v)) + }) + .collect::>>()?; + builder = builder.envs(&env)?; + } + if self.inherit_stdin { + builder = builder.inherit_stdin(); + } else if let Some(file) = self.stdin { + let file = unsafe { cap_std::fs::File::from_std(file) }; + let file = wasi_cap_std_sync::file::File::from_cap_std(file); + builder = builder.stdin(Box::new(file)); + } + if self.inherit_stdout { + builder = builder.inherit_stdout(); + } else if let Some(file) = self.stdout { + let file = unsafe { cap_std::fs::File::from_std(file) }; + let file = wasi_cap_std_sync::file::File::from_cap_std(file); + builder = builder.stdout(Box::new(file)); + } + if self.inherit_stderr { + builder = builder.inherit_stderr(); + } else if let Some(file) = self.stderr { + let file = unsafe { cap_std::fs::File::from_std(file) }; + let file = wasi_cap_std_sync::file::File::from_cap_std(file); + builder = builder.stderr(Box::new(file)); + } + for (dir, path) in self.preopens { + builder = builder.preopened_dir(dir, path)?; + } + Ok(builder.build()?) + } +} + #[no_mangle] pub extern "C" fn wasi_config_new() -> Box { Box::new(wasi_config_t::default()) @@ -198,151 +238,3 @@ pub unsafe extern "C" fn wasi_config_preopen_dir( true } - -enum WasiInstance { - Preview1(WasiPreview1), - Snapshot0(WasiSnapshot0), -} - -fn create_wasi_ctx(config: wasi_config_t) -> Result>> { - let mut builder = WasiCtxBuilder::new(); - if config.inherit_args { - builder = builder.inherit_args()?; - } else if !config.args.is_empty() { - let args = config - .args - .into_iter() - .map(|bytes| Ok(String::from_utf8(bytes)?)) - .collect::>>()?; - builder = builder.args(&args)?; - } - if config.inherit_env { - builder = builder.inherit_env()?; - } else if !config.env.is_empty() { - let env = config - .env - .into_iter() - .map(|(kbytes, vbytes)| { - let k = String::from_utf8(kbytes)?; - let v = String::from_utf8(vbytes)?; - Ok((k, v)) - }) - .collect::>>()?; - builder = builder.envs(&env)?; - } - if config.inherit_stdin { - builder = builder.inherit_stdin(); - } else if let Some(file) = config.stdin { - let file = unsafe { cap_std::fs::File::from_std(file) }; - let file = wasi_cap_std_sync::file::File::from_cap_std(file); - builder = builder.stdin(Box::new(file)); - } - if config.inherit_stdout { - builder = builder.inherit_stdout(); - } else if let Some(file) = config.stdout { - let file = unsafe { cap_std::fs::File::from_std(file) }; - let file = wasi_cap_std_sync::file::File::from_cap_std(file); - builder = builder.stdout(Box::new(file)); - } - if config.inherit_stderr { - builder = builder.inherit_stderr(); - } else if let Some(file) = config.stderr { - let file = unsafe { cap_std::fs::File::from_std(file) }; - let file = wasi_cap_std_sync::file::File::from_cap_std(file); - builder = builder.stderr(Box::new(file)); - } - for (dir, path) in config.preopens { - builder = builder.preopened_dir(dir, path)?; - } - Ok(Rc::new(RefCell::new(builder.build()?))) -} - -#[repr(C)] -pub struct wasi_instance_t { - wasi: WasiInstance, - export_cache: HashMap>, -} - -impl wasi_instance_t { - pub fn add_to_linker(&self, linker: &mut Linker) -> Result<()> { - match &self.wasi { - WasiInstance::Snapshot0(w) => w.add_to_linker(linker), - WasiInstance::Preview1(w) => w.add_to_linker(linker), - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_instance_new( - store: &wasm_store_t, - name: *const c_char, - config: Box, - trap: &mut *mut wasm_trap_t, -) -> Option> { - let store = &store.store; - - let result = match CStr::from_ptr(name).to_str().unwrap_or("") { - "wasi_snapshot_preview1" => { - create_wasi_ctx(*config).map(|cx| WasiInstance::Preview1(WasiPreview1::new(store, cx))) - } - "wasi_unstable" => create_wasi_ctx(*config) - .map(|cx| WasiInstance::Snapshot0(WasiSnapshot0::new(store, cx))), - _ => Err(anyhow::anyhow!("unsupported WASI version")), - }; - - match result { - Ok(wasi) => Some(Box::new(wasi_instance_t { - wasi, - export_cache: HashMap::new(), - })), - Err(e) => { - *trap = Box::into_raw(Box::new(wasm_trap_t { - trap: Trap::from(e), - })); - - None - } - } -} - -#[no_mangle] -pub extern "C" fn wasi_instance_delete(_instance: Box) {} - -#[no_mangle] -pub extern "C" fn wasi_instance_bind_import<'a>( - instance: &'a mut wasi_instance_t, - import: &wasm_importtype_t, -) -> Option<&'a wasm_extern_t> { - let module = &import.module; - let name = str::from_utf8(import.name.as_ref()?.as_bytes()).ok()?; - - let export = match &instance.wasi { - WasiInstance::Preview1(wasi) => { - if module != "wasi_snapshot_preview1" { - return None; - } - wasi.get_export(&name)? - } - WasiInstance::Snapshot0(wasi) => { - if module != "wasi_unstable" { - return None; - } - - wasi.get_export(&name)? - } - }; - - if &export.ty() != import.ty.func()? { - return None; - } - - let entry = instance - .export_cache - .entry(name.to_string()) - .or_insert_with(|| { - Box::new(wasm_extern_t { - which: Extern::Func(export.clone()), - }) - }); - Some(entry) -} diff --git a/examples/linking.c b/examples/linking.c index d9bc0f93c142..01de2ce37e18 100644 --- a/examples/linking.c +++ b/examples/linking.c @@ -30,12 +30,12 @@ static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_t static void read_wat_file(wasm_engine_t *engine, wasm_byte_vec_t *bytes, const char *file); int main() { - int ret = 0; // Set up our context wasm_engine_t *engine = wasm_engine_new(); assert(engine != NULL); - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); + wasmtime_context_t *context = wasmtime_store_context(store); wasm_byte_vec_t linking1_wasm, linking2_wasm; read_wat_file(engine, &linking1_wasm, "examples/linking1.wat"); @@ -43,18 +43,18 @@ int main() { // Compile our two modules wasmtime_error_t *error; - wasm_module_t *linking1_module = NULL; - wasm_module_t *linking2_module = NULL; - error = wasmtime_module_new(engine, &linking1_wasm, &linking1_module); + wasmtime_module_t *linking1_module = NULL; + wasmtime_module_t *linking2_module = NULL; + error = wasmtime_module_new(engine, (uint8_t*) linking1_wasm.data, linking1_wasm.size, &linking1_module); if (error != NULL) exit_with_error("failed to compile linking1", error, NULL); - error = wasmtime_module_new(engine, &linking2_wasm, &linking2_module); + error = wasmtime_module_new(engine, (uint8_t*) linking2_wasm.data, linking2_wasm.size, &linking2_module); if (error != NULL) exit_with_error("failed to compile linking2", error, NULL); wasm_byte_vec_delete(&linking1_wasm); wasm_byte_vec_delete(&linking2_wasm); - // Instantiate wasi + // Configure WASI and store it within our `wasmtime_store_t` wasi_config_t *wasi_config = wasi_config_new(); assert(wasi_config); wasi_config_inherit_argv(wasi_config); @@ -63,57 +63,48 @@ int main() { wasi_config_inherit_stdout(wasi_config); wasi_config_inherit_stderr(wasi_config); wasm_trap_t *trap = NULL; - wasi_instance_t *wasi = wasi_instance_new(store, "wasi_snapshot_preview1", wasi_config, &trap); - if (wasi == NULL) + error = wasmtime_context_set_wasi(context, wasi_config); + if (error != NULL) exit_with_error("failed to instantiate wasi", NULL, trap); // Create our linker which will be linking our modules together, and then add // our WASI instance to it. - wasmtime_linker_t *linker = wasmtime_linker_new(store); - error = wasmtime_linker_define_wasi(linker, wasi); + wasmtime_linker_t *linker = wasmtime_linker_new(engine); + error = wasmtime_linker_define_wasi(linker); if (error != NULL) exit_with_error("failed to link wasi", error, NULL); // Instantiate `linking2` with our linker. - wasm_instance_t *linking2; - error = wasmtime_linker_instantiate(linker, linking2_module, &linking2, &trap); + wasmtime_instance_t linking2; + error = wasmtime_linker_instantiate(linker, context, linking2_module, &linking2, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to instantiate linking2", error, trap); // Register our new `linking2` instance with the linker - wasm_name_t linking2_name; - linking2_name.data = "linking2"; - linking2_name.size = strlen(linking2_name.data); - error = wasmtime_linker_define_instance(linker, &linking2_name, linking2); + error = wasmtime_linker_define_instance(linker, context, "linking2", strlen("linking2"), linking2); if (error != NULL) exit_with_error("failed to link linking2", error, NULL); // Instantiate `linking1` with the linker now that `linking2` is defined - wasm_instance_t *linking1; - error = wasmtime_linker_instantiate(linker, linking1_module, &linking1, &trap); + wasmtime_instance_t linking1; + error = wasmtime_linker_instantiate(linker, context, linking1_module, &linking1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to instantiate linking1", error, trap); // Lookup our `run` export function - wasm_extern_vec_t linking1_externs; - wasm_instance_exports(linking1, &linking1_externs); - assert(linking1_externs.size == 1); - wasm_func_t *run = wasm_extern_as_func(linking1_externs.data[0]); - assert(run != NULL); - wasm_val_vec_t args_vec = WASM_EMPTY_VEC; - wasm_val_vec_t results_vec = WASM_EMPTY_VEC; - error = wasmtime_func_call(run, &args_vec, &results_vec, &trap); + wasmtime_extern_t run; + bool ok = wasmtime_instance_export_get(context, linking1, "run", 3, &run); + assert(ok); + assert(run.kind == WASMTIME_EXTERN_FUNC); + error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call run", error, trap); // Clean up after ourselves at this point - wasm_extern_vec_delete(&linking1_externs); - wasm_instance_delete(linking1); - wasm_instance_delete(linking2); wasmtime_linker_delete(linker); - wasm_module_delete(linking1_module); - wasm_module_delete(linking2_module); - wasm_store_delete(store); + wasmtime_module_delete(linking1_module); + wasmtime_module_delete(linking2_module); + wasmtime_store_delete(store); wasm_engine_delete(engine); return 0; } @@ -141,7 +132,7 @@ static void read_wat_file( fclose(file); // Parse the wat into the binary wasm format - wasmtime_error_t *error = wasmtime_wat2wasm(&wat, bytes); + wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, bytes); if (error != NULL) exit_with_error("failed to parse wat", error, NULL); wasm_byte_vec_delete(&wat); diff --git a/examples/wasi/main.c b/examples/wasi/main.c index 3bb71ec4de34..c1d23055e174 100644 --- a/examples/wasi/main.c +++ b/examples/wasi/main.c @@ -28,12 +28,18 @@ to tweak the `-lpthread` and such annotations. static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); int main() { - int ret = 0; // Set up our context wasm_engine_t *engine = wasm_engine_new(); assert(engine != NULL); - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); assert(store != NULL); + wasmtime_context_t *context = wasmtime_store_context(store); + + // Create a linker with WASI functions defined + wasmtime_linker_t *linker = wasmtime_linker_new(engine); + wasmtime_error_t *error = wasmtime_linker_define_wasi(linker); + if (error != NULL) + exit_with_error("failed to link wasi", error, NULL); wasm_byte_vec_t wasm; // Load our input file to parse it next @@ -53,8 +59,8 @@ int main() { fclose(file); // Compile our modules - wasm_module_t *module = NULL; - wasmtime_error_t *error = wasmtime_module_new(engine, &wasm, &module); + wasmtime_module_t *module = NULL; + error = wasmtime_module_new(engine, (uint8_t*)wasm.data, wasm.size, &module); if (!module) exit_with_error("failed to compile module", error, NULL); wasm_byte_vec_delete(&wasm); @@ -68,39 +74,28 @@ int main() { wasi_config_inherit_stdout(wasi_config); wasi_config_inherit_stderr(wasi_config); wasm_trap_t *trap = NULL; - wasi_instance_t *wasi = wasi_instance_new(store, "wasi_snapshot_preview1", wasi_config, &trap); - if (wasi == NULL) - exit_with_error("failed to instantiate WASI", NULL, trap); - - wasmtime_linker_t *linker = wasmtime_linker_new(store); - error = wasmtime_linker_define_wasi(linker, wasi); + error = wasmtime_context_set_wasi(context, wasi_config); if (error != NULL) - exit_with_error("failed to link wasi", error, NULL); + exit_with_error("failed to instantiate WASI", error, NULL); // Instantiate the module - wasm_name_t empty; - wasm_name_new_from_string(&empty, ""); - wasm_instance_t *instance = NULL; - error = wasmtime_linker_module(linker, &empty, module); + error = wasmtime_linker_module(linker, context, "", 0, module); if (error != NULL) exit_with_error("failed to instantiate module", error, NULL); // Run it. - wasm_func_t* func; - wasmtime_linker_get_default(linker, &empty, &func); + wasmtime_func_t func; + error = wasmtime_linker_get_default(linker, context, "", 0, &func); if (error != NULL) exit_with_error("failed to locate default export for module", error, NULL); - wasm_val_vec_t args_vec = WASM_EMPTY_VEC; - wasm_val_vec_t results_vec = WASM_EMPTY_VEC; - error = wasmtime_func_call(func, &args_vec, &results_vec, &trap); - if (error != NULL) + error = wasmtime_func_call(context, func, NULL, 0, NULL, 0, &trap); + if (error != NULL || trap != NULL) exit_with_error("error calling default export", error, trap); // Clean up after ourselves at this point - wasm_name_delete(&empty); - wasm_module_delete(module); - wasm_store_delete(store); + wasmtime_module_delete(module); + wasmtime_store_delete(store); wasm_engine_delete(engine); return 0; } From 8492a2b3612de2237fdad24b4d7c14695b7689fa Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 21:21:57 -0700 Subject: [PATCH 18/90] Resolve some TODO --- crates/wasmtime/src/store.rs | 62 ++++++++++++++++++++++++- crates/wast/src/spectest.rs | 15 +++--- crates/wiggle/wasmtime/macro/src/lib.rs | 16 ++++++- 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 680962558f39..555cd88b2905 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -841,7 +841,67 @@ impl StoreOpaqueSend<'_> { engine: Engine, } - // TODO + // This is surely the most dangerous `unsafe impl Send` in the entire + // crate. There are two members in `FiberFuture` which cause it to not + // be `Send`. One is `current_poll_cx` and is entirely uninteresting. + // This is just used to manage `Context` pointers across `await` points + // in the future, and requires raw pointers to get it to happen easily. + // Nothing too weird about the `Send`-ness, values aren't actually + // crossing threads. + // + // The really interesting piece is `fiber`. Now the "fiber" here is + // actual honest-to-god Rust code which we're moving around. What we're + // doing is the equivalent of moving our thread's stack to another OS + // thread. Turns out we, in general, have no idea what's on the stack + // and would generally have no way to verify that this is actually safe + // to do! + // + // Thankfully, though, Wasmtime has the power. Without being glib it's + // actually worth examining what's on the stack. It's unfortunately not + // super-local to this function itself. Our closure to `Fiber::new` runs + // `func`, which is given to us from the outside. Thankfully, though, we + // have tight control over this. Usage of `on_fiber` is typically done + // *just* before entering WebAssembly itself, so we'll have a few stack + // frames of Rust code (all in Wasmtime itself) before we enter wasm. + // + // Once we've entered wasm, well then we have a whole bunch of wasm + // frames on the stack. We've got this nifty thing called Cranelift, + // though, which allows us to also have complete control over everything + // on the stack! + // + // Finally, when wasm switches back to the fiber's starting pointer + // (this future we're returning) then it means wasm has reentered Rust. + // Suspension can only happen via the `block_on` function of an + // `AsyncCx`. This, conveniently, also happens entirely in Wasmtime + // controlled code! + // + // There's an extremely important point that should be called out here. + // User-provided futures **are not on the stack** during suspension + // points. This is extremely crucial because we in general cannot reason + // about Send/Sync for stack-local variables since rustc doesn't analyze + // them at all. With our construction, though, we are guaranteed that + // Wasmtime owns all stack frames between the stack of a fiber and when + // the fiber suspends (and it could move across threads). At this time + // the only user-provided piece of data on the stack is the future + // itself given to us. Lo-and-behold as you might notice the future is + // required to be `Send`! + // + // What this all boils down to is that we, as the authors of Wasmtime, + // need to be extremely careful that on the async fiber stack we only + // store Send things. For example we can't start using `Rc` willy nilly + // by accident and leave a copy in TLS somewhere. (similarly we have to + // be ready for TLS to change while we're executing wasm code between + // suspension points). + // + // While somewhat onerous it shouldn't be too too hard (the TLS bit is + // the hardest bit so far). This does mean, though, that no user should + // ever hae to worry about the `Send`-ness of Wasmtime. If rustc says + // it's ok, then it's ok. + // + // With all that in mind we unsafely assert here that wasmtime is + // correct. We declare the fiber as only containing Send data on its + // stack, despite not knowing for sure at compile time that this is + // correct. That's what `unsafe` in Rust is all about, though, right? unsafe impl Send for FiberFuture<'_> {} impl Future for FiberFuture<'_> { diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 474169da8bd4..62ca446dd3d4 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -18,31 +18,28 @@ pub fn link_spectest(linker: &mut Linker<()>, store: &mut Store<()>) -> Result<( println!("{}: f64", f2); })?; - // TODO: this is a bummer - let mut store = store; - let ty = GlobalType::new(ValType::I32, Mutability::Const); - let g = Global::new(&mut store, ty, Val::I32(666))?; + let g = Global::new(&mut *store, ty, Val::I32(666))?; linker.define("spectest", "global_i32", g)?; let ty = GlobalType::new(ValType::I64, Mutability::Const); - let g = Global::new(&mut store, ty, Val::I64(666))?; + let g = Global::new(&mut *store, ty, Val::I64(666))?; linker.define("spectest", "global_i64", g)?; let ty = GlobalType::new(ValType::F32, Mutability::Const); - let g = Global::new(&mut store, ty, Val::F32(0x4426_8000))?; + let g = Global::new(&mut *store, ty, Val::F32(0x4426_8000))?; linker.define("spectest", "global_f32", g)?; let ty = GlobalType::new(ValType::F64, Mutability::Const); - let g = Global::new(&mut store, ty, Val::F64(0x4084_d000_0000_0000))?; + let g = Global::new(&mut *store, ty, Val::F64(0x4084_d000_0000_0000))?; linker.define("spectest", "global_f64", g)?; let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20))); - let table = Table::new(&mut store, ty, Val::FuncRef(None))?; + let table = Table::new(&mut *store, ty, Val::FuncRef(None))?; linker.define("spectest", "table", table)?; let ty = MemoryType::new(Limits::new(1, Some(2))); - let memory = Memory::new(&mut store, ty)?; + let memory = Memory::new(&mut *store, ty)?; linker.define("spectest", "memory", memory)?; Ok(()) diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index a2ce947b5806..77fca091daf7 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -189,8 +189,22 @@ fn generate_func( return Err(wasmtime::Trap::new("missing required memory export")); } }; + // Note the unsafety here. Our goal is to simultaneously borrow the + // memory and custom data from `caller`, and the store it's connected + // to. Rust will not let us do that, however, because we must call two + // separate methods (both of which borrow the whole `caller`) and one of + // our borrows is mutable (the custom data). + // + // This operation, however, is safe because these borrows do not overlap + // and in the process of borrowing them mutability doesn't actually + // touch anything. This is akin to mutably borrowing two indices in an + // array, which is safe so long as the indices are separate. + // + // TODO: depending on how common this is for other users to run into we + // may wish to consider adding a dedicated method for this. For now the + // future of `GuestPtr` may be a bit hazy, so let's just get this + // working from the previous iteration for now. let (ctx, mem) = unsafe { - // TODO: doc this let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); (caller.data_mut().borrow_mut(), #runtime::WasmtimeGuestMemory::new(mem)) }; From f013f59add2f4ce2df949e1515f708932df8e51b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 21:26:22 -0700 Subject: [PATCH 19/90] The old backend is... old... right? --- build.rs | 9 ++++++++- tests/all/gc.rs | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 9ee3b893c784..8f20bfceedc0 100644 --- a/build.rs +++ b/build.rs @@ -219,10 +219,17 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool { _ => (), }, "Cranelift" => match (testsuite, testname) { + // Skip all reference types tests on the old backend. The modern + // implementation of reference types uses atomic instructions + // for reference counts on `externref`, but the old backend does not + // implement atomic instructions. + ("reference_types", _) if cfg!(feature = "old-x86-backend") => return true, + // Skip all SIMD tests on old backend, there are instructions not + // implemented there and support is generally not maintained. + ("simd", _) if cfg!(feature = "old-x86-backend") => return true, // No simd support yet for s390x. ("simd", _) if platform_is_s390x() => return true, - ("simd", _) if cfg!(feature = "old-x86-backend") => return true, // skip all SIMD tests on old backend. // These are new instructions that are not really implemented in any backend. ("simd", "simd_i8x16_arith2") | ("simd", "simd_conversions") diff --git a/tests/all/gc.rs b/tests/all/gc.rs index ed93ede16344..45ce2ab21687 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -188,6 +188,7 @@ fn many_live_refs() -> anyhow::Result<()> { } #[test] +#[cfg(not(feature = "old-x86-backend"))] // uses atomic instrs not implemented here fn drop_externref_via_table_set() -> anyhow::Result<()> { let (mut store, module) = ref_types_module( r#" @@ -284,6 +285,7 @@ fn global_drops_externref() -> anyhow::Result<()> { } #[test] +#[cfg(not(feature = "old-x86-backend"))] // uses atomic instrs not implemented here fn table_drops_externref() -> anyhow::Result<()> { test_engine(&Engine::default())?; test_engine(&Engine::new(Config::new().allocation_strategy( @@ -335,6 +337,7 @@ fn table_drops_externref() -> anyhow::Result<()> { } #[test] +#[cfg(not(feature = "old-x86-backend"))] // uses atomic instrs not implemented here fn gee_i_sure_hope_refcounting_is_atomic() -> anyhow::Result<()> { let mut config = Config::new(); config.wasm_reference_types(true); From fdff3b903b4d474c3fe52505aac37b8a51f182f6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 May 2021 21:40:22 -0700 Subject: [PATCH 20/90] Fix a uffd test --- crates/runtime/src/instance/allocator/pooling/uffd.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/runtime/src/instance/allocator/pooling/uffd.rs b/crates/runtime/src/instance/allocator/pooling/uffd.rs index 5357d0ffc3ff..c6fa0f57cffd 100644 --- a/crates/runtime/src/instance/allocator/pooling/uffd.rs +++ b/crates/runtime/src/instance/allocator/pooling/uffd.rs @@ -521,10 +521,7 @@ mod test { }, shared_signatures: VMSharedSignatureIndex::default().into(), host_state: Box::new(()), - interrupts: ptr::null(), - externref_activations_table: ptr::null_mut(), - module_info_lookup: None, - limiter: None, + store: None, }, ) .expect("instance should allocate"), From 7d9a2b5de7ded253eea4021ccc57f95a2454c470 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 07:07:34 -0700 Subject: [PATCH 21/90] Remove unused import --- crates/runtime/src/instance/allocator/pooling/uffd.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/runtime/src/instance/allocator/pooling/uffd.rs b/crates/runtime/src/instance/allocator/pooling/uffd.rs index c6fa0f57cffd..c0aef40a74ce 100644 --- a/crates/runtime/src/instance/allocator/pooling/uffd.rs +++ b/crates/runtime/src/instance/allocator/pooling/uffd.rs @@ -436,7 +436,6 @@ mod test { Imports, InstanceAllocationRequest, InstanceLimits, ModuleLimits, PoolingAllocationStrategy, VMSharedSignatureIndex, }; - use std::ptr; use std::sync::Arc; use wasmtime_environ::{entity::PrimaryMap, wasm::Memory, MemoryPlan, MemoryStyle, Module}; From b5d8c67d855482de849df00e12f6515c621023fd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 07:58:01 -0700 Subject: [PATCH 22/90] Update tarball creation --- ci/build-tarballs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh index 117f38d38e7d..bde9d6352d34 100755 --- a/ci/build-tarballs.sh +++ b/ci/build-tarballs.sh @@ -51,6 +51,6 @@ mkdir tmp/$api_pkgname/lib mkdir tmp/$api_pkgname/include cp LICENSE README.md tmp/$api_pkgname mv bins-$platform/* tmp/$api_pkgname/lib +cp -r crates/c-api/include tmp/$api_pkgname cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include -cp crates/c-api/include/{wasmtime,wasi}.h tmp/$api_pkgname/include mktarball $api_pkgname From 391d9b1e558ccfb055d6da68c88fb7a82cc14f03 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 08:10:45 -0700 Subject: [PATCH 23/90] Update the book docs --- docs/lang-rust.md | 65 ++++++++++++++++++++++++++++++++--------------- docs/wasm-wat.md | 11 ++++---- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/docs/lang-rust.md b/docs/lang-rust.md index e5974cb31297..ecc40906c212 100644 --- a/docs/lang-rust.md +++ b/docs/lang-rust.md @@ -55,10 +55,9 @@ use std::error::Error; use wasmtime::*; fn main() -> Result<(), Box> { + // An engine stores and configures global compilation settings like + // optimization level, enabled wasm features, etc. let engine = Engine::default(); - // A `Store` is a sort of "global object" in a sense, but for now it suffices - // to say that it's generally passed to most constructors. - let store = Store::new(&engine); # if false { // We start off by creating a `Module` which represents a compiled form @@ -68,24 +67,30 @@ fn main() -> Result<(), Box> { # } # let module = Module::new(&engine, r#"(module (func (export "answer") (result i32) i32.const 42))"#)?; - // After we have a compiled `Module` we can then instantiate it, creating + // A `Store` is what will own instances, functions, globals, etc. All wasm + // items are stored within a `Store`, and it's what we'll always be using to + // interact with the wasm world. Custom data can be stored in stores but for + // now we just use `()`. + let mut store = Store::new(&engine, ()); + + // With a compiled `Module` we can then instantiate it, creating // an `Instance` which we can actually poke at functions on. - let instance = Instance::new(&store, &module, &[])?; + let instance = Instance::new(&mut store, &module, &[])?; // The `Instance` gives us access to various exported functions and items, // which we access here to pull out our `answer` exported function and // run it. - let answer = instance.get_func("answer") + let answer = instance.get_func(&mut store, "answer") .expect("`answer` was not an exported function"); // There's a few ways we can call the `answer` `Func` value. The easiest // is to statically assert its signature with `typed` (in this case // asserting it takes no arguments and returns one i32) and then call it. - let answer = answer.typed::<(), i32>()?; + let answer = answer.typed::<(), i32, _>(&store)?; // And finally we can call our function! Note that the error propagation // with `?` is done to handle the case where the wasm function traps. - let result = answer.call(())?; + let result = answer.call(&mut store, ())?; println!("Answer: {:?}", result); Ok(()) } @@ -142,30 +147,50 @@ looks like this: use std::error::Error; use wasmtime::*; +struct Log { + integers_logged: Vec, +} + fn main() -> Result<(), Box> { let engine = Engine::default(); - let store = Store::new(&engine); # if false { let module = Module::from_file(&engine, "hello.wat")?; # } # let module = Module::new(&engine, r#"(module (import "" "log" (func $log (param i32))) (import "" "double" (func $double (param i32) (result i32))) (func (export "run") i32.const 0 call $log i32.const 1 call $log i32.const 2 call $double call $log))"#)?; - // First we can create our `log` function, which will simply print out the - // parameter it receives. - let log = Func::wrap(&store, |param: i32| { + // For host-provided functions it's recommended to use a `Linker` which does + // name-based resolution of functions. + let mut linker = Linker::new(&engine); + + // First we create our simple "double" function which will only multiply its + // input by two and return it. + linker.func_wrap("", "double", |param: i32| param * 2); + + // Next we define a `log` function. Note that we're using a + // Wasmtime-provided `Caller` argument to access the state on the `Store`, + // which allows us to record the logged information. + linker.func_wrap("", "log", |mut caller: Caller<'_, Log>, param: u32| { println!("log: {}", param); + caller.data_mut().integers_logged.push(param); }); - // Next we can create our double function which doubles the input it receives. - let double = Func::wrap(&store, |param: i32| param * 2); + // As above, instantiation always happens within a `Store`. This means to + // actually instantiate with our `Linker` we'll need to create a store. Note + // that we're also initializing the store with our custom data here too. + // + // Afterwards we use the `linker` to create the instance. + let data = Log { integers_logged: Vec::new() }; + let mut store = Store::new(&engine, data); + let instance = linker.instantiate(&mut store, &module)?; + + // Like before, we can get the run function and execute it. + let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?; + run.call(&mut store, ())?; - // When instantiating the module we now need to provide the imports to the - // instantiation process. This is the second slice argument, where each - // entry in the slice must line up with the imports in the module. - let instance = Instance::new(&store, &module, &[log.into(), double.into()])?; + // We can also inspect what integers were logged: + println!("logged integers: {:?}", store.data().integers_logged); - let run = instance.get_typed_func::<(), ()>("run")?; - Ok(run.call(())?) + Ok(()) } ``` diff --git a/docs/wasm-wat.md b/docs/wasm-wat.md index 9df253699368..c08fc49e71d0 100644 --- a/docs/wasm-wat.md +++ b/docs/wasm-wat.md @@ -37,8 +37,7 @@ You can also see how this works in the Rust API like so: use wasmtime::*; # fn main() -> anyhow::Result<()> { -let engine = Engine::default(); -let store = Store::new(&engine); +let mut store = Store::<()>::default(); let wat = r#" (module (func (export "add") (param i32 i32) (result i32) @@ -46,10 +45,10 @@ let wat = r#" local.get 1 i32.add)) "#; -let module = Module::new(&engine, wat)?; -let instance = Instance::new(&store, &module, &[])?; -let add = instance.get_typed_func::<(i32, i32), i32>("add")?; -println!("1 + 2 = {}", add.call((1, 2))?); +let module = Module::new(store.engine(), wat)?; +let instance = Instance::new(&mut store, &module, &[])?; +let add = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "add")?; +println!("1 + 2 = {}", add.call(&mut store, (1, 2))?); # Ok(()) # } ``` From 3beb706faab63cc034bdeeca2b39d9b7001c24ce Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 09:25:35 -0700 Subject: [PATCH 24/90] Try to get docs working --- .github/workflows/main.yml | 7 +- crates/c-api/doxygen.conf | 253 +++++++++++-------- crates/c-api/include/doc-wasm.h | 293 ++++++++++------------- crates/c-api/include/wasi.h | 4 +- crates/c-api/include/wasmtime.h | 7 +- crates/c-api/include/wasmtime/config.h | 6 + crates/c-api/include/wasmtime/error.h | 11 +- crates/c-api/include/wasmtime/extern.h | 49 ++++ crates/c-api/include/wasmtime/func.h | 13 + crates/c-api/include/wasmtime/global.h | 8 + crates/c-api/include/wasmtime/instance.h | 9 + crates/c-api/include/wasmtime/linker.h | 20 +- crates/c-api/include/wasmtime/memory.h | 12 + crates/c-api/include/wasmtime/module.h | 22 +- crates/c-api/include/wasmtime/store.h | 20 +- crates/c-api/include/wasmtime/table.h | 8 + crates/c-api/include/wasmtime/trap.h | 7 + crates/c-api/include/wasmtime/val.h | 56 ++++- crates/wasmtime/src/func.rs | 37 +-- crates/wasmtime/src/limits.rs | 2 +- crates/wasmtime/src/linker.rs | 11 +- crates/wasmtime/src/memory.rs | 7 - 22 files changed, 535 insertions(+), 327 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bfc9e7807d02..1ad9f597483a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -92,12 +92,9 @@ jobs: doc_capi: name: Doc - build the C API documentation runs-on: ubuntu-latest - container: ubuntu:20.04 steps: - - run: apt-get update && apt-get install -y doxygen git - - uses: actions/checkout@v2 - with: - submodules: true + - run: curl -L https://doxygen.nl/files/doxygen-1.9.1.linux.bin.tar.gz | tar xzf - + - run: echo "`pwd`/doxygen-1.9.1/bin" >> $GITHUB_PATH - run: cd crates/c-api && doxygen doxygen.conf - uses: actions/upload-artifact@v1 with: diff --git a/crates/c-api/doxygen.conf b/crates/c-api/doxygen.conf index b8c1b5273c92..4e43ca4076de 100644 --- a/crates/c-api/doxygen.conf +++ b/crates/c-api/doxygen.conf @@ -1,4 +1,4 @@ -# Doxyfile 1.8.17 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Wasmtime" +PROJECT_NAME = Wasmtime # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -227,6 +227,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -263,12 +271,6 @@ TAB_SIZE = 4 ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -310,18 +312,21 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is -# Fortran), use: inc=Fortran f=C. +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = @@ -455,6 +460,19 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -518,6 +536,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -555,11 +580,18 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) ands Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES @@ -798,7 +830,10 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = YES @@ -829,13 +864,13 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = +INPUT = include wasm-c-api/include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 @@ -848,13 +883,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen -# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h @@ -899,13 +936,14 @@ EXCLUDE_PATTERNS = # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = assertions \ - WASM_DECLARE_* \ - WASM_API_EXTERN \ - WASI_API_EXTERN \ - WASMTIME_DECLARE_OWN \ - WASI_DECLARE_OWN \ - WASMTIME_CONFIG_PROP \ - own + WASM_DECLARE_* \ + WASM_API_EXTERN \ + WASI_API_EXTERN \ + WASMTIME_DECLARE_OWN \ + WASI_DECLARE_OWN \ + WASMTIME_CONFIG_PROP \ + own \ + wasm_*_enum # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -1075,35 +1113,6 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse_libclang=ON option for CMake. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = NO - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = - -# If clang assisted parsing is enabled you can provide the clang parser with the -# path to the compilation database (see: -# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files -# were built. This is equivalent to specifying the "-p" option to a clang tool, -# such as clang-check. These options will then be passed to the parser. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse_libclang=ON option for CMake. - -CLANG_DATABASE_PATH = - #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1115,13 +1124,6 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored @@ -1292,10 +1294,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1337,8 +1340,8 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1368,7 +1371,7 @@ CHM_FILE = HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1413,7 +1416,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1421,8 +1425,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1430,16 +1434,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1451,9 +1455,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1504,7 +1508,7 @@ DISABLE_INDEX = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. @@ -1530,6 +1534,17 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1569,7 +1584,7 @@ USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1585,7 +1600,7 @@ MATHJAX_FORMAT = HTML-CSS # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ @@ -1599,7 +1614,8 @@ MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1646,7 +1662,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1659,8 +1676,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1824,9 +1842,11 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2142,7 +2162,8 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = wasm-c-api/include include +INCLUDE_PATH = wasm-c-api/include \ + include # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -2150,7 +2171,7 @@ INCLUDE_PATH = wasm-c-api/include include # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = *.h # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. @@ -2258,9 +2279,9 @@ HIDE_UNDOC_RELATIONS = YES # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: YES. +# The default value is: NO. -HAVE_DOT = YES +HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2337,10 +2358,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2414,9 +2457,7 @@ DIRECTORY_GRAPH = YES # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, -# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, -# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:gdiplus:gdiplus. # The default value is: png. @@ -2532,9 +2573,11 @@ DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/crates/c-api/include/doc-wasm.h b/crates/c-api/include/doc-wasm.h index 356db1e1bc61..9bbf5c1026ed 100644 --- a/crates/c-api/include/doc-wasm.h +++ b/crates/c-api/include/doc-wasm.h @@ -217,14 +217,14 @@ * at * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html. * - * \fn own wasm_config_t *wasm_config_new(void); + * \fn wasm_config_t *wasm_config_new(void); * \brief Creates a new empty configuration object. * * The object returned is owned by the caller and will need to be deleted with * #wasm_config_delete. May return `NULL` if a configuration object could not be * allocated. * - * \fn void wasm_config_delete(own wasm_config_t*); + * \fn void wasm_config_delete(wasm_config_t*); * \brief Deletes a configuration object. */ @@ -244,14 +244,14 @@ * within the same engine with each store living on a separate thread. Typically * you'll create one #wasm_engine_t for the lifetime of your program. * - * \fn own wasm_engine_t *wasm_engine_new(void); + * \fn wasm_engine_t *wasm_engine_new(void); * \brief Creates a new engine with the default configuration. * * The object returned is owned by the caller and will need to be deleted with * #wasm_engine_delete. This may return `NULL` if the engine could not be * allocated. * - * \fn own wasm_engine_t *wasm_engine_new_with_config(wasm_config_t *); + * \fn wasm_engine_t *wasm_engine_new_with_config(wasm_config_t *); * \brief Creates a new engine with the specified configuration. * * This function will take ownership of the configuration specified regardless @@ -260,7 +260,7 @@ * be deleted with #wasm_engine_delete. This may return `NULL` if the engine * could not be allocated. * - * \fn void wasm_engine_delete(own wasm_engine_t*); + * \fn void wasm_engine_delete(wasm_engine_t*); * \brief Deletes an engine. */ @@ -274,14 +274,14 @@ * A #wasm_store_t corresponds to the concept of an [embedding * store](https://webassembly.github.io/spec/core/exec/runtime.html#store) * - * \fn own wasm_store_t *wasm_store_new(wasm_engine_t *); + * \fn wasm_store_t *wasm_store_new(wasm_engine_t *); * \brief Creates a new store within the specified engine. * * The object returned is owned by the caller and will need to be deleted with * #wasm_store_delete. This may return `NULL` if the store could not be * allocated. * - * \fn void wasm_store_delete(own wasm_store_t *); + * \fn void wasm_store_delete(wasm_store_t *); * \brief Deletes the specified store. */ @@ -316,10 +316,10 @@ * * \fn wasm_name_new_new_uninitialized * \brief Convenience alias - * + * * \fn wasm_name_new_from_string * \brief Create a new name from a C string. - * + * * \fn wasm_name_new_from_string_nt * \brief Create a new name from a C string with null terminator. * @@ -329,10 +329,10 @@ * \fn wasm_name_delete * \brief Convenience alias * - * \fn void wasm_byte_vec_new_empty(own wasm_byte_vec_t *out); + * \fn void wasm_byte_vec_new_empty(wasm_byte_vec_t *out); * \brief Initializes an empty byte vector. * - * \fn void wasm_byte_vec_new_uninitialized(own wasm_byte_vec_t *out, size_t); + * \fn void wasm_byte_vec_new_uninitialized(wasm_byte_vec_t *out, size_t); * \brief Initializes an byte vector with the specified capacity. * * This function will initialize the provided vector with capacity to hold the @@ -340,7 +340,7 @@ * initialized and after this function is called you are then responsible for * ensuring #wasm_byte_vec_delete is called. * - * \fn void wasm_byte_vec_new(own wasm_byte_vec_t *out, size_t, own wasm_byte_t const[]); + * \fn void wasm_byte_vec_new(wasm_byte_vec_t *out, size_t, wasm_byte_t const[]); * \brief Copies the specified data into a new byte vector. * * This function will copy the provided data into this byte vector. The byte @@ -351,14 +351,14 @@ * must be managed externally. This function will copy the contents to the * output vector, but it's up to the caller to properly deallocate the memory. * - * \fn void wasm_byte_vec_copy(own wasm_byte_vec_t *out, const wasm_byte_vec_t *); + * \fn void wasm_byte_vec_copy(wasm_byte_vec_t *out, const wasm_byte_vec_t *); * \brief Copies one vector into a new vector. * * Copies the second argument's data into the first argument. The `out` vector * should not be previously initialized and after this function returns you're * responsible for calling #wasm_byte_vec_delete. * - * \fn void wasm_byte_vec_delete(own wasm_byte_vec_t *); + * \fn void wasm_byte_vec_delete(wasm_byte_vec_t *); * \brief Deletes a byte vector. * * This function will deallocate the data referenced by the argument provided. @@ -385,40 +385,40 @@ * \typedef wasm_valtype_vec_t * \brief Convenience alias for #wasm_valtype_vec_t * - * \fn void wasm_valtype_delete(own wasm_valtype_t *); + * \fn void wasm_valtype_delete(wasm_valtype_t *); * \brief Deletes a type. * - * \fn void wasm_valtype_vec_new_empty(own wasm_valtype_vec_t *out); + * \fn void wasm_valtype_vec_new_empty(wasm_valtype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_valtype_vec_new_uninitialized(own wasm_valtype_vec_t *out, size_t); + * \fn void wasm_valtype_vec_new_uninitialized(wasm_valtype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_valtype_vec_new(own wasm_valtype_vec_t *out, size_t, own wasm_valtype_t *const[]); + * \fn void wasm_valtype_vec_new(wasm_valtype_vec_t *out, size_t, wasm_valtype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_valtype_vec_copy(own wasm_valtype_vec_t *out, const wasm_valtype_vec_t *) + * \fn void wasm_valtype_vec_copy(wasm_valtype_vec_t *out, const wasm_valtype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_valtype_vec_delete(own wasm_valtype_vec_t *out) + * \fn void wasm_valtype_vec_delete(wasm_valtype_vec_t *out) * \brief Deallocates memory for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_valtype_t* wasm_valtype_copy(const wasm_valtype_t *) + * \fn wasm_valtype_t* wasm_valtype_copy(const wasm_valtype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. * - * \fn own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); + * \fn wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); * \brief Creates a new value type from the specified kind. * * The caller is responsible for deleting the returned value. @@ -429,10 +429,6 @@ /** * \typedef wasm_valkind_t - * \brief Indicator for the kind of a type, with values defined by - * #wasm_valkind_enum - * - * \enum wasm_valkind_enum * \brief Different kinds of types supported in wasm. */ @@ -455,40 +451,40 @@ * \typedef wasm_functype_vec_t * \brief Convenience alias for #wasm_functype_vec_t * - * \fn void wasm_functype_delete(own wasm_functype_t *); + * \fn void wasm_functype_delete(wasm_functype_t *); * \brief Deletes a type. * - * \fn void wasm_functype_vec_new_empty(own wasm_functype_vec_t *out); + * \fn void wasm_functype_vec_new_empty(wasm_functype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_functype_vec_new_uninitialized(own wasm_functype_vec_t *out, size_t); + * \fn void wasm_functype_vec_new_uninitialized(wasm_functype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_functype_vec_new(own wasm_functype_vec_t *out, size_t, own wasm_functype_t *const[]); + * \fn void wasm_functype_vec_new(wasm_functype_vec_t *out, size_t, wasm_functype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_functype_vec_copy(own wasm_functype_vec_t *out, const wasm_functype_vec_t *) + * \fn void wasm_functype_vec_copy(wasm_functype_vec_t *out, const wasm_functype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_functype_vec_delete(own wasm_functype_vec_t *out) + * \fn void wasm_functype_vec_delete(wasm_functype_vec_t *out) * \brief Deallocates memory for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_functype_t* wasm_functype_copy(const wasm_functype_t *) + * \fn wasm_functype_t* wasm_functype_copy(const wasm_functype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. * - * \fn own wasm_functype_t* wasm_functype_new(wasm_valtype_vec_t *params, wasm_valtype_vec_t *results); + * \fn wasm_functype_t* wasm_functype_new(wasm_valtype_vec_t *params, wasm_valtype_vec_t *results); * \brief Creates a new function type with the provided parameter and result * types. * @@ -526,40 +522,40 @@ * \typedef wasm_globaltype_vec_t * \brief Convenience alias for #wasm_globaltype_vec_t * - * \fn void wasm_globaltype_delete(own wasm_globaltype_t *); + * \fn void wasm_globaltype_delete(wasm_globaltype_t *); * \brief Deletes a type. * - * \fn void wasm_globaltype_vec_new_empty(own wasm_globaltype_vec_t *out); + * \fn void wasm_globaltype_vec_new_empty(wasm_globaltype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_globaltype_vec_new_uninitialized(own wasm_globaltype_vec_t *out, size_t); + * \fn void wasm_globaltype_vec_new_uninitialized(wasm_globaltype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_globaltype_vec_new(own wasm_globaltype_vec_t *out, size_t, own wasm_globaltype_t *const[]); + * \fn void wasm_globaltype_vec_new(wasm_globaltype_vec_t *out, size_t, wasm_globaltype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_globaltype_vec_copy(own wasm_globaltype_vec_t *out, const wasm_globaltype_vec_t *) + * \fn void wasm_globaltype_vec_copy(wasm_globaltype_vec_t *out, const wasm_globaltype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_globaltype_vec_delete(own wasm_globaltype_vec_t *out) + * \fn void wasm_globaltype_vec_delete(wasm_globaltype_vec_t *out) * \brief Deallocates memory for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_globaltype_t* wasm_globaltype_copy(const wasm_globaltype_t *) + * \fn wasm_globaltype_t* wasm_globaltype_copy(const wasm_globaltype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. * - * \fn own wasm_globaltype_t* wasm_globaltype_new(wasm_valtype_t *, wasm_mutability_t) + * \fn wasm_globaltype_t* wasm_globaltype_new(wasm_valtype_t *, wasm_mutability_t) * \brief Creates a new global type. * * This function takes ownership of the #wasm_valtype_t argument. @@ -578,9 +574,6 @@ /** * \typedef wasm_mutability_t - * \brief Typedef for the #wasm_mutability_enum values. - * - * \enum wasm_mutability_enum * \brief Boolean flag for whether a global is mutable or not. */ @@ -603,40 +596,40 @@ * \typedef wasm_tabletype_vec_t * \brief Convenience alias for #wasm_tabletype_vec_t * - * \fn void wasm_tabletype_delete(own wasm_tabletype_t *); + * \fn void wasm_tabletype_delete(wasm_tabletype_t *); * \brief Deletes a type. * - * \fn void wasm_tabletype_vec_new_empty(own wasm_tabletype_vec_t *out); + * \fn void wasm_tabletype_vec_new_empty(wasm_tabletype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_tabletype_vec_new_uninitialized(own wasm_tabletype_vec_t *out, size_t); + * \fn void wasm_tabletype_vec_new_uninitialized(wasm_tabletype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_tabletype_vec_new(own wasm_tabletype_vec_t *out, size_t, own wasm_tabletype_t *const[]); + * \fn void wasm_tabletype_vec_new(wasm_tabletype_vec_t *out, size_t, wasm_tabletype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_tabletype_vec_copy(own wasm_tabletype_vec_t *out, const wasm_tabletype_vec_t *) + * \fn void wasm_tabletype_vec_copy(wasm_tabletype_vec_t *out, const wasm_tabletype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_tabletype_vec_delete(own wasm_tabletype_vec_t *out) + * \fn void wasm_tabletype_vec_delete(wasm_tabletype_vec_t *out) * \brief Deallocates memory for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_tabletype_t* wasm_tabletype_copy(const wasm_tabletype_t *) + * \fn wasm_tabletype_t* wasm_tabletype_copy(const wasm_tabletype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. * - * \fn own wasm_tabletype_t* wasm_tabletype_new(wasm_valtype_t *, const wasm_limits_t *)h + * \fn wasm_tabletype_t* wasm_tabletype_new(wasm_valtype_t *, const wasm_limits_t *)h * \brief Creates a new table type. * * This function takes ownership of the #wasm_valtype_t argument, but does not @@ -689,40 +682,40 @@ * \typedef wasm_memorytype_vec_t * \brief Convenience alias for #wasm_memorytype_vec_t * - * \fn void wasm_memorytype_delete(own wasm_memorytype_t *); + * \fn void wasm_memorytype_delete(wasm_memorytype_t *); * \brief Deletes a type. * - * \fn void wasm_memorytype_vec_new_empty(own wasm_memorytype_vec_t *out); + * \fn void wasm_memorytype_vec_new_empty(wasm_memorytype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_memorytype_vec_new_uninitialized(own wasm_memorytype_vec_t *out, size_t); + * \fn void wasm_memorytype_vec_new_uninitialized(wasm_memorytype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_memorytype_vec_new(own wasm_memorytype_vec_t *out, size_t, own wasm_memorytype_t *const[]); + * \fn void wasm_memorytype_vec_new(wasm_memorytype_vec_t *out, size_t, wasm_memorytype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_memorytype_vec_copy(own wasm_memorytype_vec_t *out, const wasm_memorytype_vec_t *) + * \fn void wasm_memorytype_vec_copy(wasm_memorytype_vec_t *out, const wasm_memorytype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_memorytype_vec_delete(own wasm_memorytype_vec_t *out) + * \fn void wasm_memorytype_vec_delete(wasm_memorytype_vec_t *out) * \brief Deallocates memory for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_memorytype_t* wasm_memorytype_copy(const wasm_memorytype_t *) + * \fn wasm_memorytype_t* wasm_memorytype_copy(const wasm_memorytype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. * - * \fn own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t *)h + * \fn wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t *)h * \brief Creates a new memory type. * * This function takes ownership of the #wasm_valtype_t argument, but does not @@ -758,35 +751,35 @@ * \typedef wasm_externtype_vec_t * \brief Convenience alias for #wasm_externtype_vec_t * - * \fn void wasm_externtype_delete(own wasm_externtype_t *); + * \fn void wasm_externtype_delete(wasm_externtype_t *); * \brief Deletes a type. * - * \fn void wasm_externtype_vec_new_empty(own wasm_externtype_vec_t *out); + * \fn void wasm_externtype_vec_new_empty(wasm_externtype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_externtype_vec_new_uninitialized(own wasm_externtype_vec_t *out, size_t); + * \fn void wasm_externtype_vec_new_uninitialized(wasm_externtype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_externtype_vec_new(own wasm_externtype_vec_t *out, size_t, own wasm_externtype_t *const[]); + * \fn void wasm_externtype_vec_new(wasm_externtype_vec_t *out, size_t, wasm_externtype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_externtype_vec_copy(own wasm_externtype_vec_t *out, const wasm_externtype_vec_t *) + * \fn void wasm_externtype_vec_copy(wasm_externtype_vec_t *out, const wasm_externtype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_externtype_vec_delete(own wasm_externtype_vec_t *out) + * \fn void wasm_externtype_vec_delete(wasm_externtype_vec_t *out) * \brief Deallocates extern for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_externtype_t* wasm_externtype_copy(const wasm_externtype_t *) + * \fn wasm_externtype_t* wasm_externtype_copy(const wasm_externtype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. @@ -797,17 +790,10 @@ /** * \typedef wasm_externkind_t - * \brief Classifier for #wasm_externtype_t, defined by #wasm_externkind_enum + * \brief Classifier for #wasm_externtype_t * * This is returned from #wasm_extern_kind and #wasm_externtype_kind to * determine what kind of type is wrapped. - * - * \enum wasm_externkind_enum - * \brief Kinds of external items for a wasm module. - * - * Note that this also includes #WASM_EXTERN_INSTANCE as well as - * #WASM_EXTERN_MODULE and is intended to be used when #wasm_externkind_t is - * used. */ /** @@ -935,40 +921,40 @@ * \typedef wasm_importtype_vec_t * \brief Convenience alias for #wasm_importtype_vec_t * - * \fn void wasm_importtype_delete(own wasm_importtype_t *); + * \fn void wasm_importtype_delete(wasm_importtype_t *); * \brief Deletes a type. * - * \fn void wasm_importtype_vec_new_empty(own wasm_importtype_vec_t *out); + * \fn void wasm_importtype_vec_new_empty(wasm_importtype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_importtype_vec_new_uninitialized(own wasm_importtype_vec_t *out, size_t); + * \fn void wasm_importtype_vec_new_uninitialized(wasm_importtype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_importtype_vec_new(own wasm_importtype_vec_t *out, size_t, own wasm_importtype_t *const[]); + * \fn void wasm_importtype_vec_new(wasm_importtype_vec_t *out, size_t, wasm_importtype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_importtype_vec_copy(own wasm_importtype_vec_t *out, const wasm_importtype_vec_t *) + * \fn void wasm_importtype_vec_copy(wasm_importtype_vec_t *out, const wasm_importtype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_importtype_vec_delete(own wasm_importtype_vec_t *out) + * \fn void wasm_importtype_vec_delete(wasm_importtype_vec_t *out) * \brief Deallocates import for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_importtype_t* wasm_importtype_copy(const wasm_importtype_t *) + * \fn wasm_importtype_t* wasm_importtype_copy(const wasm_importtype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. * - * \fn own wasm_importtype_t* wasm_importtype_new(wasm_name_t *module, wasm_name_t *name, wasm_externtype_t *) + * \fn wasm_importtype_t* wasm_importtype_new(wasm_name_t *module, wasm_name_t *name, wasm_externtype_t *) * \brief Creates a new import type. * * This function takes ownership of the `module`, `name`, and @@ -1016,40 +1002,40 @@ * \typedef wasm_exporttype_vec_t * \brief Convenience alias for #wasm_exporttype_vec_t * - * \fn void wasm_exporttype_delete(own wasm_exporttype_t *); + * \fn void wasm_exporttype_delete(wasm_exporttype_t *); * \brief Deletes a type. * - * \fn void wasm_exporttype_vec_new_empty(own wasm_exporttype_vec_t *out); + * \fn void wasm_exporttype_vec_new_empty(wasm_exporttype_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_exporttype_vec_new_uninitialized(own wasm_exporttype_vec_t *out, size_t); + * \fn void wasm_exporttype_vec_new_uninitialized(wasm_exporttype_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_exporttype_vec_new(own wasm_exporttype_vec_t *out, size_t, own wasm_exporttype_t *const[]); + * \fn void wasm_exporttype_vec_new(wasm_exporttype_vec_t *out, size_t, wasm_exporttype_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_exporttype_vec_copy(own wasm_exporttype_vec_t *out, const wasm_exporttype_vec_t *) + * \fn void wasm_exporttype_vec_copy(wasm_exporttype_vec_t *out, const wasm_exporttype_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_exporttype_vec_delete(own wasm_exporttype_vec_t *out) + * \fn void wasm_exporttype_vec_delete(wasm_exporttype_vec_t *out) * \brief Deallocates export for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_exporttype_t* wasm_exporttype_copy(const wasm_exporttype_t *) + * \fn wasm_exporttype_t* wasm_exporttype_copy(const wasm_exporttype_t *) * \brief Creates a new value which matches the provided one. * * The caller is responsible for deleting the returned value. * - * \fn own wasm_exporttype_t* wasm_exporttype_new(wasm_name_t *name, wasm_externtype_t *) + * \fn wasm_exporttype_t* wasm_exporttype_new(wasm_name_t *name, wasm_externtype_t *) * \brief Creates a new export type. * * This function takes ownership of the `name` and @@ -1127,27 +1113,27 @@ * reside on the stack. Instead this only deletes the memory referenced by `v`, * such as the `ref` variant of #wasm_val_t. * - * \fn void wasm_val_vec_new_empty(own wasm_val_vec_t *out); + * \fn void wasm_val_vec_new_empty(wasm_val_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_val_vec_new_uninitialized(own wasm_val_vec_t *out, size_t); + * \fn void wasm_val_vec_new_uninitialized(wasm_val_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_val_vec_new(own wasm_val_vec_t *out, size_t, own wasm_val_t const[]); + * \fn void wasm_val_vec_new(wasm_val_vec_t *out, size_t, wasm_val_t const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_val_vec_copy(own wasm_val_vec_t *out, const wasm_val_vec_t *) + * \fn void wasm_val_vec_copy(wasm_val_vec_t *out, const wasm_val_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_val_vec_delete(own wasm_val_vec_t *out) + * \fn void wasm_val_vec_delete(wasm_val_vec_t *out) * \brief Deallocates export for a vector. * * See #wasm_byte_vec_delete for more information. @@ -1168,10 +1154,10 @@ * \typedef wasm_ref_t * \brief Convenience alias for #wasm_ref_t * - * \fn void wasm_ref_delete(own wasm_ref_t *v); + * \fn void wasm_ref_delete(wasm_ref_t *v); * \brief Delete a reference. * - * \fn own wasm_ref_t *wasm_ref_copy(const wasm_ref_t *) + * \fn wasm_ref_t *wasm_ref_copy(const wasm_ref_t *) * \brief Copy a reference. * * \fn bool wasm_ref_same(const wasm_ref_t *, const wasm_ref_t *) @@ -1209,35 +1195,35 @@ * \typedef wasm_frame_vec_t * \brief Convenience alias for #wasm_frame_vec_t * - * \fn void wasm_frame_delete(own wasm_frame_t *v); + * \fn void wasm_frame_delete(wasm_frame_t *v); * \brief Deletes a frame. * - * \fn void wasm_frame_vec_new_empty(own wasm_frame_vec_t *out); + * \fn void wasm_frame_vec_new_empty(wasm_frame_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_frame_vec_new_uninitialized(own wasm_frame_vec_t *out, size_t); + * \fn void wasm_frame_vec_new_uninitialized(wasm_frame_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_frame_vec_new(own wasm_frame_vec_t *out, size_t, own wasm_frame_t *const[]); + * \fn void wasm_frame_vec_new(wasm_frame_vec_t *out, size_t, wasm_frame_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_frame_vec_copy(own wasm_frame_vec_t *out, const wasm_frame_vec_t *) + * \fn void wasm_frame_vec_copy(wasm_frame_vec_t *out, const wasm_frame_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_frame_vec_delete(own wasm_frame_vec_t *out) + * \fn void wasm_frame_vec_delete(wasm_frame_vec_t *out) * \brief Deallocates export for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_frame_t *wasm_frame_copy(const wasm_frame_t *) + * \fn wasm_frame_t *wasm_frame_copy(const wasm_frame_t *) * \brief Copies a #wasm_frame_t to a new one. * * The caller is responsible for deleting the returned #wasm_frame_t. @@ -1265,10 +1251,10 @@ * \typedef wasm_trap_t * \brief Convenience alias for #wasm_trap_t * - * \fn void wasm_trap_delete(own wasm_trap_t *v); + * \fn void wasm_trap_delete(wasm_trap_t *v); * \brief Deletes a trap. * - * \fn own wasm_trap_t *wasm_trap_copy(const wasm_trap_t *) + * \fn wasm_trap_t *wasm_trap_copy(const wasm_trap_t *) * \brief Copies a #wasm_trap_t to a new one. * * The caller is responsible for deleting the returned #wasm_trap_t. @@ -1317,7 +1303,7 @@ * The caller takes ownership of the returned `out` value and is responsible for * calling #wasm_byte_vec_delete on it. * - * \fn own wasm_frame_t* wasm_trap_origin(const wasm_trap_t *); + * \fn wasm_frame_t* wasm_trap_origin(const wasm_trap_t *); * \brief Returns the top frame of the wasm stack responsible for this trap. * * The caller is responsible for deallocating the returned frame. This function @@ -1340,10 +1326,10 @@ * \typedef wasm_foreign_t * \brief Convenience alias for #wasm_foreign_t * - * \fn void wasm_foreign_delete(own wasm_foreign_t *v); + * \fn void wasm_foreign_delete(wasm_foreign_t *v); * \brief Unimplemented in Wasmtime, aborts the process if called * - * \fn own wasm_foreign_t *wasm_foreign_copy(const wasm_foreign_t *) + * \fn wasm_foreign_t *wasm_foreign_copy(const wasm_foreign_t *) * \brief Unimplemented in Wasmtime, aborts the process if called * * \fn void wasm_foreign_same(const wasm_foreign_t *, const wasm_foreign_t *) @@ -1392,10 +1378,10 @@ * \typedef wasm_shared_module_t * \brief Convenience alias for #wasm_shared_module_t * - * \fn void wasm_module_delete(own wasm_module_t *v); + * \fn void wasm_module_delete(wasm_module_t *v); * \brief Deletes a module. * - * \fn own wasm_module_t *wasm_module_copy(const wasm_module_t *) + * \fn wasm_module_t *wasm_module_copy(const wasm_module_t *) * \brief Copies a #wasm_module_t to a new one. * * The caller is responsible for deleting the returned #wasm_module_t. @@ -1427,10 +1413,10 @@ * \fn wasm_ref_as_module_const(const wasm_ref_t *); * \brief Unimplemented in Wasmtime, aborts the process if called. * - * \fn void wasm_shared_module_delete(own wasm_shared_module_t *); + * \fn void wasm_shared_module_delete(wasm_shared_module_t *); * \brief Deletes the provided module. * - * \fn own wasm_shared_module_t *wasm_module_share(const wasm_module_t *); + * \fn wasm_shared_module_t *wasm_module_share(const wasm_module_t *); * \brief Creates a shareable module from the provided module. * * > Note that this API is not necessary in Wasmtime because #wasm_module_t can @@ -1439,7 +1425,7 @@ * This function does not take ownership of the argument, but the caller is * expected to deallocate the returned #wasm_shared_module_t. * - * \fn own wasm_module_t *wasm_module_obtain(wasm_store_t *, const wasm_shared_module_t *); + * \fn wasm_module_t *wasm_module_obtain(wasm_store_t *, const wasm_shared_module_t *); * \brief Attempts to create a #wasm_module_t from the shareable module. * * > Note that this API is not necessary in Wasmtime because #wasm_module_t can @@ -1451,7 +1437,7 @@ * This function may fail if the engines associated with the #wasm_store_t or * #wasm_shared_module_t are different. * - * \fn own wasm_module_t *wasm_module_new(wasm_store_t *, const wasm_byte_vec_t *binary) + * \fn wasm_module_t *wasm_module_new(wasm_store_t *, const wasm_byte_vec_t *binary) * \brief Compiles a raw WebAssembly binary to a #wasm_module_t. * * This function will validate and compile the provided binary. The returned @@ -1463,9 +1449,6 @@ * This function may fail if the provided binary is not a WebAssembly binary or * if it does not pass validation. In these cases this function returns `NULL`. * - * > Note: for a richer error message it's recommended to use - * > #wasmtime_module_new. - * * \fn bool wasm_module_validate(wasm_store_t *, const wasm_byte_vec_t *binary); * \brief Validates whether a provided byte sequence is a valid wasm binary. * @@ -1473,15 +1456,12 @@ * `binary` is a valid WebAssembly binary according to the configuration of the * #wasm_store_t provided. * - * > Note: for a richer error message for invalid binaries you can use - * #wasmtime_module_validate. - * * \fn void wasm_module_imports(const wasm_module_t *, wasm_importtype_vec_t *out); * \brief Returns the list of imports that this module expects. * * The list of imports returned are the types of items expected to be passed to - * #wasm_instance_new (or #wasmtime_instance_new). You can use - * #wasm_importtype_type to learn about the expected type of each import. + * #wasm_instance_new. You can use #wasm_importtype_type to learn about the + * expected type of each import. * * This function does not take ownership of the provided module but ownership of * `out` is passed to the caller. Note that `out` is treated as uninitialized @@ -1536,10 +1516,10 @@ * #wasm_func_callback_t, except the first argument is the same `void*` argument * passed to #wasm_func_new_with_env. * - * \fn void wasm_func_delete(own wasm_func_t *v); + * \fn void wasm_func_delete(wasm_func_t *v); * \brief Deletes a func. * - * \fn own wasm_func_t *wasm_func_copy(const wasm_func_t *) + * \fn wasm_func_t *wasm_func_copy(const wasm_func_t *) * \brief Copies a #wasm_func_t to a new one. * * The caller is responsible for deleting the returned #wasm_func_t. @@ -1612,7 +1592,7 @@ * \fn size_t wasm_func_result_arity(const wasm_func_t *); * \brief Returns the number of results returned by this function. * -* \fn own wasm_trap_t *wasm_func_call(const wasm_func_t *, const wasm_val_vec_t *args, wasm_val_vec_t *results); +* \fn wasm_trap_t *wasm_func_call(const wasm_func_t *, const wasm_val_vec_t *args, wasm_val_vec_t *results); * \brief Calls the provided function with the arguments given. * * This function is used to call WebAssembly from the host. The parameter array @@ -1633,10 +1613,6 @@ * * Does not take ownership of `wasm_val_t` arguments. Gives ownership of * `wasm_val_t` results. - * - * > Note: to avoid the UB associated with passing the wrong number of results - * > or parameters by accident, or to distinguish between traps and other - * > errors, it's recommended to use #wasmtime_func_call. */ /** @@ -1646,10 +1622,10 @@ * \typedef wasm_global_t * \brief Convenience alias for #wasm_global_t * - * \fn void wasm_global_delete(own wasm_global_t *v); + * \fn void wasm_global_delete(wasm_global_t *v); * \brief Deletes a global. * - * \fn own wasm_global_t *wasm_global_copy(const wasm_global_t *) + * \fn wasm_global_t *wasm_global_copy(const wasm_global_t *) * \brief Copies a #wasm_global_t to a new one. * * The caller is responsible for deleting the returned #wasm_global_t. @@ -1693,9 +1669,6 @@ * * The type of the global doesn't match the type of the value specified. * * The initialization value does not come from the provided #wasm_store_t. * - * > Note: to get richer information on errors it's recommended to call - * > #wasmtime_global_new. - * * This function does not take ownership of any of its arguments. The caller is * expected to deallocate the returned value. * @@ -1718,9 +1691,6 @@ * the wrong type, or if the provided value comes from a different store as the * #wasm_global_t. * - * > Note: to get an error and detect erroneous cases, it's recommended to call - * > #wasmtime_global_set - * * This function does not take ownership of its arguments. */ @@ -1734,10 +1704,10 @@ * \typedef wasm_table_size_t * \brief Typedef for indices and sizes of wasm tables. * - * \fn void wasm_table_delete(own wasm_table_t *v); + * \fn void wasm_table_delete(wasm_table_t *v); * \brief Deletes a table. * - * \fn own wasm_table_t *wasm_table_copy(const wasm_table_t *) + * \fn wasm_table_t *wasm_table_copy(const wasm_table_t *) * \brief Copies a #wasm_table_t to a new one. * * The caller is responsible for deleting the returned #wasm_table_t. @@ -1780,8 +1750,6 @@ * * Does not take ownship of the `init` value. * - * > Note: for funcref tables you can use #wasmtime_funcref_table_new as well. - * * \fn wasm_tabletype_t *wasm_table_type(const wasm_table_t *); * \brief Returns the type of this table. * @@ -1795,9 +1763,6 @@ * * Gives ownership of the resulting `wasm_ref_t*`. * - * > Note: for funcref tables you can use #wasmtime_funcref_table_get to learn - * > about out-of-bounds errors. - * * \fn void wasm_table_set(wasm_table_t *, wasm_table_size_t index, wasm_ref_t *); * \brief Sets an element in this table. * @@ -1810,9 +1775,6 @@ * * Does not take ownership of the given `wasm_ref_t*`. * - * > Note: for funcref tables you can use #wasmtime_funcref_table_set to learn - * > about errors. - * * \fn wasm_table_size_t wasm_table_size(const wasm_table_t *); * \brief Gets the current size, in elements, of this table. * @@ -1829,9 +1791,7 @@ * * The #wasm_ref_t comes from a different store than the table provided. * * The #wasm_ref_t does not have an appropriate type to store in this table. * - * Does not take ownership of the givein `init` value. - * - * > Note: for funcref tables you can use #wasmtime_funcref_table_grow as well. + * Does not take ownership of the given `init` value. */ /** @@ -1844,10 +1804,10 @@ * \typedef wasm_memory_pages_t * \brief Unsigned integer to hold the number of pages a memory has. * - * \fn void wasm_memory_delete(own wasm_memory_t *v); + * \fn void wasm_memory_delete(wasm_memory_t *v); * \brief Deletes a memory. * - * \fn own wasm_memory_t *wasm_memory_copy(const wasm_memory_t *) + * \fn wasm_memory_t *wasm_memory_copy(const wasm_memory_t *) * \brief Copies a #wasm_memory_t to a new one. * * The caller is responsible for deleting the returned #wasm_memory_t. @@ -1927,35 +1887,35 @@ * \typedef wasm_extern_vec_t * \brief Convenience alias for #wasm_extern_vec_t * - * \fn void wasm_extern_delete(own wasm_extern_t *v); + * \fn void wasm_extern_delete(wasm_extern_t *v); * \brief Deletes a extern. * - * \fn void wasm_extern_vec_new_empty(own wasm_extern_vec_t *out); + * \fn void wasm_extern_vec_new_empty(wasm_extern_vec_t *out); * \brief Creates an empty vector. * * See #wasm_byte_vec_new_empty for more information. * - * \fn void wasm_extern_vec_new_uninitialized(own wasm_extern_vec_t *out, size_t); + * \fn void wasm_extern_vec_new_uninitialized(wasm_extern_vec_t *out, size_t); * \brief Creates a vector with the given capacity. * * See #wasm_byte_vec_new_uninitialized for more information. * - * \fn void wasm_extern_vec_new(own wasm_extern_vec_t *out, size_t, own wasm_extern_t *const[]); + * \fn void wasm_extern_vec_new(wasm_extern_vec_t *out, size_t, wasm_extern_t *const[]); * \brief Creates a vector with the provided contents. * * See #wasm_byte_vec_new for more information. * - * \fn void wasm_extern_vec_copy(own wasm_extern_vec_t *out, const wasm_extern_vec_t *) + * \fn void wasm_extern_vec_copy(wasm_extern_vec_t *out, const wasm_extern_vec_t *) * \brief Copies one vector to another * * See #wasm_byte_vec_copy for more information. * - * \fn void wasm_extern_vec_delete(own wasm_extern_vec_t *out) + * \fn void wasm_extern_vec_delete(wasm_extern_vec_t *out) * \brief Deallocates import for a vector. * * See #wasm_byte_vec_delete for more information. * - * \fn own wasm_extern_t *wasm_extern_copy(const wasm_extern_t *) + * \fn wasm_extern_t *wasm_extern_copy(const wasm_extern_t *) * \brief Copies a #wasm_extern_t to a new one. * * The caller is responsible for deleting the returned #wasm_extern_t. @@ -2133,10 +2093,10 @@ * \typedef wasm_instance_t * \brief Convenience alias for #wasm_instance_t * - * \fn void wasm_instance_delete(own wasm_instance_t *v); + * \fn void wasm_instance_delete(wasm_instance_t *v); * \brief Deletes a instance. * - * \fn own wasm_instance_t *wasm_instance_copy(const wasm_instance_t *) + * \fn wasm_instance_t *wasm_instance_copy(const wasm_instance_t *) * \brief Copies a #wasm_instance_t to a new one. * * The caller is responsible for deleting the returned #wasm_instance_t. @@ -2168,7 +2128,7 @@ * \fn wasm_ref_as_instance_const(const wasm_ref_t *); * \brief Unimplemented in Wasmtime, aborts the process if called. * - * \fn own wasm_instance_t *wasm_instance_new(wasm_store_t *, const wasm_module_t *, const wasm_extern_vec_t *, wasm_trap_t **); + * \fn wasm_instance_t *wasm_instance_new(wasm_store_t *, const wasm_module_t *, const wasm_extern_vec_t *, wasm_trap_t **); * \brief Instantiates a module with the provided imports. * * This function will instantiate the provided #wasm_module_t into the provided @@ -2178,9 +2138,6 @@ * This function must provide exactly the same number of imports as returned by * #wasm_module_imports or this results in undefined behavior. * - * > Note: to avoid the undefined behavior here related to the number of imports - * > it's recommended to use #wasmtime_instance_new instead. - * * Imports provided are expected to be 1:1 matches against the list returned by * #wasm_module_imports. * diff --git a/crates/c-api/include/wasi.h b/crates/c-api/include/wasi.h index 9b98becab749..994c66b22605 100644 --- a/crates/c-api/include/wasi.h +++ b/crates/c-api/include/wasi.h @@ -32,9 +32,9 @@ extern "C" { * \brief Convenience alias for #wasi_config_t * * \struct wasi_config_t - * \brief Opaque type used to create a #wasi_instance_t. + * \brief TODO * - * \fn void wasi_config_delete(own wasi_config_t *); + * \fn void wasi_config_delete(wasi_config_t *); * \brief Deletes a configuration object. */ WASI_DECLARE_OWN(config) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index edf05d63c4b4..9249ea177b37 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -1,3 +1,7 @@ +/** + * \file wasmtime.h + */ + #ifndef WASMTIME_API_H #define WASMTIME_API_H @@ -22,8 +26,9 @@ extern "C" { /** * \brief Converts from the text format of WebAssembly to to the binary format. * - * \param wat this it the input buffer with the WebAssembly Text Format inside of + * \param wat this it the input pointer with the WebAssembly Text Format inside of * it. This will be parsed and converted to the binary format. + * \param wat_len this it the length of `wat`, in bytes. * \param ret if the conversion is successful, this byte vector is filled in with * the WebAssembly binary format. * diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h index 538454052781..e23277b45768 100644 --- a/crates/c-api/include/wasmtime/config.h +++ b/crates/c-api/include/wasmtime/config.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/config.h + * + * TODO + */ + #ifndef WASMTIME_CONFIG_H #define WASMTIME_CONFIG_H diff --git a/crates/c-api/include/wasmtime/error.h b/crates/c-api/include/wasmtime/error.h index 87f3415371f8..f42447a9bd04 100644 --- a/crates/c-api/include/wasmtime/error.h +++ b/crates/c-api/include/wasmtime/error.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/error.h + * + * TODO + */ + #ifndef WASMTIME_ERROR_H #define WASMTIME_ERROR_H @@ -13,14 +19,15 @@ extern "C" { * * \struct wasmtime_error * \brief Errors generated by Wasmtime. + * \headerfile wasmtime/error.h * * This opaque type represents an error that happened as part of one of the * functions below. Errors primarily have an error message associated with them - * at this time, which you can acquire by calling #wasmtime_error_message. + * at this time, which you can acquire by calling TODO wasmtime_error_message. */ typedef struct wasmtime_error wasmtime_error_t; -/* +/** * \brief Deletes an error. */ WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error); diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index eb80ac2ad0e4..781ec113ab87 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/extern.h + * + * TODO + */ + #ifndef WASMTIME_EXTERN_H #define WASMTIME_EXTERN_H @@ -7,34 +13,77 @@ extern "C" { #endif +/// TODO typedef uint64_t wasmtime_func_t; +/// TODO typedef uint64_t wasmtime_table_t; +/// TODO typedef uint64_t wasmtime_memory_t; +/// TODO typedef uint64_t wasmtime_instance_t; +/// TODO typedef uint64_t wasmtime_global_t; +/// TODO typedef uint8_t wasmtime_extern_kind_t; +/// TODO #define WASMTIME_EXTERN_FUNC 0 +/// TODO #define WASMTIME_EXTERN_GLOBAL 1 +/// TODO #define WASMTIME_EXTERN_TABLE 2 +/// TODO #define WASMTIME_EXTERN_MEMORY 3 +/// TODO #define WASMTIME_EXTERN_INSTANCE 4 +/// TODO #define WASMTIME_EXTERN_MODULE 5 +/** + * TODO + */ typedef union wasmtime_extern_union { + /** + * TODO + */ wasmtime_func_t func; + /** + * TODO + */ wasmtime_global_t global; + /** + * TODO + */ wasmtime_table_t table; + /** + * TODO + */ wasmtime_memory_t memory; + /** + * TODO + */ wasmtime_instance_t instance; + /** + * TODO + */ wasmtime_module_t *module; } wasmtime_extern_union_t; +/** + * TODO + */ typedef struct wasmtime_extern { + /** + * TODO + */ wasmtime_extern_kind_t kind; + /** + * TODO + */ wasmtime_extern_union_t of; } wasmtime_extern_t; +/// TODO void wasmtime_extern_delete(wasmtime_extern_t *val); #ifdef __cplusplus diff --git a/crates/c-api/include/wasmtime/func.h b/crates/c-api/include/wasmtime/func.h index 96026fb5a0f8..f77dcc6f26bd 100644 --- a/crates/c-api/include/wasmtime/func.h +++ b/crates/c-api/include/wasmtime/func.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/func.h + * + * TODO + */ + #ifndef WASMTIME_FUNC_H #define WASMTIME_FUNC_H @@ -11,7 +17,12 @@ extern "C" { #endif /** + * \typedef wasmtime_caller_t + * \brief Alias to #wasmtime_caller + * * \brief Structure used to learn about the caller of a host-defined function. + * \struct wasmtime_caller + * \headerfile wasmtime/func.h * * This structure is the first argument of #wasmtime_func_callback_t and * wasmtime_func_callback_with_env_t. The main purpose of this structure is for @@ -56,6 +67,7 @@ WASM_API_EXTERN wasmtime_func_t wasmtime_func_new( void (*finalizer)(void*) ); +/// TODO WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( const wasmtime_context_t *store, wasmtime_func_t func @@ -118,6 +130,7 @@ WASM_API_EXTERN bool wasmtime_caller_export_get( wasmtime_extern_t *item ); +/// TODO WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* caller); #ifdef __cplusplus diff --git a/crates/c-api/include/wasmtime/global.h b/crates/c-api/include/wasmtime/global.h index 5efdc56c34b9..854a448c55c0 100644 --- a/crates/c-api/include/wasmtime/global.h +++ b/crates/c-api/include/wasmtime/global.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/global.h + * + * TODO + */ + #ifndef WASMTIME_GLOBAL_H #define WASMTIME_GLOBAL_H @@ -30,11 +36,13 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new( wasmtime_global_t *ret ); +/// TODO WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( const wasmtime_context_t *store, wasmtime_global_t global ); +/// TODO WASM_API_EXTERN void wasmtime_global_get( wasmtime_context_t *store, wasmtime_global_t global, diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h index 12276dcfb702..3edc0c7ad4ad 100644 --- a/crates/c-api/include/wasmtime/instance.h +++ b/crates/c-api/include/wasmtime/instance.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/instance.h + * + * TODO + */ + #ifndef WASMTIME_INSTANCE_H #define WASMTIME_INSTANCE_H @@ -71,11 +77,13 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( wasm_trap_t **trap ); +/// TODO WASM_API_EXTERN wasmtime_instancetype_t *wasmtime_instance_type( const wasmtime_context_t *store, wasmtime_instance_t instance ); +/// TODO WASM_API_EXTERN bool wasmtime_instance_export_get( wasmtime_context_t *store, wasmtime_instance_t instance, @@ -84,6 +92,7 @@ WASM_API_EXTERN bool wasmtime_instance_export_get( wasmtime_extern_t *item ); +/// TODO WASM_API_EXTERN bool wasmtime_instance_export_nth( wasmtime_context_t *store, wasmtime_instance_t instance, diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index 8bf6fbcec8c1..05cbb8a0a55e 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/linker.h + * + * TODO + */ + #ifndef WASMTIME_LINKER_H #define WASMTIME_LINKER_H @@ -34,6 +40,7 @@ typedef struct wasmtime_linker wasmtime_linker_t; */ WASM_API_EXTERN wasmtime_linker_t* wasmtime_linker_new(wasm_engine_t* engine); +/// TODO WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker); /** @@ -49,7 +56,9 @@ WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, * * \param linker the linker the name is being defined in. * \param module the module name the item is defined under. + * \param module_len the byte length of `module` * \param name the field name the item is defined under + * \param name_len the byte length of `name` * \param item the item that is being defined in this linker. * * \return On success `NULL` is returned, otherwise an error is returned which @@ -71,7 +80,6 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( * \brief Defines a WASI instance in this linker. * * \param linker the linker the name is being defined in. - * \param instance a previously-created WASI instance. * * \return On success `NULL` is returned, otherwise an error is returned which * describes why the definition failed. @@ -87,7 +95,9 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( * \brief Defines an instance under the specified name in this linker. * * \param linker the linker the name is being defined in. + * \param store TODO * \param name the module name to define `instance` under. + * \param name_len the byte length of `name` * \param instance a previously-created instance. * * \return On success `NULL` is returned, otherwise an error is returned which @@ -112,6 +122,7 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance( * \brief Instantiates a #wasm_module_t with the items defined in this linker. * * \param linker the linker used to instantiate the provided module. + * \param store TODO * \param module the module that is being instantiated. * \param instance the returned instance, if successful. * \param trap a trap returned, if the start function traps. @@ -141,7 +152,9 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate( * \brief Defines automatic instantiations of a #wasm_module_t in this linker. * * \param linker the linker the module is being added to + * \param store TODO * \param name the name of the module within the linker + * \param name_len TODO * \param module the module that's being instantiated * * \return An error if the module could not be instantiated or added or `NULL` @@ -166,7 +179,9 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module( * \brief Acquires the "default export" of the named module in this linker. * * \param linker the linker to load from + * \param store TODO * \param name the name of the module to get the default export for + * \param name_len TODO * \param func where to store the extracted default function. * * \return An error is returned if the default export could not be found, or @@ -187,8 +202,11 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default( * \brief Loads an item by name from this linker. * * \param linker the linker to load from + * \param store TODO * \param module the name of the module to get + * \param module_len TODO * \param name the name of the field to get + * \param name_len TODO * \param item where to store the extracted item * * \return An error is returned if the item isn't defined or has more than one diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h index b23cf04c65e4..6b18a37de7e8 100644 --- a/crates/c-api/include/wasmtime/memory.h +++ b/crates/c-api/include/wasmtime/memory.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/memory.h + * + * TODO + */ + #ifndef WASMTIME_MEMORY_H #define WASMTIME_MEMORY_H @@ -10,29 +16,35 @@ extern "C" { #endif +/// TODO WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( wasmtime_context_t *store, const wasm_memorytype_t* ty, wasmtime_memory_t *ret ); +/// TODO WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type( const wasmtime_context_t *store, wasmtime_memory_t memory ); +/// TODO WASM_API_EXTERN uint8_t *wasmtime_memory_data( const wasmtime_context_t *store, wasmtime_memory_t memory ); +/// TODO WASM_API_EXTERN size_t wasmtime_memory_data_size( const wasmtime_context_t *store, wasmtime_memory_t memory ); +/// TODO WASM_API_EXTERN uint32_t wasmtime_memory_size( const wasmtime_context_t *store, wasmtime_memory_t memory ); +/// TODO WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow( wasmtime_context_t *store, wasmtime_memory_t memory, diff --git a/crates/c-api/include/wasmtime/module.h b/crates/c-api/include/wasmtime/module.h index dc7d9c4a927f..62307cadf8a6 100644 --- a/crates/c-api/include/wasmtime/module.h +++ b/crates/c-api/include/wasmtime/module.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/module.h + * + * TODO + */ + #ifndef WASMTIME_MODULE_H #define WASMTIME_MODULE_H @@ -37,19 +43,19 @@ WASM_API_EXTERN void wasmtime_moduletype_imports(const wasmtime_moduletype_t*, w WASM_API_EXTERN void wasmtime_moduletype_exports(const wasmtime_moduletype_t*, wasm_exporttype_vec_t* out); /** - * \brief Converts a #wasm_moduletype_t to a #wasm_externtype_t + * \brief Converts a #wasmtime_moduletype_t to a #wasm_externtype_t * - * The returned value is owned by the #wasm_moduletype_t argument and should not + * The returned value is owned by the #wasmtime_moduletype_t argument and should not * be deleted. */ WASM_API_EXTERN wasm_externtype_t* wasmtime_moduletype_as_externtype(wasmtime_moduletype_t*); /** - * \brief Attempts to convert a #wasm_externtype_t to a #wasm_moduletype_t + * \brief Attempts to convert a #wasm_externtype_t to a #wasmtime_moduletype_t * - * The returned value is owned by the #wasm_moduletype_t argument and should not - * be deleted. Returns `NULL` if the provided argument is not a - * #wasm_moduletype_t. + * The returned value is owned by the #wasmtime_moduletype_t argument and + * should not be deleted. Returns `NULL` if the provided argument is not a + * #wasmtime_moduletype_t. */ WASM_API_EXTERN wasmtime_moduletype_t* wasm_externtype_as_moduletype(wasm_externtype_t*); @@ -62,7 +68,7 @@ WASM_API_EXTERN wasmtime_moduletype_t* wasm_externtype_as_moduletype(wasm_extern * * This type represents a compiled WebAssembly module. The compiled module is * ready to be instantiated and can be inspected for imports/exports. It is safe - * to use a #wasmtime_module_t across multiple threads simultaneously. + * to use a module across multiple threads simultaneously. */ typedef struct wasmtime_module wasmtime_module_t; @@ -87,7 +93,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( wasmtime_module_t **ret ); -/* +/** * \brief Deletes a module. */ WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m); diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h index 1aa527bdd1f3..1c7d461dc36f 100644 --- a/crates/c-api/include/wasmtime/store.h +++ b/crates/c-api/include/wasmtime/store.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/store.h + * + * TODO + */ + #ifndef WASMTIME_STORE_H #define WASMTIME_STORE_H @@ -19,22 +25,28 @@ extern "C" { */ typedef struct wasmtime_store wasmtime_store_t; +/// TODO typedef struct wasmtime_context wasmtime_context_t; +/// TODO WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new( wasm_engine_t *engine, void *data, void(*finalizer)(void*) ); +/// TODO WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *store); -/* +/** * \brief Deletes a store. */ WASM_API_EXTERN void wasmtime_store_delete(wasmtime_store_t *store); +/// TODO WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* context); + +/// TODO WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void *data); /** @@ -74,12 +86,14 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t * * fuel parameter is filled in with fuel consuemd so far. * * Also note that fuel, if enabled, must be originally configured via - * #wasmtime_store_add_fuel. + * #wasmtime_context_add_fuel. */ WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel); +/// TODO typedef struct wasi_config_t wasi_config_t; +/// TODO WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); /** @@ -120,7 +134,7 @@ WASM_API_EXTERN wasmtime_interrupt_handle_t *wasmtime_interrupt_handle_new(wasmt */ WASM_API_EXTERN void wasmtime_interrupt_handle_interrupt(wasmtime_interrupt_handle_t *handle); -/* +/** * \brief Deletes an interrupt handle. */ WASM_API_EXTERN void wasmtime_interrupt_handle_delete(wasmtime_interrupt_handle_t *handle); diff --git a/crates/c-api/include/wasmtime/table.h b/crates/c-api/include/wasmtime/table.h index af21fafba3ea..129bcd8f3e9c 100644 --- a/crates/c-api/include/wasmtime/table.h +++ b/crates/c-api/include/wasmtime/table.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/table.h + * + * TODO + */ + #ifndef WASMTIME_TABLE_H #define WASMTIME_TABLE_H @@ -28,6 +34,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new( wasmtime_table_t *table ); +/// TODO WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( const wasmtime_context_t *store, wasmtime_table_t table @@ -69,6 +76,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( const wasmtime_val_t *value ); +/// TODO WASM_API_EXTERN uint32_t wasmtime_table_size( const wasmtime_context_t *store, wasmtime_table_t table diff --git a/crates/c-api/include/wasmtime/trap.h b/crates/c-api/include/wasmtime/trap.h index f87534c89846..e7ca326b3831 100644 --- a/crates/c-api/include/wasmtime/trap.h +++ b/crates/c-api/include/wasmtime/trap.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/trap.h + * + * TODO + */ + #ifndef WASMTIME_TRAP_H #define WASMTIME_TRAP_H @@ -7,6 +13,7 @@ extern "C" { #endif +/// TODO WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(char *msg); /** diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h index 56052059a08a..1bf0c6c97203 100644 --- a/crates/c-api/include/wasmtime/val.h +++ b/crates/c-api/include/wasmtime/val.h @@ -1,3 +1,9 @@ +/** + * \file wasmtime/val.h + * + * TODO + */ + #ifndef WASMTIME_VAL_H #define WASMTIME_VAL_H @@ -8,6 +14,7 @@ extern "C" { #endif +/// TODO typedef struct wasmtime_externref wasmtime_externref_t; /** @@ -32,8 +39,8 @@ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (* * TODO * * If the given value is a reference to a non-null `externref`, writes the - * wrapped data that was passed into #wasmtime_externref_new or - * #wasmtime_externref_new_with_finalizer when creating the given `externref` to + * wrapped data that was passed into #wasmtime_externref_new + * when creating the given `externref` to * `datap`, and returns `true`. * * If the value is a reference to a null `externref`, writes `NULL` to `datap` @@ -49,6 +56,7 @@ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (* */ WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_externref_t *data); +/// TODO WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externref_t *ref); /** @@ -56,33 +64,77 @@ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externre */ WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref); +/// TODO typedef uint8_t wasmtime_valkind_t; +/// TODO #define WASMTIME_I32 0 +/// TODO #define WASMTIME_I64 1 +/// TODO #define WASMTIME_F32 2 +/// TODO #define WASMTIME_F64 3 +/// TODO #define WASMTIME_V128 4 +/// TODO #define WASMTIME_FUNCREF 5 +/// TODO #define WASMTIME_EXTERNREF 6 +/// TODO typedef uint8_t wasmtime_v128[16]; +/** + * TODO + */ typedef union wasmtime_valunion { + /** + * TODO + */ int32_t i32; + /** + * TODO + */ int64_t i64; + /** + * TODO + */ float32_t f32; + /** + * TODO + */ float64_t f64; + /** + * TODO + */ wasmtime_func_t funcref; + /** + * TODO + */ wasmtime_externref_t *externref; + /** + * TODO + */ wasmtime_v128 v128; } wasmtime_valunion_t; +/** + * TODO + */ typedef struct wasmtime_val { + /** + * TODO + */ wasmtime_valkind_t kind; + /** + * TODO + */ wasmtime_valunion_t of; } wasmtime_val_t; +/// TODO #define WASMTIME_FUNCREF_NULL ((uint64_t) -1) + /** * TODO */ diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index b992650cc292..35b9d2543ce0 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -47,11 +47,11 @@ use wasmtime_runtime::{ /// might have an `async` function in Rust, however, which you'd like to make /// available from WebAssembly. Wasmtime supports asynchronously calling /// WebAssembly through native stack switching. You can get some more -/// information about [asynchronous configs](Config::async_support), but from the -/// perspective of `Func` it's important to know that whether or not your -/// [`Store`] is asynchronous will dictate whether you call functions through -/// [`Func::call`] or [`Func::call_async`] (or the typed wrappers such as -/// [`TypedFunc::call`] vs [`TypedFunc::call_async`]). +/// information about [asynchronous configs](crate::Config::async_support), but +/// from the perspective of `Func` it's important to know that whether or not +/// your [`Store`](crate::Store) is asynchronous will dictate whether you call +/// functions through [`Func::call`] or [`Func::call_async`] (or the typed +/// wrappers such as [`TypedFunc::call`] vs [`TypedFunc::call_async`]). /// /// Note that asynchronous function APIs here are a bit trickier than their /// synchronous brethren. For example [`Func::new_async`] and @@ -337,7 +337,7 @@ impl Func { /// # Panics /// /// This function will panic if `store` is not associated with an [async - /// config](Config::async_support). + /// config](crate::Config::async_support). /// /// # Examples /// @@ -670,7 +670,7 @@ impl Func { /// /// This function is the same as [`Func::call`] except that it is /// asynchronous. This is only compatible with stores associated with an - /// [asynchronous config](Config::async_support). + /// [asynchronous config](crate::Config::async_support). /// /// It's important to note that the execution of WebAssembly will happen /// synchronously in the `poll` method of the future returned from this @@ -680,7 +680,7 @@ impl Func { /// in a "blocking context". /// /// For more information see the documentation on [asynchronous - /// configs](Config::async_support). + /// configs](crate::Config::async_support). /// /// # Panics /// @@ -1395,7 +1395,7 @@ macro_rules! impl_host_abi { for_each_function_signature!(impl_host_abi); /// Internal trait implemented for all arguments that can be passed to -/// [`Func::wrap`] and [`Config::wrap_host_func`](crate::Config::wrap_host_func). +/// [`Func::wrap`] and [`Linker::func_wrap`](crate::Linker::func_wrap). /// /// This trait should not be implemented by external users, it's only intended /// as an implementation detail of this crate. @@ -1488,14 +1488,14 @@ impl Caller<'_, T> { /// Access the underlying data owned by this `Store`. /// - /// Same as [`Store::data`]. + /// Same as [`Store::data`](crate::Store::data) pub fn data(&self) -> &T { self.store.data() } /// Access the underlying data owned by this `Store`. /// - /// Same as [`Store::data_mut`]. + /// Same as [`Store::data_mut`](crate::Store::data_mut) pub fn data_mut(&mut self) -> &mut T { self.store.data_mut() } @@ -1507,35 +1507,37 @@ impl Caller<'_, T> { /// Returns an [`InterruptHandle`] to interrupt wasm execution. /// - /// See [`Store::interrupt_handle`] for more information. + /// See [`Store::interrupt_handle`](crate::Store::interrupt_handle) for more + /// information. pub fn interrupt_handle(&self) -> Result { self.store.interrupt_handle() } /// Perform garbage collection of `ExternRef`s. /// - /// Same as [`Store::gc`]. + /// Same as [`Store::gc`](crate::Store::gc). pub fn gc(&mut self) { self.store.gc() } /// Returns the fuel consumed by this store. /// - /// For more information see [`Store::fuel_consumed`]. + /// For more information see [`Store::fuel_consumed`](crate::Store::fuel_consumed) pub fn fuel_consumed(&self) -> Option { self.store.fuel_consumed() } /// Inject more fuel into this store to be consumed when executing wasm code. /// - /// For more information see [`Store::add_fuel`] + /// For more information see [`Store::add_fuel`](crate::Store::add_fuel) pub fn add_fuel(&mut self, fuel: u64) -> Result<()> { self.store.add_fuel(fuel) } /// Configures this `Store` to trap whenever fuel runs out. /// - /// For more information see [`Store::out_of_fuel_trap`] + /// For more information see + /// [`Store::out_of_fuel_trap`](crate::Store::out_of_fuel_trap) pub fn out_of_fuel_trap(&mut self) { self.store.out_of_fuel_trap() } @@ -1543,7 +1545,8 @@ impl Caller<'_, T> { /// Configures this `Store` to yield while executing futures whenever fuel /// runs out. /// - /// For more information see [`Store::out_of_fuel_async_yield`] + /// For more information see + /// [`Store::out_of_fuel_async_yield`](crate::Store::out_of_fuel_async_yield) pub fn out_of_fuel_async_yield(&mut self, injection_count: u32, fuel_to_inject: u64) { self.store .out_of_fuel_async_yield(injection_count, fuel_to_inject) diff --git a/crates/wasmtime/src/limits.rs b/crates/wasmtime/src/limits.rs index 9f2d5553552e..132f6eed64a1 100644 --- a/crates/wasmtime/src/limits.rs +++ b/crates/wasmtime/src/limits.rs @@ -4,7 +4,7 @@ pub(crate) const DEFAULT_MEMORY_LIMIT: usize = 10000; /// Used by hosts to limit resource consumption of instances at runtime. /// -/// [`Store::new_with_limits`](crate::Store::new_with_limits) can be used +/// [`Store::limiter`](crate::Store::limiter) can be used /// with a resource limiter to take into account non-WebAssembly resource /// usage to determine if a linear memory or table should be grown. pub trait ResourceLimiter: Send + Sync + 'static { diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index 6b3071968ff4..42efe3a57f17 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -340,8 +340,9 @@ impl Linker { /// /// Returns an error if the any item is redefined twice in this linker (for /// example the same `module_name` was already defined) and shadowing is - /// disallowed, or if `instance` comes from a different [`Store`] than this - /// [`Linker`] originally was created with. + /// disallowed, or if `instance` comes from a different + /// [`Store`](crate::Store) than this [`Linker`] originally was created + /// with. /// /// # Examples /// @@ -411,9 +412,9 @@ impl Linker { /// /// Returns an error if the any item is redefined twice in this linker (for /// example the same `module_name` was already defined) and shadowing is - /// disallowed, if `instance` comes from a different [`Store`] than this - /// [`Linker`] originally was created with, or if a Reactor initialization - /// function traps. + /// disallowed, if `instance` comes from a different + /// [`Store`](crate::Store) than this [`Linker`] originally was created + /// with, or if a Reactor initialization function traps. /// /// # Examples /// diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/memory.rs index db0e227eb4e7..a9beb16923b3 100644 --- a/crates/wasmtime/src/memory.rs +++ b/crates/wasmtime/src/memory.rs @@ -372,13 +372,6 @@ impl Memory { /// is located at. /// /// TODO - /// - /// When reading and manipulating memory be sure to read up on the caveats - /// of [`Memory::data_unchecked`] to make sure that you can safely - /// read/write the memory. - /// - /// For more information and examples see the documentation on the - /// [`Memory`] type. pub fn data_ptr(&self, store: impl AsContext) -> *mut u8 { unsafe { (*store.as_context()[self.0].definition).base } } From 3860b2d98249a114020b4411392776a13bf80116 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 09:26:56 -0700 Subject: [PATCH 25/90] Checkout code for c api --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1ad9f597483a..b962bd77bb86 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -93,6 +93,9 @@ jobs: name: Doc - build the C API documentation runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 + with: + submodules: true - run: curl -L https://doxygen.nl/files/doxygen-1.9.1.linux.bin.tar.gz | tar xzf - - run: echo "`pwd`/doxygen-1.9.1/bin" >> $GITHUB_PATH - run: cd crates/c-api && doxygen doxygen.conf From d09f9aa067c26ee0898f525ef76e1316eeafa38c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 09:29:16 -0700 Subject: [PATCH 26/90] More c api bits --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b962bd77bb86..ac94e86a6d26 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -96,6 +96,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - run: sudo apt-get update -y && sudo apt-get install -y libclang1-9 - run: curl -L https://doxygen.nl/files/doxygen-1.9.1.linux.bin.tar.gz | tar xzf - - run: echo "`pwd`/doxygen-1.9.1/bin" >> $GITHUB_PATH - run: cd crates/c-api && doxygen doxygen.conf From 8e90d8034b10807b28b592372b4441c64567019a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 09:57:03 -0700 Subject: [PATCH 27/90] include wasi --- crates/c-api/include/wasmtime.h | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 9249ea177b37..61ec1b15452d 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -5,6 +5,7 @@ #ifndef WASMTIME_API_H #define WASMTIME_API_H +#include #include #include #include From 8f907b3003c4500faddb4cfa4b2de023735a6577 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 10:35:46 -0700 Subject: [PATCH 28/90] Remove new-api branch changes --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ac94e86a6d26..37034b55408b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,7 @@ name: CI on: push: - branches: [main, new-api] + branches: [main] tags-ignore: [dev] pull_request: branches: [main] From e9dc6a2328b55281f3b98534a19efd01633a318d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 13:32:44 -0700 Subject: [PATCH 29/90] Don't redefine wasi_config_t --- crates/c-api/include/wasmtime/store.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h index 1c7d461dc36f..58833c1a5c13 100644 --- a/crates/c-api/include/wasmtime/store.h +++ b/crates/c-api/include/wasmtime/store.h @@ -8,6 +8,7 @@ #define WASMTIME_STORE_H #include +#include #include #ifdef __cplusplus @@ -90,9 +91,6 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t * */ WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel); -/// TODO -typedef struct wasi_config_t wasi_config_t; - /// TODO WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); From 0dd02dbc00e1b7eba7b908343e47f0b0acc527d0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 May 2021 14:06:12 -0700 Subject: [PATCH 30/90] Prefix methods with wasmtime --- crates/c-api/include/wasmtime/instance.h | 2 +- crates/c-api/include/wasmtime/module.h | 2 +- crates/c-api/src/types/extern.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h index 3edc0c7ad4ad..7f298d141bca 100644 --- a/crates/c-api/include/wasmtime/instance.h +++ b/crates/c-api/include/wasmtime/instance.h @@ -46,7 +46,7 @@ WASM_API_EXTERN wasm_externtype_t* wasmtime_instancetype_as_externtype(wasmtime_ * be deleted. Returns `NULL` if the provided argument is not a * #wasmtime_instancetype_t. */ -WASM_API_EXTERN wasmtime_instancetype_t* wasm_externtype_as_instancetype(wasm_externtype_t*); +WASM_API_EXTERN wasmtime_instancetype_t* wasmtime_externtype_as_instancetype(wasm_externtype_t*); /** * \brief Wasmtime-specific function to instantiate a module. diff --git a/crates/c-api/include/wasmtime/module.h b/crates/c-api/include/wasmtime/module.h index 62307cadf8a6..87d2b9391179 100644 --- a/crates/c-api/include/wasmtime/module.h +++ b/crates/c-api/include/wasmtime/module.h @@ -57,7 +57,7 @@ WASM_API_EXTERN wasm_externtype_t* wasmtime_moduletype_as_externtype(wasmtime_mo * should not be deleted. Returns `NULL` if the provided argument is not a * #wasmtime_moduletype_t. */ -WASM_API_EXTERN wasmtime_moduletype_t* wasm_externtype_as_moduletype(wasm_externtype_t*); +WASM_API_EXTERN wasmtime_moduletype_t* wasmtime_externtype_as_moduletype(wasm_externtype_t*); /** * \typedef wasmtime_module_t diff --git a/crates/c-api/src/types/extern.rs b/crates/c-api/src/types/extern.rs index 99b279b6b98c..f36ff4b27997 100644 --- a/crates/c-api/src/types/extern.rs +++ b/crates/c-api/src/types/extern.rs @@ -123,14 +123,14 @@ pub extern "C" fn wasm_externtype_as_memorytype_const( } #[no_mangle] -pub extern "C" fn wasm_externtype_as_moduletype( +pub extern "C" fn wasmtime_externtype_as_moduletype( et: &wasm_externtype_t, ) -> Option<&wasmtime_moduletype_t> { wasmtime_moduletype_t::try_from(et) } #[no_mangle] -pub extern "C" fn wasm_externtype_as_instancetype( +pub extern "C" fn wasmtime_externtype_as_instancetype( et: &wasm_externtype_t, ) -> Option<&wasmtime_instancetype_t> { wasmtime_instancetype_t::try_from(et) From 1c705f18d53fde5ce54236a244546be559afc3dd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 12 May 2021 22:01:35 -0700 Subject: [PATCH 31/90] Tweaks and additions to the C API --- crates/c-api/include/wasmtime/extern.h | 4 ++++ crates/c-api/include/wasmtime/instance.h | 2 ++ crates/c-api/include/wasmtime/module.h | 5 +++++ crates/c-api/include/wasmtime/trap.h | 2 +- crates/c-api/include/wasmtime/val.h | 7 ++++++- crates/c-api/src/extern.rs | 10 +++++++++- crates/c-api/src/module.rs | 6 ++++++ crates/c-api/src/trap.rs | 6 ++---- crates/c-api/src/val.rs | 8 ++++++++ 9 files changed, 43 insertions(+), 7 deletions(-) diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index 781ec113ab87..5ca2ed0abaf8 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -8,6 +8,7 @@ #define WASMTIME_EXTERN_H #include +#include #ifdef __cplusplus extern "C" { @@ -86,6 +87,9 @@ typedef struct wasmtime_extern { /// TODO void wasmtime_extern_delete(wasmtime_extern_t *val); +/// TODO +wasm_externtype_t *wasmtime_extern_type(wasmtime_context_t *context, wasmtime_extern_t *val); + #ifdef __cplusplus } // extern "C" #endif diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h index 7f298d141bca..4abad99c421b 100644 --- a/crates/c-api/include/wasmtime/instance.h +++ b/crates/c-api/include/wasmtime/instance.h @@ -21,6 +21,8 @@ extern "C" { */ typedef struct wasmtime_instancetype wasmtime_instancetype_t; +/// TODO +WASM_API_EXTERN void wasmtime_instancetype_delete(wasmtime_instancetype_t *ty); /** * \brief Returns the list of exports that this instance type provides. diff --git a/crates/c-api/include/wasmtime/module.h b/crates/c-api/include/wasmtime/module.h index 87d2b9391179..c134883eb000 100644 --- a/crates/c-api/include/wasmtime/module.h +++ b/crates/c-api/include/wasmtime/module.h @@ -98,6 +98,11 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( */ WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m); +/** + * TODO + */ +WASM_API_EXTERN wasmtime_module_t *wasmtime_module_clone(wasmtime_module_t *m); + /** * \brief Validate a WebAssembly binary. * diff --git a/crates/c-api/include/wasmtime/trap.h b/crates/c-api/include/wasmtime/trap.h index e7ca326b3831..2811492fef62 100644 --- a/crates/c-api/include/wasmtime/trap.h +++ b/crates/c-api/include/wasmtime/trap.h @@ -14,7 +14,7 @@ extern "C" { #endif /// TODO -WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(char *msg); +WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(char *msg, size_t msg_len); /** * \brief Attempts to extract a WASI-specific exit status from this trap. diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h index 1bf0c6c97203..ee79ce144054 100644 --- a/crates/c-api/include/wasmtime/val.h +++ b/crates/c-api/include/wasmtime/val.h @@ -133,13 +133,18 @@ typedef struct wasmtime_val { } wasmtime_val_t; /// TODO -#define WASMTIME_FUNCREF_NULL ((uint64_t) -1) +#define WASMTIME_FUNCREF_NULL ((uint64_t) 0xffffffffffffffff) /** * TODO */ WASM_API_EXTERN void wasmtime_val_delete(wasmtime_val_t *val); +/** + * TODO + */ +WASM_API_EXTERN void wasmtime_val_copy(wasmtime_val_t *dst, const wasmtime_val_t *src); + #ifdef __cplusplus } // extern "C" #endif diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs index f531c567e975..a8ec69f49af8 100644 --- a/crates/c-api/src/extern.rs +++ b/crates/c-api/src/extern.rs @@ -1,6 +1,6 @@ use crate::{ wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_instance_t, - wasm_memory_t, wasm_module_t, wasm_table_t, wasmtime_module_t, StoreRef, + wasm_memory_t, wasm_module_t, wasm_table_t, wasmtime_module_t, CStoreContext, StoreRef, }; use std::mem::ManuallyDrop; use wasmtime::{Extern, Func, Global, Instance, Memory, Table}; @@ -175,3 +175,11 @@ impl Drop for wasmtime_extern_t { pub unsafe extern "C" fn wasmtime_extern_delete(e: &mut ManuallyDrop) { ManuallyDrop::drop(e); } + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_extern_type( + store: CStoreContext<'_>, + e: &wasmtime_extern_t, +) -> Box { + Box::new(wasm_externtype_t::new(e.to_extern().ty(store))) +} diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 10397a16bb09..9e0f0c5f2f04 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -139,6 +139,7 @@ pub unsafe extern "C" fn wasm_module_deserialize( } } +#[derive(Clone)] pub struct wasmtime_module_t { pub(crate) module: Module, } @@ -161,6 +162,11 @@ pub unsafe extern "C" fn wasmtime_module_new( #[no_mangle] pub extern "C" fn wasmtime_module_delete(_module: Box) {} +#[no_mangle] +pub extern "C" fn wasmtime_module_clone(module: &wasmtime_module_t) -> Box { + Box::new(module.clone()) +} + #[no_mangle] pub unsafe extern "C" fn wasmtime_module_validate( engine: &wasm_engine_t, diff --git a/crates/c-api/src/trap.rs b/crates/c-api/src/trap.rs index 6a4834c71d03..63d393fc8cf1 100644 --- a/crates/c-api/src/trap.rs +++ b/crates/c-api/src/trap.rs @@ -1,7 +1,5 @@ use crate::{wasm_frame_vec_t, wasm_instance_t, wasm_name_t, wasm_store_t}; use once_cell::unsync::OnceCell; -use std::ffi::CStr; -use std::os::raw::c_char; use wasmtime::Trap; #[repr(C)] @@ -47,8 +45,8 @@ pub extern "C" fn wasm_trap_new( } #[no_mangle] -pub unsafe extern "C" fn wasmtime_trap_new(message: *const c_char) -> Box { - let bytes = CStr::from_ptr(message).to_bytes(); +pub unsafe extern "C" fn wasmtime_trap_new(message: *const u8, len: usize) -> Box { + let bytes = crate::slice_from_raw_parts(message, len); let message = String::from_utf8_lossy(&bytes); Box::new(wasm_trap_t { trap: Trap::new(message), diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs index b503d835c0d5..e597cf4d2808 100644 --- a/crates/c-api/src/val.rs +++ b/crates/c-api/src/val.rs @@ -242,6 +242,14 @@ pub unsafe extern "C" fn wasmtime_val_delete(val: &mut ManuallyDrop, + src: &wasmtime_val_t, +) { + crate::initialize(dst, wasmtime_val_t::from_val(src.to_val())) +} + #[no_mangle] pub extern "C" fn wasmtime_externref_new( data: *mut c_void, From a49e967fe533064a4d2497966f15fc687df27325 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 12 May 2021 22:13:30 -0700 Subject: [PATCH 32/90] Fix build --- crates/wasmtime/src/linker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index d0c1bbb6fe80..5089d85aaba8 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -183,7 +183,7 @@ impl Linker { /// # Ok(()) /// # } /// ``` - pub fn allow_unknown_exports(&mut self, allow: bool) -> &mut Linker { + pub fn allow_unknown_exports(&mut self, allow: bool) -> &mut Self { self.allow_unknown_exports = allow; self } From 9dda2fc676627827940b2f9cfc84a15029a7af85 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 12 May 2021 22:15:04 -0700 Subject: [PATCH 33/90] Remove duplicate finalizer in C API --- crates/c-api/src/func.rs | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index bcc94db031a7..8511d2c8522b 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -30,22 +30,6 @@ pub type wasm_func_callback_with_env_t = extern "C" fn( results: *mut wasm_val_vec_t, ) -> Option>; -struct Finalizer { - env: *mut c_void, - finalizer: Option, -} - -unsafe impl Send for Finalizer {} -unsafe impl Sync for Finalizer {} - -impl Drop for Finalizer { - fn drop(&mut self) { - if let Some(f) = self.finalizer { - f(self.env); - } - } -} - impl wasm_func_t { pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> { match &e.which { @@ -116,12 +100,12 @@ pub unsafe extern "C" fn wasm_func_new_with_env( store: &mut wasm_store_t, ty: &wasm_functype_t, callback: wasm_func_callback_with_env_t, - env: *mut c_void, + data: *mut c_void, finalizer: Option, ) -> Box { - let finalizer = Finalizer { env, finalizer }; + let finalizer = crate::ForeignData { data, finalizer }; create_function(store, ty, move |params, results| { - callback(finalizer.env, params, results) + callback(finalizer.data, params, results) }) } From 734af27a14d8b2899a754302bfc30f294d9de350 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 May 2021 07:11:33 -0700 Subject: [PATCH 34/90] Fix a test --- tests/all/linker.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/all/linker.rs b/tests/all/linker.rs index 80cafefae55c..128d0be7f821 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -174,18 +174,18 @@ fn module_interposition() -> Result<()> { #[test] fn allow_unknown_exports() -> Result<()> { - let store = Store::default(); - let mut linker = Linker::new(&store); + let mut store = Store::<()>::default(); + let mut linker = Linker::new(store.engine()); let module = Module::new( store.engine(), r#"(module (func (export "_start")) (global (export "g") i32 (i32.const 0)))"#, )?; - assert!(linker.module("module", &module).is_err()); + assert!(linker.module(&mut store, "module", &module).is_err()); - let mut linker = Linker::new(&store); + let mut linker = Linker::new(store.engine()); linker.allow_unknown_exports(true); - linker.module("module", &module)?; + linker.module(&mut store, "module", &module)?; Ok(()) } From 2dbbdce33e2ed5dfd2cfa6f630584405314bf4d2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 May 2021 07:12:13 -0700 Subject: [PATCH 35/90] Assert Linker is Send/Sync regardless of `T` --- crates/wasmtime/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 3cc9bf5c16bf..a32cd7ebcf7f 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -338,6 +338,7 @@ fn _assert_send_sync() { _assert::>(); _assert::>(); _assert::>(); + _assert::>(); _assert::(); #[cfg(feature = "async")] From 6d347d3aa274accd002f9a57f72cead26c21947c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 May 2021 07:12:29 -0700 Subject: [PATCH 36/90] Add TODO links to uncomment --- crates/wasmtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index a32cd7ebcf7f..91c612449d29 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -270,8 +270,8 @@ //! ``` #![allow(unknown_lints)] -//#![deny(missing_docs, broken_intra_doc_links)] -//#![doc(test(attr(deny(warnings))))] +//#![deny(missing_docs, broken_intra_doc_links)] TODO +//#![doc(test(attr(deny(warnings))))] TODO #![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))] #![cfg_attr(nightlydoc, feature(doc_cfg))] #![cfg_attr(not(feature = "default"), allow(dead_code, unused_imports))] From b0a70b1026b70ce5098683cc5c25b62b20bd2883 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 May 2021 08:40:47 -0700 Subject: [PATCH 37/90] Various small updates found re-reviewing --- crates/c-api/src/lib.rs | 21 +++++++++++++++++++-- crates/c-api/src/memory.rs | 5 ----- crates/c-api/src/store.rs | 17 +++++++++++++++++ crates/runtime/src/externref.rs | 17 ++++++++--------- crates/wasmtime/src/func.rs | 19 +++++++++++++++++++ crates/wasmtime/src/signatures.rs | 21 --------------------- crates/wasmtime/src/store/context.rs | 8 ++++++-- crates/wasmtime/src/store/data.rs | 2 ++ crates/wast/src/wast.rs | 2 -- 9 files changed, 71 insertions(+), 41 deletions(-) diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 955a257372b9..4bc111b5dc1b 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -1,6 +1,15 @@ +//! This crate is the implementation of Wasmtime's C API. +//! +//! This crate is not intended to be used from Rust itself, for that see the +//! `wasmtime` crate. Otherwise this is typically compiled as a +//! cdylib/staticlib. Documentation for this crate largely lives in the header +//! files of the `include` directory for this crate. +//! +//! At a high level this crate implements the `wasm.h` API with some gymnastics, +//! but otherwise an accompanying `wasmtime.h` API is provided which is more +//! specific to Wasmtime and has fewer gymnastics to implement. + #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] -// #![allow(unknown_lints)] -// #![allow(improper_ctypes_definitions)] mod config; mod engine; @@ -59,6 +68,8 @@ pub(crate) fn initialize(dst: &mut std::mem::MaybeUninit, val: T) { } } +/// Helper for running a C-defined finalizer over some data when the Rust +/// structure is dropped. pub struct ForeignData { data: *mut std::ffi::c_void, finalizer: Option, @@ -75,6 +86,11 @@ impl Drop for ForeignData { } } +/// Helper for creating Rust slices from C inputs. +/// +/// This specifically disregards the `ptr` argument if the length is zero. The +/// `ptr` in that case maybe `NULL` or invalid, and it's not valid to have a +/// zero-length Rust slice with a `NULL` pointer. unsafe fn slice_from_raw_parts<'a, T>(ptr: *const T, len: usize) -> &'a [T] { if len == 0 { &[] @@ -83,6 +99,7 @@ unsafe fn slice_from_raw_parts<'a, T>(ptr: *const T, len: usize) -> &'a [T] { } } +/// Same as above, but for `*_mut` unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T] { if len == 0 { &mut [] diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs index e763dcb8bf0c..c4b7f2047786 100644 --- a/crates/c-api/src/memory.rs +++ b/crates/c-api/src/memory.rs @@ -80,11 +80,6 @@ pub unsafe extern "C" fn wasm_memory_grow( memory.grow(&mut store, delta).is_ok() } -// WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( -// wasmtime_context_t *store, -// const wasm_memorytype_t* -// ); - #[no_mangle] pub extern "C" fn wasmtime_memory_new( store: CStoreContextMut<'_>, diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index 5622e65c51f7..0a91cf22d1d3 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -4,6 +4,16 @@ use std::ffi::c_void; use std::sync::Arc; use wasmtime::{AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut}; +/// This representation of a `Store` is used to implement the `wasm.h` API. +/// +/// This is stored alongside `Func` and such for `wasm.h` so each object is +/// independently owned. The usage of `Arc` here is mostly to just get it to be +/// safe to drop across multiple threads, but otherwise acquiring the `context` +/// values from this struct is considered unsafe due to it being unknown how the +/// aliasing is working on the C side of things. +/// +/// The aliasing requirements are documented in the C API `wasm.h` itself (at +/// least Wasmtime's implementation). #[derive(Clone)] pub struct StoreRef { store: Arc>>, @@ -38,6 +48,13 @@ pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box { }) } +/// Representation of a `Store` for `wasmtime.h` This notably tries to move more +/// burden of aliasing on the caller rather than internally, allowing for a more +/// raw representation of contexts and such that requires less `unsafe` in the +/// implementation. +/// +/// Note that this notably carries `StoreData` as a payload which allows storing +/// foreign data and configuring WASI as well. #[repr(C)] pub struct wasmtime_store_t { pub(crate) store: Store, diff --git a/crates/runtime/src/externref.rs b/crates/runtime/src/externref.rs index 4f9b17f13022..8983eb0f45fc 100644 --- a/crates/runtime/src/externref.rs +++ b/crates/runtime/src/externref.rs @@ -325,19 +325,14 @@ impl VMExternRef { let value_ptr = alloc_ptr.cast::(); ptr::write(value_ptr.as_ptr(), make_value()); - let value_ref: &T = value_ptr.as_ref(); - let value_ref: &(dyn Any + Send + Sync) = value_ref as _; - let value_ptr: *const (dyn Any + Send + Sync) = value_ref as _; - let value_ptr: *mut (dyn Any + Send + Sync) = value_ptr as _; - let value_ptr = NonNull::new_unchecked(value_ptr); - let extern_data_ptr = alloc_ptr.cast::().as_ptr().add(footer_offset) as *mut VMExternData; ptr::write( extern_data_ptr, VMExternData { ref_count: AtomicUsize::new(1), - value_ptr, + // cast from `*mut T` to `*mut dyn Any` here + value_ptr: NonNull::new_unchecked(value_ptr.as_ptr()), }, ); @@ -496,10 +491,12 @@ type TableElem = UnsafeCell>; /// /// Under the covers, this is a simple bump allocator that allows duplicate /// entries. Deduplication happens at GC time. -#[repr(C)] +#[repr(C)] // `alloc` must be the first member, it's accessed from JIT code pub struct VMExternRefActivationsTable { /// Structures used to perform fast bump allocation of storage of externref /// values. + /// + /// This is the only member of this structure accessed from JIT code. alloc: VMExternRefTableAlloc, /// When unioned with `chunk`, this is an over-approximation of the GC roots @@ -528,7 +525,7 @@ pub struct VMExternRefActivationsTable { stack_canary: Option, } -#[repr(C)] +#[repr(C)] // this is accessed from JTI code struct VMExternRefTableAlloc { /// Bump-allocation finger within the `chunk`. /// @@ -543,6 +540,8 @@ struct VMExternRefTableAlloc { end: NonNull, /// Bump allocation chunk that stores fast-path insertions. + /// + /// This is not accessed from JIT code. chunk: Box<[TableElem]>, } diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 35b9d2543ce0..60e6a8f08c0e 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -204,12 +204,31 @@ use wasmtime_runtime::{ #[repr(transparent)] // here for the C API pub struct Func(Stored); +/// The three ways that a function can be created and referenced from within a +/// store. pub(crate) enum FuncData { + /// A function already owned by the store via some other means. This is + /// used, for example, when creating a `Func` from an instance's exported + /// function. The instance's `InstanceHandle` is already owned by the store + /// and we just have some pointers into that which represent how to call the + /// function. StoreOwned { trampoline: VMTrampoline, export: ExportFunction, }, + + /// A function is shared across possibly other stores, hence the `Arc`. This + /// variant happens when a `Linker`-defined function is instantiated within + /// a `Store` (e.g. via `Linker::get` or similar APIs). The `Arc` here + /// indicates that there's some number of other stores holding this function + /// too, so dropping this may not deallocate the underlying + /// `InstanceHandle`. SharedHost(Arc), + + /// A uniquely-owned host function within a `Store`. This comes about with + /// `Func::new` or similar APIs. The `HostFunc` internally owns the + /// `InstanceHandle` and that will get dropped when this `HostFunc` itself + /// is dropped. Host(HostFunc), } diff --git a/crates/wasmtime/src/signatures.rs b/crates/wasmtime/src/signatures.rs index 74cb2496a0fc..0f4095c3b19c 100644 --- a/crates/wasmtime/src/signatures.rs +++ b/crates/wasmtime/src/signatures.rs @@ -64,27 +64,6 @@ impl SignatureCollection { .get(&index) .map(|(_, trampoline)| *trampoline) } - - ///// Registers a single function with the collection. - ///// - ///// Returns the shared signature index for the function. - //pub fn register( - // &mut self, - // ty: &WasmFuncType, - // trampoline: VMTrampoline, - //) -> VMSharedSignatureIndex { - // let index = self.registry.write().unwrap().register(ty); - - // let entry = match self.trampolines.entry(index) { - // Entry::Occupied(e) => e.into_mut(), - // Entry::Vacant(e) => e.insert((0, trampoline)), - // }; - - // // Increment the ref count - // entry.0 += 1; - - // index - //} } impl Drop for SignatureCollection { diff --git a/crates/wasmtime/src/store/context.rs b/crates/wasmtime/src/store/context.rs index 1c1d486bfec5..a8100251d488 100644 --- a/crates/wasmtime/src/store/context.rs +++ b/crates/wasmtime/src/store/context.rs @@ -2,11 +2,15 @@ use crate::store::{Store, StoreInner}; use std::ops::{Deref, DerefMut}; /// TODO -#[repr(transparent)] // here for the C API +// NB the repr(transparent) here is for the C API and it's important that the +// representation of this `struct` is a pointer for now. If the representation +// changes then the C API will need to be updated +#[repr(transparent)] pub struct StoreContext<'a, T>(pub(super) &'a StoreInner); /// TODO -#[repr(transparent)] // here for the C API +// NB the repr(transparent) here is for the same reason as above. +#[repr(transparent)] pub struct StoreContextMut<'a, T>(pub(super) &'a mut StoreInner); impl<'a, T> StoreContextMut<'a, T> { diff --git a/crates/wasmtime/src/store/data.rs b/crates/wasmtime/src/store/data.rs index 65a67412ee3e..d920c0e980d8 100644 --- a/crates/wasmtime/src/store/data.rs +++ b/crates/wasmtime/src/store/data.rs @@ -169,6 +169,8 @@ where } } +// NB the repre(transparent) here is for the usage of this throughout the C API. +// If the representation here changes then the C API will need changing as well. #[repr(transparent)] pub struct Stored { // See documentation below on `INDEX_BITS` for how this is interpreted. diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index 92efe37377cd..3c5d6791a275 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -230,8 +230,6 @@ impl WastContext { for directive in ast.directives { let sp = directive.span(); - let (line, col) = sp.linecol_in(wast); - println!("run directive on {}:{}:{}", filename, line + 1, col); self.run_directive(directive, &adjust_wast) .with_context(|| { let (line, col) = sp.linecol_in(wast); From 9d0ecf03f832d63e3735f895e5450b7997e1b0d3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 May 2021 08:26:02 -0700 Subject: [PATCH 38/90] Update wasmtime crate's documentation * Update all API documentation * Update book examples * Remove multithreading docs since there's now nothing more to add over what you generally do for multithreading in Rust anyway. * Tweak `Linker` functions that were not updated since multiple items per name was removed. This simplifies some of the `get_*` functions. --- crates/wasmtime/Cargo.toml | 4 - crates/wasmtime/src/engine.rs | 2 +- crates/wasmtime/src/externals.rs | 183 +++++++++--- crates/wasmtime/src/func.rs | 318 +++++++++++---------- crates/wasmtime/src/instance.rs | 95 ++++--- crates/wasmtime/src/lib.rs | 288 ++++++++++++------- crates/wasmtime/src/linker.rs | 397 ++++++++++++++------------- crates/wasmtime/src/memory.rs | 355 ++++++++++++------------ crates/wasmtime/src/store.rs | 96 ++++--- crates/wasmtime/src/store/context.rs | 61 +++- crates/wasmtime/src/types.rs | 11 - crates/wast/src/wast.rs | 3 +- docs/SUMMARY.md | 1 - docs/examples-rust-embed.md | 3 +- docs/examples-rust-hello-world.md | 5 +- docs/examples-rust-linking.md | 4 +- docs/examples-rust-multithreading.md | 148 ---------- docs/examples-rust-wasi.md | 64 ++++- src/commands/run.rs | 5 +- tests/all/async_functions.rs | 15 +- tests/all/host_funcs.rs | 55 ++-- tests/all/linker.rs | 4 +- 22 files changed, 1193 insertions(+), 924 deletions(-) delete mode 100644 docs/examples-rust-multithreading.md diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index a3d61a79d52e..a7e5a914cb9f 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -13,10 +13,6 @@ edition = "2018" # [package.metadata.docs.rs] # rustdoc-args = ["--cfg", "nightlydoc"] -[lib] -# TODO -doctest = false - [dependencies] wasmtime-runtime = { path = "../runtime", version = "0.26.0" } wasmtime-environ = { path = "../environ", version = "0.26.0" } diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 6634e1e6500e..bce82a8a9fcd 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -11,7 +11,7 @@ use wasmtime_runtime::{debug_builtins, InstanceAllocator}; /// modules. /// /// An engine can be safely shared across threads and is a cheap cloneable -/// handle to the actual engine. The engine itself will be deallocate once all +/// handle to the actual engine. The engine itself will be deallocated once all /// references to it have gone away. /// /// Engines store global configuration preferences such as compilation settings, diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index 2f8b881883c6..cdc32835e11a 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -26,7 +26,7 @@ pub enum Extern { /// A WebAssembly `global` which acts like a `Cell` of sorts, supporting /// `get` and `set` operations. Global(Global), - /// A WebAssembly `table` which is an array of `Val` types. + /// A WebAssembly `table` which is an array of `Val` reference types. Table(Table), /// A WebAssembly linear memory. Memory(Memory), @@ -98,6 +98,13 @@ impl Extern { } /// Returns the type associated with this `Extern`. + /// + /// The `store` argument provided must own this `Extern` and is used to look + /// up type information. + /// + /// # Panics + /// + /// Panics if this item does not belong to the `store` provided. pub fn ty(&self, store: impl AsContext) -> ExternType { let store = store.as_context(); match self { @@ -198,14 +205,10 @@ impl From for Extern { /// instructions will modify and read global values in a wasm module. Globals /// can either be imported or exported from wasm modules. /// -/// If you're familiar with Rust already you can think of a `Global` as a sort -/// of `Rc>`, more or less. -/// -/// # `Global` and `Clone` -/// -/// Globals are internally reference counted so you can `clone` a `Global`. The -/// cloning process only performs a shallow clone, so two cloned `Global` -/// instances are equivalent in their functionality. +/// A [`Global`] "belongs" to the store that it was originally created within +/// (either via [`Global::new`] or via instantiating a [`Module`]). Operations +/// on a [`Global`] only work with the store it belongs to, and if another store +/// is passed in by accident then methods will panic. #[derive(Copy, Clone, Debug)] #[repr(transparent)] // here for the C API pub struct Global(Stored); @@ -214,14 +217,47 @@ impl Global { /// Creates a new WebAssembly `global` value with the provide type `ty` and /// initial value `val`. /// - /// The `store` argument provided is used as a general global cache for - /// information, and otherwise the `ty` and `val` arguments are used to - /// initialize the global. + /// The `store` argument will be the owner of the [`Global`] returned. Using + /// the returned [`Global`] other items in the store may access this global. + /// For example this could be provided as an argument to + /// [`Instance::new`](crate::Instance::new) or + /// [`Linker::define`](crate::Linker::define). /// /// # Errors /// /// Returns an error if the `ty` provided does not match the type of the - /// value `val`. + /// value `val`, or if `val` comes from a different store than `store`. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// let engine = Engine::default(); + /// let mut store = Store::new(&engine, ()); + /// + /// let ty = GlobalType::new(ValType::I32, Mutability::Const); + /// let i32_const = Global::new(&mut store, ty, 1i32.into())?; + /// let ty = GlobalType::new(ValType::F64, Mutability::Var); + /// let f64_mut = Global::new(&mut store, ty, 2.0f64.into())?; + /// + /// let module = Module::new( + /// &engine, + /// "(module + /// (global (import \"\" \"i32-const\") i32) + /// (global (import \"\" \"f64-mut\") (mut f64)) + /// )" + /// )?; + /// + /// let mut linker = Linker::new(&engine); + /// linker.define("", "i32-const", i32_const)?; + /// linker.define("", "f64-mut", f64_mut)?; + /// + /// let instance = linker.instantiate(&mut store, &module)?; + /// // ... + /// # Ok(()) + /// # } + /// ``` pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result { Global::_new(&mut store.as_context_mut().opaque(), ty, val) } @@ -240,6 +276,10 @@ impl Global { } /// Returns the underlying type of this `global`. + /// + /// # Panics + /// + /// Panics if `store` does not own this global. pub fn ty(&self, store: impl AsContext) -> GlobalType { let store = store.as_context(); let ty = &store[self.0].global; @@ -247,6 +287,10 @@ impl Global { } /// Returns the current [`Val`] of this global. + /// + /// # Panics + /// + /// Panics if `store` does not own this global. pub fn get(&self, mut store: impl AsContextMut) -> Val { unsafe { let store = store.as_context_mut(); @@ -274,8 +318,13 @@ impl Global { /// /// # Errors /// - /// Returns an error if this global has a different type than `Val`, or if - /// it's not a mutable global. + /// Returns an error if this global has a different type than `Val`, if + /// it's not a mutable global, or if `val` comes from a different store than + /// the one provided. + /// + /// # Panics + /// + /// Panics if `store` does not own this global. pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> { let store = store.as_context_mut(); let ty = self.ty(&store); @@ -336,26 +385,22 @@ impl Global { /// A WebAssembly `table`, or an array of values. /// /// Like [`Memory`] a table is an indexed array of values, but unlike [`Memory`] -/// it's an array of WebAssembly values rather than bytes. One of the most -/// common usages of a table is a function table for wasm modules, where each -/// element has the `Func` type. -/// -/// Tables, like globals, are not threadsafe and can only be used on one thread. -/// Tables can be grown in size and each element can be read/written. +/// it's an array of WebAssembly reference type values rather than bytes. One of +/// the most common usages of a table is a function table for wasm modules (a +/// `funcref` table), where each element has the `ValType::FuncRef` type. /// -/// # `Table` and `Clone` -/// -/// Tables are internally reference counted so you can `clone` a `Table`. The -/// cloning process only performs a shallow clone, so two cloned `Table` -/// instances are equivalent in their functionality. +/// A [`Table`] "belongs" to the store that it was originally created within +/// (either via [`Table::new`] or via instantiating a [`Module`]). Operations +/// on a [`Table`] only work with the store it belongs to, and if another store +/// is passed in by accident then methods will panic. #[derive(Copy, Clone, Debug)] #[repr(transparent)] // here for the C API pub struct Table(Stored); impl Table { - /// Creates a new `Table` with the given parameters. + /// Creates a new [`Table`] with the given parameters. /// - /// * `store` - a global cache to store information in + /// * `store` - the owner of the resulting [`Table`] /// * `ty` - the type of this table, containing both the element type as /// well as the initial size and maximum size, if any. /// * `init` - the initial value to fill all table entries with, if the @@ -363,7 +408,35 @@ impl Table { /// /// # Errors /// - /// Returns an error if `init` does not match the element type of the table. + /// Returns an error if `init` does not match the element type of the table, + /// or if `init` does not belong to the `store` provided. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// let engine = Engine::default(); + /// let mut store = Store::new(&engine, ()); + /// + /// let ty = TableType::new(ValType::FuncRef, Limits::new(2, None)); + /// let table = Table::new(&mut store, ty, Val::FuncRef(None))?; + /// + /// let module = Module::new( + /// &engine, + /// "(module + /// (table (import \"\" \"\") 2 funcref) + /// (func $f (result i32) + /// i32.const 10) + /// (elem (i32.const 0) (func $f)) + /// )" + /// )?; + /// + /// let instance = Instance::new(&mut store, &module, &[table.into()])?; + /// // ... + /// # Ok(()) + /// # } + /// ``` pub fn new(mut store: impl AsContextMut, ty: TableType, init: Val) -> Result
{ Table::_new(&mut store.as_context_mut().opaque(), ty, init) } @@ -403,6 +476,10 @@ impl Table { /// Returns the underlying type of this table, including its element type as /// well as the maximum/minimum lower bounds. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. pub fn ty(&self, store: impl AsContext) -> TableType { let store = store.as_context(); let ty = &store[self.0].table.table; @@ -421,6 +498,10 @@ impl Table { /// Returns the table element value at `index`. /// /// Returns `None` if `index` is out of bounds. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Option { let mut store = store.as_context_mut().opaque(); let table = self.wasmtime_table(&mut store); @@ -439,8 +520,13 @@ impl Table { /// /// # Errors /// - /// Returns an error if `index` is out of bounds or if `val` does not have - /// the right type to be stored in this table. + /// Returns an error if `index` is out of bounds, if `val` does not have + /// the right type to be stored in this table, or if `val` belongs to a + /// different store. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. pub fn set(&self, mut store: impl AsContextMut, index: u32, val: Val) -> Result<()> { let ty = self.ty(&store).element().clone(); let mut store = store.as_context_mut().opaque(); @@ -454,6 +540,10 @@ impl Table { } /// Returns the current size of this table. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. pub fn size(&self, store: impl AsContext) -> u32 { let store = store.as_context(); unsafe { (*store[self.0].definition).current_elements } @@ -468,7 +558,12 @@ impl Table { /// /// Returns an error if the table cannot be grown by `delta`, for example /// if it would cause the table to exceed its maximum size. Also returns an - /// error if `init` is not of the right type. + /// error if `init` is not of the right type or if `init` does not belong to + /// `store`. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Val) -> Result { let ty = self.ty(&store).element().clone(); let mut store = store.as_context_mut().opaque(); @@ -493,6 +588,10 @@ impl Table { /// /// Returns an error if the range is out of bounds of either the source or /// destination tables. + /// + /// # Panics + /// + /// Panics if `store` does not own either `dst_table` or `src_table`. pub fn copy( mut store: impl AsContextMut, dst_table: &Table, @@ -528,6 +627,10 @@ impl Table { /// * the region to be filled is out of bounds, or /// /// * `val` comes from a different `Store` from this table. + /// + /// # Panics + /// + /// Panics if `store` does not own either `dst_table` or `src_table`. pub fn fill(&self, mut store: impl AsContextMut, dst: u32, val: Val, len: u32) -> Result<()> { let ty = self.ty(&store).element().clone(); let mut store = store.as_context_mut().opaque(); @@ -590,6 +693,10 @@ impl<'instance> Export<'instance> { } /// Return the `ExternType` of this export. + /// + /// # Panics + /// + /// Panics if `store` does not own this `Extern`. pub fn ty(&self, store: impl AsContext) -> ExternType { self.definition.ty(store) } @@ -622,4 +729,16 @@ impl<'instance> Export<'instance> { pub fn into_global(self) -> Option { self.definition.into_global() } + + /// Consume this `Export` and return the contained `Instance`, if it's a + /// instance, or `None` otherwise. + pub fn into_instance(self) -> Option { + self.definition.into_instance() + } + + /// Consume this `Export` and return the contained `Module`, if it's a + /// module, or `None` otherwise. + pub fn into_module(self) -> Option { + self.definition.into_module() + } } diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 60e6a8f08c0e..7e58cbb5a84d 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -24,22 +24,16 @@ use wasmtime_runtime::{ /// A WebAssembly function which can be called. /// -/// This type can represent a number of callable items, such as: -/// -/// * An exported function from a WebAssembly module. -/// * A user-defined function used to satisfy an import. -/// -/// These types of callable items are all wrapped up in this `Func` and can be -/// used to both instantiate an [`Instance`] as well as be extracted from an -/// [`Instance`]. +/// This type can represent either an exported function from a WebAssembly +/// module or a host-defined function which can be used to satisfy an import of +/// a module. [`Func`] and can be used to both instantiate an [`Instance`] as +/// well as be extracted from an [`Instance`]. /// /// [`Instance`]: crate::Instance /// -/// # `Func` and `Clone` -/// -/// Functions are internally reference counted so you can `clone` a `Func`. The -/// cloning process only performs a shallow clone, so two cloned `Func` -/// instances are equivalent in their functionality. +/// A [`Func`] "belongs" to the store that it was originally created within. +/// Operations on a [`Func`] only work with the store it belongs to, and if +/// another store is passed in by accident then methods will panic. /// /// # `Func` and `async` /// @@ -53,26 +47,13 @@ use wasmtime_runtime::{ /// functions through [`Func::call`] or [`Func::call_async`] (or the typed /// wrappers such as [`TypedFunc::call`] vs [`TypedFunc::call_async`]). /// -/// Note that asynchronous function APIs here are a bit trickier than their -/// synchronous brethren. For example [`Func::new_async`] and -/// [`Func::wrapN_async`](Func::wrap1_async) take explicit state parameters to -/// allow you to close over the state in the returned future. It's recommended -/// that you pass state via these parameters instead of through the closure's -/// environment, which may give Rust lifetime errors. Additionally unlike -/// synchronous functions which can all get wrapped through [`Func::wrap`] -/// asynchronous functions need to explicitly wrap based on the number of -/// parameters that they have (e.g. no wasm parameters gives you -/// [`Func::wrap0_async`], one wasm parameter you'd use [`Func::wrap1_async`], -/// etc). Be sure to consult the documentation for [`Func::wrap`] for how the -/// wasm type signature is inferred from the Rust type signature. -/// /// # To `Func::call` or to `Func::typed().call()` /// -/// There's a 2x2 matrix of methods to call `Func`. Invocations can either be +/// There's a 2x2 matrix of methods to call [`Func`]. Invocations can either be /// asynchronous or synchronous. They can also be statically typed or not. /// Whether or not an invocation is asynchronous is indicated via the method -/// being `async` and `call_async` being the entry point. Otherwise for -/// statically typed or not your options are: +/// being `async` and [`call_async`](Func::call_async) being the entry point. +/// Otherwise for statically typed or not your options are: /// /// * Dynamically typed - if you don't statically know the signature of the /// function that you're calling you'll be using [`Func::call`] or @@ -103,26 +84,26 @@ use wasmtime_runtime::{ /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { /// let engine = Engine::default(); -/// let store = Store::new(&engine); /// let module = Module::new(&engine, r#"(module (func (export "foo")))"#)?; -/// let instance = Instance::new(&store, &module, &[])?; -/// let foo = instance.get_func("foo").expect("export wasn't a function"); +/// let mut store = Store::new(&engine, ()); +/// let instance = Instance::new(&mut store, &module, &[])?; +/// let foo = instance.get_func(&mut store, "foo").expect("export wasn't a function"); /// /// // Work with `foo` as a `Func` at this point, such as calling it /// // dynamically... -/// match foo.call(&[]) { +/// match foo.call(&mut store, &[]) { /// Ok(result) => { /* ... */ } /// Err(trap) => { /// panic!("execution of `foo` resulted in a wasm trap: {}", trap); /// } /// } -/// foo.call(&[])?; +/// foo.call(&mut store, &[])?; /// /// // ... or we can make a static assertion about its signature and call it. /// // Our first call here can fail if the signatures don't match, and then the /// // second call can fail if the function traps (like the `match` above). -/// let foo = foo.typed::<(), ()>()?; -/// foo.call(())?; +/// let foo = foo.typed::<(), (), _>(&store)?; +/// foo.call(&mut store, ())?; /// # Ok(()) /// # } /// ``` @@ -133,11 +114,11 @@ use wasmtime_runtime::{ /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { -/// let store = Store::default(); +/// let mut store = Store::<()>::default(); /// /// // Create a custom `Func` which can execute arbitrary code inside of the /// // closure. -/// let add = Func::wrap(&store, |a: i32, b: i32| -> i32 { a + b }); +/// let add = Func::wrap(&mut store, |a: i32, b: i32| -> i32 { a + b }); /// /// // Next we can hook that up to a wasm module which uses it. /// let module = Module::new( @@ -155,10 +136,10 @@ use wasmtime_runtime::{ /// i32.add)) /// "#, /// )?; -/// let instance = Instance::new(&store, &module, &[add.into()])?; -/// let call_add_twice = instance.get_typed_func::<(), i32>("call_add_twice")?; +/// let instance = Instance::new(&mut store, &module, &[add.into()])?; +/// let call_add_twice = instance.get_typed_func::<(), i32, _>(&mut store, "call_add_twice")?; /// -/// assert_eq!(call_add_twice.call(())?, 10); +/// assert_eq!(call_add_twice.call(&mut store, ())?, 10); /// # Ok(()) /// # } /// ``` @@ -168,7 +149,7 @@ use wasmtime_runtime::{ /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { -/// let store = Store::default(); +/// let mut store = Store::<()>::default(); /// /// // Here we need to define the type signature of our `Double` function and /// // then wrap it up in a `Func` @@ -176,7 +157,7 @@ use wasmtime_runtime::{ /// [wasmtime::ValType::I32].iter().cloned(), /// [wasmtime::ValType::I32].iter().cloned(), /// ); -/// let double = Func::new(&store, double_type, |_, params, results| { +/// let double = Func::new(&mut store, double_type, |_, params, results| { /// let mut value = params[0].unwrap_i32(); /// value *= 2; /// results[0] = value.into(); @@ -195,7 +176,7 @@ use wasmtime_runtime::{ /// (start $start)) /// "#, /// )?; -/// let instance = Instance::new(&store, &module, &[double.into()])?; +/// let instance = Instance::new(&mut store, &module, &[double.into()])?; /// // .. work with `instance` if necessary /// # Ok(()) /// # } @@ -291,13 +272,13 @@ macro_rules! generate_wrap_async_func { impl Func { /// Creates a new `Func` with the given arguments, typically to create a - /// user-defined function to pass as an import to a module. + /// host-defined function to pass as an import to a module. /// - /// * `store` - a cache of data where information is stored, typically - /// shared with a [`Module`](crate::Module). + /// * `store` - the store in which to create this [`Func`], which will own + /// the return value. /// /// * `ty` - the signature of this function, used to indicate what the - /// inputs and outputs are, which must be WebAssembly types. + /// inputs and outputs are. /// /// * `func` - the native code invoked whenever this `Func` will be called. /// This closure is provided a [`Caller`] as its first argument to learn @@ -305,14 +286,20 @@ impl Func { /// parameters as a slice along with a mutable slice of where to write /// results. /// - /// Note that the implementation of `func` must adhere to the `ty` - /// signature given, error or traps may occur if it does not respect the - /// `ty` signature. + /// Note that the implementation of `func` must adhere to the `ty` signature + /// given, error or traps may occur if it does not respect the `ty` + /// signature. For example if the function type declares that it returns one + /// i32 but the `func` closures does not write anything into the results + /// slice then a trap may be generated. /// /// Additionally note that this is quite a dynamic function since signatures - /// are not statically known. For a more performant `Func` it's recommended - /// to use [`Func::wrap`] if you can because with statically known - /// signatures the engine can optimize the implementation much more. + /// are not statically known. For a more performant and ergonomic `Func` + /// it's recommended to use [`Func::wrap`] if you can because with + /// statically known signatures Wasmtime can optimize the implementation + /// much more. + /// + /// For more information about `Send + Sync + 'static` requirements on the + /// `func`, see [`Func::wrap`](#why-send--sync--static). pub fn new( mut store: impl AsContextMut, ty: FuncType, @@ -333,25 +320,15 @@ impl Func { /// and then return the result to WebAssembly. /// /// This function is the asynchronous analogue of [`Func::new`] and much of - /// that documentation applies to this as well. There are a few key - /// differences (besides being asynchronous) that are worth pointing out: - /// - /// * The state parameter `T` is passed to the provided function `F` on - /// each invocation. This is done so you can use the state in `T` in the - /// computation of the output future (the future can close over this - /// value). Unfortunately due to limitations of async-in-Rust right now - /// you **cannot** close over the captured variables in `F` itself in the - /// returned future. This means that you likely won't close over much - /// state in `F` and will instead use `T`. - /// - /// * The closure here returns a *boxed* future, not something that simply - /// implements a future. This is also unfortunately due to limitations in - /// Rust right now. - /// - /// Overall we're not super happy with this API signature and would love to - /// change it to make it more ergonomic. Despite this, however, you should - /// be able to still hook into asynchronous computations and plug them into - /// wasm. Improvements are always welcome with PRs! + /// that documentation applies to this as well. The key difference is that + /// `func` returns a future instead of simply a `Result`. Note that the + /// returned future can close over any of the arguments, but it cannot close + /// over the state of the closure itself. It's recommended to store any + /// necessary async state in the `T` of the [`Store`](crate::Store) which + /// can be accessed through [`Caller::data`] or [`Caller::data_mut`]. + /// + /// For more information on `Send + Sync + 'static`, see + /// [`Func::wrap`](#why-send--sync--static). /// /// # Panics /// @@ -382,14 +359,17 @@ impl Func { /// /// // Using `new_async` we can hook up into calling our async /// // `get_row_count` function. - /// let store = Store::new(&Engine::new(Config::new().async_support(true))?); + /// let engine = Engine::new(Config::new().async_support(true))?; + /// let mut store = Store::new(&engine, MyDatabase { + /// // ... + /// }); /// let get_row_count_type = wasmtime::FuncType::new( /// None, /// Some(wasmtime::ValType::I32), /// ); - /// let double = Func::new_async(&store, get_row_count_type, my_database, |_, database, params, results| { + /// let get = Func::new_async(&mut store, get_row_count_type, |caller, _params, results| { /// Box::new(async move { - /// let count = database.get_row_count().await; + /// let count = caller.data().get_row_count().await; /// results[0] = Val::I32(count as i32); /// Ok(()) /// }) @@ -477,6 +457,30 @@ impl Func { /// sufficient inlining and optimization the WebAssembly will call straight /// into `func` provided, with no extra fluff entailed. /// + /// # Why `Send + Sync + 'static`? + /// + /// All host functions defined in a [`Store`](crate::Store) (including + /// those from [`Func::new`] and other constructors) require that the + /// `func` provided is `Send + Sync + 'static`. Additionally host functions + /// always are `Fn` as opposed to `FnMut` or `FnOnce`. This can at-a-glance + /// feel restrictive since the closure cannot close over as many types as + /// before. The reason for this, though, is to ensure that + /// [`Store`](crate::Store) can implement both the `Send` and `Sync` + /// traits. + /// + /// Fear not, however, because this isn't as restrictive as it seems! Host + /// functions are provided a [`Caller<'_, T>`](crate::Caller) argument which + /// allows access to the host-defined data within the + /// [`Store`](crate::Store). The `T` type is not required to be any of + /// `Send`, `Sync`, or `'static`! This means that you can store whatever + /// you'd like in `T` and have it accessible by all host functions. + /// Additionally mutable access to `T` is allowed through + /// [`Caller::data_mut`]. + /// + /// Most host-defined [`Func`] values provide closures that end up not + /// actually closing over any values. These zero-sized types will use the + /// context from [`Caller`] for host-defined information. + /// /// # Examples /// /// First up we can see how simple wasm imports can be implemented, such @@ -485,8 +489,8 @@ impl Func { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let add = Func::wrap(&store, |a: i32, b: i32| a + b); + /// # let mut store = Store::<()>::default(); + /// let add = Func::wrap(&mut store, |a: i32, b: i32| a + b); /// let module = Module::new( /// store.engine(), /// r#" @@ -498,9 +502,9 @@ impl Func { /// call $add)) /// "#, /// )?; - /// let instance = Instance::new(&store, &module, &[add.into()])?; - /// let foo = instance.get_typed_func::<(i32, i32), i32>("foo")?; - /// assert_eq!(foo.call((1, 2))?, 3); + /// let instance = Instance::new(&mut store, &module, &[add.into()])?; + /// let foo = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "foo")?; + /// assert_eq!(foo.call(&mut store, (1, 2))?, 3); /// # Ok(()) /// # } /// ``` @@ -511,8 +515,8 @@ impl Func { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let add = Func::wrap(&store, |a: i32, b: i32| { + /// # let mut store = Store::<()>::default(); + /// let add = Func::wrap(&mut store, |a: i32, b: i32| { /// match a.checked_add(b) { /// Some(i) => Ok(i), /// None => Err(Trap::new("overflow")), @@ -529,10 +533,10 @@ impl Func { /// call $add)) /// "#, /// )?; - /// let instance = Instance::new(&store, &module, &[add.into()])?; - /// let foo = instance.get_typed_func::<(i32, i32), i32>("foo")?; - /// assert_eq!(foo.call((1, 2))?, 3); - /// assert!(foo.call((i32::max_value(), 1)).is_err()); + /// let instance = Instance::new(&mut store, &module, &[add.into()])?; + /// let foo = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "foo")?; + /// assert_eq!(foo.call(&mut store, (1, 2))?, 3); + /// assert!(foo.call(&mut store, (i32::max_value(), 1)).is_err()); /// # Ok(()) /// # } /// ``` @@ -542,8 +546,8 @@ impl Func { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let debug = Func::wrap(&store, |a: i32, b: u32, c: f32, d: i64, e: u64, f: f64| { + /// # let mut store = Store::<()>::default(); + /// let debug = Func::wrap(&mut store, |a: i32, b: u32, c: f32, d: i64, e: u64, f: f64| { /// /// println!("a={}", a); /// println!("b={}", b); @@ -567,9 +571,9 @@ impl Func { /// call $debug)) /// "#, /// )?; - /// let instance = Instance::new(&store, &module, &[debug.into()])?; - /// let foo = instance.get_typed_func::<(), ()>("foo")?; - /// foo.call(())?; + /// let instance = Instance::new(&mut store, &module, &[debug.into()])?; + /// let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?; + /// foo.call(&mut store, ())?; /// # Ok(()) /// # } /// ``` @@ -582,32 +586,24 @@ impl Func { /// /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let log_str = Func::wrap(&store, |caller: Caller<'_>, ptr: i32, len: i32| { + /// # let mut store = Store::default(); + /// let log_str = Func::wrap(&mut store, |mut caller: Caller<'_, ()>, ptr: i32, len: i32| { /// let mem = match caller.get_export("memory") { /// Some(Extern::Memory(mem)) => mem, /// _ => return Err(Trap::new("failed to find host memory")), /// }; - /// - /// // We're reading raw wasm memory here so we need `unsafe`. Note - /// // though that this should be safe because we don't reenter wasm - /// // while we're reading wasm memory, nor should we clash with - /// // any other memory accessors (assuming they're well-behaved - /// // too). - /// unsafe { - /// let data = mem.data_unchecked() - /// .get(ptr as u32 as usize..) - /// .and_then(|arr| arr.get(..len as u32 as usize)); - /// let string = match data { - /// Some(data) => match str::from_utf8(data) { - /// Ok(s) => s, - /// Err(_) => return Err(Trap::new("invalid utf-8")), - /// }, - /// None => return Err(Trap::new("pointer/length out of bounds")), - /// }; - /// assert_eq!(string, "Hello, world!"); - /// println!("{}", string); - /// } + /// let data = mem.data(&caller) + /// .get(ptr as u32 as usize..) + /// .and_then(|arr| arr.get(..len as u32 as usize)); + /// let string = match data { + /// Some(data) => match str::from_utf8(data) { + /// Ok(s) => s, + /// Err(_) => return Err(Trap::new("invalid utf-8")), + /// }, + /// None => return Err(Trap::new("pointer/length out of bounds")), + /// }; + /// assert_eq!(string, "Hello, world!"); + /// println!("{}", string); /// Ok(()) /// }); /// let module = Module::new( @@ -623,9 +619,9 @@ impl Func { /// (data (i32.const 4) "Hello, world!")) /// "#, /// )?; - /// let instance = Instance::new(&store, &module, &[log_str.into()])?; - /// let foo = instance.get_typed_func::<(), ()>("foo")?; - /// foo.call(())?; + /// let instance = Instance::new(&mut store, &module, &[log_str.into()])?; + /// let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?; + /// foo.call(&mut store, ())?; /// # Ok(()) /// # } /// ``` @@ -645,6 +641,10 @@ impl Func { for_each_function_signature!(generate_wrap_async_func); /// Returns the underlying wasm type that this `Func` has. + /// + /// # Panics + /// + /// Panics if `store` does not own this function. pub fn ty(&self, store: impl AsContext) -> FuncType { // Signatures should always be registered in the engine's registry of // shared signatures, so we should be able to unwrap safely here. @@ -674,7 +674,7 @@ impl Func { /// /// This function will panic if called on a function belonging to an async /// store. Asynchronous stores must always use `call_async`. - /// initiates a panic. + /// initiates a panic. Also panics if `store` does not own this function. pub fn call(&self, mut store: impl AsContextMut, params: &[Val]) -> Result> { assert!( !cfg!(feature = "async") || !store.as_context().async_support(), @@ -704,7 +704,8 @@ impl Func { /// # Panics /// /// Panics if this is called on a function in a synchronous store. This - /// only works with functions defined within an asynchronous store. + /// only works with functions defined within an asynchronous store. Also + /// panics if `store` does not own this function. #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] pub async fn call_async( @@ -911,6 +912,10 @@ impl Func { /// function. This behaves the same way as `Params`, but just for the /// results of the function. /// + /// The `S` type parameter represents the method of passing in the store + /// context, and can typically be specified as simply `_` when calling this + /// function. + /// /// Translation between Rust types and WebAssembly types looks like: /// /// | WebAssembly | Rust | @@ -940,6 +945,10 @@ impl Func { /// This function will return an error if `Params` or `Results` does not /// match the native type of this WebAssembly function. /// + /// # Panics + /// + /// This method will panic if `store` does not own this function. + /// /// # Examples /// /// An end-to-end example of calling a function which takes no parameters @@ -949,18 +958,18 @@ impl Func { /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { /// let engine = Engine::default(); - /// let store = Store::new(&engine); + /// let mut store = Store::new(&engine, ()); /// let module = Module::new(&engine, r#"(module (func (export "foo")))"#)?; - /// let instance = Instance::new(&store, &module, &[])?; - /// let foo = instance.get_func("foo").expect("export wasn't a function"); + /// let instance = Instance::new(&mut store, &module, &[])?; + /// let foo = instance.get_func(&mut store, "foo").expect("export wasn't a function"); /// /// // Note that this call can fail due to the typecheck not passing, but /// // in our case we statically know the module so we know this should /// // pass. - /// let typed = foo.typed::<(), ()>()?; + /// let typed = foo.typed::<(), (), _>(&store)?; /// /// // Note that this can fail if the wasm traps at runtime. - /// typed.call(())?; + /// typed.call(&mut store, ())?; /// # Ok(()) /// # } /// ``` @@ -969,9 +978,9 @@ impl Func { /// /// ``` /// # use wasmtime::*; - /// # fn foo(add: &Func) -> anyhow::Result<()> { - /// let typed = add.typed::<(i32, i64), f32>()?; - /// assert_eq!(typed.call((1, 2))?, 3.0); + /// # fn foo(add: &Func, mut store: Store<()>) -> anyhow::Result<()> { + /// let typed = add.typed::<(i32, i64), f32, _>(&store)?; + /// assert_eq!(typed.call(&mut store, (1, 2))?, 3.0); /// # Ok(()) /// # } /// ``` @@ -982,9 +991,9 @@ impl Func { /// # #[cfg(not(feature = "old-x86-backend"))] /// # use wasmtime::*; /// # #[cfg(not(feature = "old-x86-backend"))] - /// # fn foo(add_with_overflow: &Func) -> anyhow::Result<()> { - /// let typed = add_with_overflow.typed::<(u32, u32), (u32, i32)>()?; - /// let (result, overflow) = typed.call((u32::max_value(), 2))?; + /// # fn foo(add_with_overflow: &Func, mut store: Store<()>) -> anyhow::Result<()> { + /// let typed = add_with_overflow.typed::<(u32, u32), (u32, i32), _>(&store)?; + /// let (result, overflow) = typed.call(&mut store, (u32::max_value(), 2))?; /// assert_eq!(result, 1); /// assert_eq!(overflow, 1); /// # Ok(()) @@ -996,12 +1005,12 @@ impl Func { Results: WasmResults, S: AsContext, { - // First type-check that the params/results are all valid... + // Type-check that the params/results are all valid let ty = self.ty(store); Params::typecheck(ty.params()).context("type mismatch with parameters")?; Results::typecheck(ty.results()).context("type mismatch with results")?; - // ... then we can construct the typed version of this function + // and then we can construct the typed version of this function // (unsafely), which should be safe since we just did the type check above. unsafe { Ok(TypedFunc::new_unchecked(*self)) } } @@ -1423,25 +1432,26 @@ pub trait IntoFunc: Send + Sync + 'static { fn into_func(self, engine: &Engine) -> (InstanceHandle, VMTrampoline); } -/// A structure representing the *caller's* context when creating a function +/// A structure representing the caller's context when creating a function /// via [`Func::wrap`]. /// /// This structure can be taken as the first parameter of a closure passed to -/// [Func::wrap], and it can be used to learn information about the caller of -/// the function, such as the calling module's memory, exports, etc. +/// [`Func::wrap`] or other constructors, and serves two purposes: /// -/// The primary purpose of this structure is to provide access to the -/// caller's information, namely it's exported memory and exported functions. This -/// allows functions which take pointers as arguments to easily read the memory the -/// pointers point into, or if a function is expected to call malloc in the wasm -/// module to reserve space for the output you can do that. +/// * First consumers can use [`Caller<'_, T>`](crate::Caller) to get access to +/// [`StoreContextMut<'_, T>`](crate::StoreContextMut) and/or get access to +/// `T` itself. This means that the [`Caller`] type can serve as a proxy to +/// the original [`Store`](crate::Store) itself and is used to satisfy +/// [`AsContext`] and [`AsContextMut`] bounds. /// -/// Note that this Caller type a pretty temporary mechanism for accessing the -/// caller's information until interface types has been fully standardized and -/// implemented. The interface types proposal will obsolete this type and this will -/// be removed in the future at some point after interface types is implemented. If -/// you're relying on this Caller type it's recommended to become familiar with -/// interface types to ensure that your use case is covered by the proposal. +/// * Second a [`Caller`] can be used as the name implies, learning about the +/// caller's context, namely it's exported memory and exported functions. This +/// allows functions which take pointers as arguments to easily read the +/// memory the pointers point into, or if a function is expected to call +/// malloc in the wasm module to reserve space for the output you can do that. +/// +/// Host functions which want access to [`Store`](crate::Store)-level state are +/// recommended to use this type. pub struct Caller<'a, T> { pub(crate) store: StoreContextMut<'a, T>, caller: &'a InstanceHandle, @@ -1469,11 +1479,17 @@ impl Caller<'_, T> { /// /// Note that this function is only implemented for the `Extern::Memory` /// and the `Extern::Func` types currently. No other exported structures - /// can be acquired through this just yet, but this may be implemented - /// in the future! - /// - /// Note that when accessing and calling exported functions, one should adhere - /// to the guidelines of the interface types proposal. + /// can be acquired through this method. + /// + /// Note that when accessing and calling exported functions, one should + /// adhere to the guidelines of the interface types proposal. This method + /// is a temporary mechanism for accessing the caller's information until + /// interface types has been fully standardized and implemented. The + /// interface types proposal will obsolete this type and this will be + /// removed in the future at some point after interface types is + /// implemented. If you're relying on this method type it's recommended to + /// become familiar with interface types to ensure that your use case is + /// covered by the proposal. /// /// # Return /// diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 7d933c1cb7b7..0ca5a166bce2 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -22,17 +22,14 @@ use wasmtime_runtime::{ /// This type represents the instantiation of a [`Module`]. Once instantiated /// you can access the [`exports`](Instance::exports) which are of type /// [`Extern`] and provide the ability to call functions, set globals, read -/// memory, etc. This is where all the fun stuff happens! +/// memory, etc. When interacting with any wasm code you'll want to make an +/// [`Instance`] to call any code or execute anything. /// -/// An [`Instance`] is created from two inputs, a [`Module`] and a list of -/// imports, provided as a list of [`Extern`] values. The [`Module`] is the wasm -/// code that was compiled and we're instantiating, and the [`Extern`] imports -/// are how we're satisfying the imports of the module provided. On successful -/// instantiation an [`Instance`] will automatically invoke the wasm `start` -/// function. -/// -/// When interacting with any wasm code you'll want to make an [`Instance`] to -/// call any code or execute anything! +/// Instances are owned by a [`Store`](crate::Store) which is passed in at +/// creation time. It's recommended to create instances with +/// [`Linker::instantiate`](crate::Linker::instantiate) or similar +/// [`Linker`](crate::Linker) methods, but a more low-level constructor is also +/// available as [`Instance::new`]. #[derive(Copy, Clone, Debug)] #[repr(transparent)] pub struct Instance(Stored); @@ -45,32 +42,30 @@ impl Instance { /// following the procedure in the [core specification][inst] to /// instantiate. Instantiation can fail for a number of reasons (many /// specified below), but if successful the `start` function will be - /// automatically run (if provided) and then the [`Instance`] will be - /// returned. + /// automatically run (if specified in the `module`) and then the + /// [`Instance`] will be returned. /// /// Per the WebAssembly spec, instantiation includes running the module's /// start function, if it has one (not to be confused with the `_start` /// function, which is not run). /// - /// Note that this is a low-level function that just performance an - /// instantiation. See the `Linker` struct for an API which provides a - /// convenient way to link imports and provides automatic Command and Reactor - /// behavior. + /// Note that this is a low-level function that just performs an + /// instantiation. See the [`Linker`](crate::Linker) struct for an API which + /// provides a convenient way to link imports and provides automatic Command + /// and Reactor behavior. /// /// ## Providing Imports /// - /// The `imports` array here is a bit tricky. The entries in the list of - /// `imports` are intended to correspond 1:1 with the list of imports - /// returned by [`Module::imports`]. Before calling [`Instance::new`] you'll - /// want to inspect the return value of [`Module::imports`] and, for each - /// import type, create an [`Extern`] which corresponds to that type. - /// These [`Extern`] values are all then collected into a list and passed to - /// this function. + /// The entries in the list of `imports` are intended to correspond 1:1 + /// with the list of imports returned by [`Module::imports`]. Before + /// calling [`Instance::new`] you'll want to inspect the return value of + /// [`Module::imports`] and, for each import type, create an [`Extern`] + /// which corresponds to that type. These [`Extern`] values are all then + /// collected into a list and passed to this function. /// - /// Note that this function is intentionally relatively low level. It is the - /// intention that we'll soon provide a [higher level API][issue] which will - /// be much more ergonomic for instantiating modules. If you need the full - /// power of customization of imports, though, this is the method for you! + /// Note that this function is intentionally relatively low level. For an + /// easier time passing imports by doing name-based resolution it's + /// recommended to instead use the [`Linker`](crate::Linker) type. /// /// ## Errors /// @@ -91,10 +86,10 @@ impl Instance { /// # Panics /// /// This function will panic if called with a store associated with a - /// [`asynchronous config`](crate::Config::async_support). + /// [`asynchronous config`](crate::Config::async_support). This function + /// will also panic if any [`Extern`] supplied is not owned by `store`. /// /// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation - /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727 /// [`ExternType`]: crate::ExternType pub fn new( mut store: impl AsContextMut, @@ -139,9 +134,13 @@ impl Instance { /// /// # Panics /// - /// This function will panic if called with a store associated with a [`synchronous - /// config`](crate::Config::new). This is only compatible with stores associated with - /// an [`asynchronous config`](crate::Config::async_support). + /// This function will panic if called with a store associated with a + /// [`synchronous config`](crate::Config::new). This is only compatible with + /// stores associated with an [`asynchronous + /// config`](crate::Config::async_support). + /// + /// This function will also panic, like [`Instance::new`], if any [`Extern`] + /// specified does not belong to `store`. #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] pub async fn new_async( @@ -190,6 +189,10 @@ impl Instance { } /// Returns the type signature of this instance. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn ty(&self, store: impl AsContext) -> InstanceType { let store = store.as_context(); let items = &store[self.0]; @@ -209,6 +212,10 @@ impl Instance { } /// Returns the list of exported items from this [`Instance`]. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn exports<'a, T: 'a>( &'a self, store: impl Into>, @@ -225,6 +232,10 @@ impl Instance { /// the value, if found. /// /// Returns `None` if there was no export named `name`. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn get_export(&self, store: impl AsContextMut, name: &str) -> Option { let store = store.as_context(); store[self.0].get(name).cloned() @@ -234,6 +245,10 @@ impl Instance { /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a function. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn get_func(&self, store: impl AsContextMut, name: &str) -> Option { self.get_export(store, name)?.into_func() } @@ -245,6 +260,10 @@ impl Instance { /// /// Returns an error if `name` isn't a function export or if the export's /// type did not match `Params` or `Results` + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn get_typed_func( &self, mut store: S, @@ -266,6 +285,10 @@ impl Instance { /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a table. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn get_table(&self, store: impl AsContextMut, name: &str) -> Option
{ self.get_export(store, name)?.into_table() } @@ -274,6 +297,10 @@ impl Instance { /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a memory. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn get_memory(&self, store: impl AsContextMut, name: &str) -> Option { self.get_export(store, name)?.into_memory() } @@ -282,6 +309,10 @@ impl Instance { /// /// Returns `None` if there was no export named `name`, or if there was but /// it wasn't a global. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. pub fn get_global(&self, store: impl AsContextMut, name: &str) -> Option { self.get_export(store, name)?.into_global() } diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 91c612449d29..801e9cd9b0fc 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -7,13 +7,14 @@ //! globals, etc, which can do things that WebAssembly cannot (such as print to //! the screen). //! -//! The `wasmtime` crate draws inspiration from a number of sources, including +//! The `wasmtime` crate has similar concepts to the //! the [JS WebAssembly //! API](https://developer.mozilla.org/en-US/docs/WebAssembly) as well as the -//! [proposed C API](https://github.com/webassembly/wasm-c-api). As with all -//! other Rust code you're guaranteed that programs will be safe (not have -//! undefined behavior or segfault) so long as you don't use `unsafe` in your -//! own program. With `wasmtime` you can easily and conveniently embed a +//! [proposed C API](https://github.com/webassembly/wasm-c-api), but the Rust +//! API is designed for efficiency, ergonomics, and expressivity in Rust. As +//! with all other Rust code you're guaranteed that programs will be safe (not +//! have undefined behavior or segfault) so long as you don't use `unsafe` in +//! your own program. With `wasmtime` you can easily and conveniently embed a //! WebAssembly runtime with confidence that the WebAssembly is safely //! sandboxed. //! @@ -24,35 +25,36 @@ //! use wasmtime::*; //! //! fn main() -> Result<()> { -//! // All wasm objects operate within the context of a "store" -//! let store = Store::default(); -//! //! // Modules can be compiled through either the text or binary format +//! let engine = Engine::default(); //! let wat = r#" //! (module -//! (import "" "" (func $host_hello (param i32))) +//! (import "host" "hello" (func $host_hello (param i32))) //! //! (func (export "hello") //! i32.const 3 //! call $host_hello) //! ) //! "#; -//! let module = Module::new(store.engine(), wat)?; +//! let module = Module::new(&engine, wat)?; //! -//! // Host functions can be defined which take/return wasm values and -//! // execute arbitrary code on the host. -//! let host_hello = Func::wrap(&store, |param: i32| { +//! // All wasm objects operate within the context of a "store". Each +//! // `Store` has a type parameter to store host-specific data, which in +//! // this case we're using `4` for. +//! let mut store = Store::new(&engine, 4); +//! let host_hello = Func::wrap(&mut store, |caller: Caller<'_, u32>, param: i32| { //! println!("Got {} from WebAssembly", param); +//! println!("my host state is: {}", caller.data()); //! }); //! //! // Instantiation of a module requires specifying its imports and then //! // afterwards we can fetch exports by name, as well as asserting the //! // type signature of the function with `get_typed_func`. -//! let instance = Instance::new(&store, &module, &[host_hello.into()])?; -//! let hello = instance.get_typed_func::<(), ()>("hello")?; +//! let instance = Instance::new(&mut store, &module, &[host_hello.into()])?; +//! let hello = instance.get_typed_func::<(), (), _>(&mut store, "hello")?; //! -//! // And finally we can call the wasm as if it were a Rust function! -//! hello.call(())?; +//! // And finally we can call the wasm! +//! hello.call(&mut store, ())?; //! //! Ok(()) //! } @@ -63,79 +65,166 @@ //! There are a number of core types and concepts that are important to be aware //! of when using the `wasmtime` crate: //! -//! * Reference counting - almost all objects in this API are reference counted. -//! Most of the time when and object is `clone`d you're just bumping a -//! reference count. For example when you clone an [`Instance`] that is a -//! cheap operation, it doesn't create an entirely new instance. -//! -//! * [`Store`] - all WebAssembly object and host values will be "connected" to -//! a store. A [`Store`] is not threadsafe which means that itself and all -//! objects connected to it are pinned to a single thread (this happens -//! automatically through a lack of the `Send` and `Sync` traits). Similarly -//! `wasmtime` does not have a garbage collector so anything created within a -//! [`Store`] will not be deallocated until all references have gone away. See -//! the [`Store`] documentation for more information. +//! * [`Engine`] - a global compilation environment for WebAssembly. An +//! [`Engine`] is an object that can be shared concurrently across threads and +//! is created with a [`Config`] to tweak various settings. Compilation of any +//! WebAssembly requires first configuring and creating an [`Engine`]. //! //! * [`Module`] - a compiled WebAssembly module. This structure represents //! in-memory JIT code which is ready to execute after being instantiated. //! It's often important to cache instances of a [`Module`] because creation //! (compilation) can be expensive. Note that [`Module`] is safe to share -//! across threads. +//! across threads, and can be created from a WebAssembly binary and an +//! [`Engine`] with [`Module::new`]. Caching can either happen with +//! [`Engine::precompile_module`] or [`Module::serialize`], feeding those +//! bytes back into [`Module::deserialize`]. +//! +//! * [`Store`] - container for all information related to WebAssembly objects +//! such as functions, instances, memories, etc. A [`Store`][`Store`] +//! allows customization of the `T` to store arbitrary host data within a +//! [`Store`]. This host data can be accessed through host functions via the +//! [`Caller`] function parameter in host-defined functions. A [`Store`] is +//! required for all WebAssembly operations, such as calling a wasm function. +//! The [`Store`] is passed in as a "context" to methods like [`Func::call`]. +//! Dropping a [`Store`] will deallocate all memory associated with +//! WebAssembly objects within the [`Store`]. //! //! * [`Instance`] - an instantiated WebAssembly module. An instance is where -//! you can actually acquire a [`Func`] from, for example, to call. Each -//! [`Instance`], like all other [`Store`]-connected objects, cannot be sent -//! across threads. +//! you can actually acquire a [`Func`] from, for example, to call. +//! +//! * [`Func`] - a WebAssembly (or host) function. This can be acquired as the +//! export of an [`Instance`] to call WebAssembly functions, or it can be +//! created via functions like [`Func::wrap`] to wrap host-defined +//! functionality and give it to WebAssembly. +//! +//! * [`Table`], [`Global`], [`Memory`] - other WebAssembly objects which can +//! either be defined on the host or in wasm itself (via instances). These all +//! have various ways of being interacted with like [`Func`]. +//! +//! All "store-connected" types such as [`Func`], [`Memory`], etc, require the +//! store to be passed in as a context to each method. Methods in wasmtime +//! frequently have their first parameter as either [`impl +//! AsContext`][`AsContext`] or [`impl AsContextMut`][`AsContextMut`]. These +//! traits are implemented for a variety of types, allowing you to, for example, +//! pass the following types into methods: +//! +//! * `&Store` +//! * `&mut Store` +//! * `&Caller<'_, T>` +//! * `&mut Caller<'_, T>` +//! * `StoreContext<'_, T>` +//! * `StoreContextMut<'_, T>` +//! +//! A [`Store`] is the sole owner of all WebAssembly internals. Types like +//! [`Func`] point within the [`Store`] and require the [`Store`] to be provided +//! to actually access the internals of the WebAssembly function, for instance. +//! +//! ## Linking +//! +//! WebAssembly modules almost always require functionality from the host to +//! perform I/O-like tasks. They might refer to quite a few pieces of host +//! functionality, WASI, or maybe even a number of other wasm modules. To assist +//! with managing this a [`Linker`] type is provided to instantiate modules. +//! +//! A [`Linker`] performs name-based resolution of the imports of a WebAssembly +//! module so the [`Linker::instantiate`] method does not take an `imports` +//! argument like [`Instance::new`] does. Methods like [`Linker::define`] or +//! [`Linker::func_wrap`] can be used to define names within a [`Linker`] to +//! later be used for instantiation. +//! +//! For example we can reimplement the above example with a `Linker`: +//! +//! ``` +//! use anyhow::Result; +//! use wasmtime::*; +//! +//! fn main() -> Result<()> { +//! let engine = Engine::default(); +//! let wat = r#" +//! (module +//! (import "host" "hello" (func $host_hello (param i32))) +//! +//! (func (export "hello") +//! i32.const 3 +//! call $host_hello) +//! ) +//! "#; +//! let module = Module::new(&engine, wat)?; //! -//! There are other important types within the `wasmtime` crate but it's crucial -//! to be familiar with the above types! Be sure to browse the API documentation -//! to get a feeling for what other functionality is offered by this crate. +//! // Create a `Linker` and define our host function in it: +//! let mut linker = Linker::new(&engine); +//! linker.func_wrap("host", "hello", |caller: Caller<'_, u32>, param: i32| { +//! println!("Got {} from WebAssembly", param); +//! println!("my host state is: {}", caller.data()); +//! })?; +//! +//! // Use the `linker` to instantiate the module, which will automatically +//! // resolve the imports of the module using name-based resolution. +//! let mut store = Store::new(&engine, 0); +//! let instance = linker.instantiate(&mut store, &module)?; +//! let hello = instance.get_typed_func::<(), (), _>(&mut store, "hello")?; +//! hello.call(&mut store, ())?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! The [`Linker`] type also transparently handles Commands and Reactors +//! as defined by WASI. //! //! ## Example Architecture //! //! To better understand how Wasmtime types interact with each other let's walk //! through, at a high-level, an example of how you might use WebAssembly. In //! our use case let's say we have a web server where we'd like to run some -//! custom WebAssembly on each request. To ensure requests are isolated from -//! each other, though, we'll be creating a new [`Instance`] for each request. +//! custom WebAssembly on each request. To ensure requests are entirely isolated +//! from each other, though, we'll be creating a new [`Store`] for each +//! request. //! //! When the server starts, we'll start off by creating an [`Engine`] (and maybe //! tweaking [`Config`] settings if necessary). This [`Engine`] will be the only -//! engine for the lifetime of the server itself. -//! -//! Next, we can compile our WebAssembly. You'd create a [`Module`] through the -//! [`Module::new`] API. This will generate JIT code and perform expensive -//! compilation tasks up-front. +//! engine for the lifetime of the server itself. Next, we can compile our +//! WebAssembly. You'd create a [`Module`] through the [`Module::new`] API. +//! This will generate JIT code and perform expensive compilation tasks +//! up-front. Finally the last step of initialization would be to create a +//! [`Linker`] which will later be used to instantiate modules, adding +//! functionality like WASI to the linker too. //! //! After that setup, the server starts up as usual and is ready to receive //! requests. Upon receiving a request you'd then create a [`Store`] with //! [`Store::new`] referring to the original [`Engine`]. Using your [`Module`] -//! from before you'd then call [`Instance::new`] to instantiate our module for -//! the request. Both of these operations are designed to be as cheap as -//! possible. +//! and [`Linker`] from before you'd then call [`Linker::instantiate`] to +//! instantiate our module for the request. Both of these operations are +//! designed to be as cheap as possible. //! //! With an [`Instance`] you can then invoke various exports and interact with //! the WebAssembly module. Once the request is finished the [`Store`], -//! [`Instance`], and all other items loaded are dropped and everything will be -//! deallocated. Note that it's crucial to create a [`Store`]-per-request to -//! ensure that memory usage doesn't balloon accidentally by keeping a [`Store`] -//! alive indefinitely. -//! -//! ## Advanced Linking -//! -//! Often WebAssembly modules are not entirely self-isolated. They might refer -//! to quite a few pieces of host functionality, WASI, or maybe even a number of -//! other wasm modules. To help juggling all this together this crate provides a -//! [`Linker`] type which serves as an abstraction to assist in instantiating a -//! module. The [`Linker`] type also transparently handles Commands and Reactors -//! as defined by WASI. +//! is dropped and everything will be deallocated. Note that if the same +//! [`Store`] were used for every request then that would have all requests +//! sharing resources and nothing would ever get deallocated, causing memory +//! usage to baloon and would achive less isolation between requests. //! //! ## WASI //! //! The `wasmtime` crate does not natively provide support for WASI, but you can -//! use the `wasmtime-wasi` crate for that purpose. With `wasmtime-wasi` you can -//! create a "wasi instance" and then add all of its items into a [`Linker`], -//! which can then be used to instantiate a [`Module`] that uses WASI. +//! use the [`wasmtime-wasi`] crate for that purpose. With [`wasmtime-wasi`] all +//! WASI functions can be added to a [`Linker`] and then used to instantiate +//! WASI-using modules. For more information see the [WASI example in the +//! documentation](https://docs.wasmtime.dev/examples-rust-wasi.html). +//! +//! [`wasmtime-wasi`]: https://crates.io/crates/wasmtime-wasi +//! +//! ## Cross-store usage of items +//! +//! In `wasmtime` wasm items such as [`Global`] and [`Memory`] "belong" to a +//! [`Store`]. The store they belong to is the one they were created with +//! (passed in as a parameter) or instantiated with. This store is the only +//! store that can be used to interact with wasm items after they're created. +//! +//! The `wasmtime` crate will panic if the [`Store`] argument passed in to these +//! operations is incorrect. In other words it's considered a programmer error +//! rather than a recoverable error for the wrong [`Store`] to be used when +//! calling APIs. //! //! ## Crate Features //! @@ -192,22 +281,24 @@ //! ```no_run //! # use anyhow::Result; //! # use wasmtime::*; -//! use wasmtime_wasi::Wasi; -//! use wasi_cap_std_sync::WasiCtxBuilder; +//! use wasmtime_wasi::sync::WasiCtxBuilder; //! //! # fn main() -> Result<()> { -//! let store = Store::default(); -//! let mut linker = Linker::new(&store); -//! -//! // Create an instance of `Wasi` which contains a `WasiCtx`. Note that -//! // `WasiCtx` provides a number of ways to configure what the target program -//! // will have access to. -//! let wasi = Wasi::new(&store, WasiCtxBuilder::new().inherit_stdio().build()?); -//! wasi.add_to_linker(&mut linker)?; +//! // Compile our module and create a `Linker` which has WASI functions defined +//! // within it. +//! let engine = Engine::default(); +//! let module = Module::from_file(&engine, "foo.wasm")?; +//! let mut linker = Linker::new(&engine); +//! wasmtime_wasi::add_to_linker(&mut linker)?; +//! +//! // Configure and create a `WasiCtx`, which WASI functions need access to +//! // through the host state of the store (which in this case is the host state +//! // of the store) +//! let wasi_ctx = WasiCtxBuilder::new().inherit_stdio().build()?; +//! let mut store = Store::new(&engine, wasi_ctx); //! //! // Instantiate our module with the imports we've created, and run it. -//! let module = Module::from_file(store.engine(), "foo.wasm")?; -//! let instance = linker.instantiate(&module)?; +//! let instance = linker.instantiate(&mut store, &module)?; //! // ... //! //! # Ok(()) @@ -221,32 +312,29 @@ //! //! # use wasmtime::*; //! # fn main() -> anyhow::Result<()> { -//! let store = Store::default(); -//! let log_str = Func::wrap(&store, |caller: Caller<'_>, ptr: i32, len: i32| { +//! let mut store = Store::default(); +//! let log_str = Func::wrap(&mut store, |mut caller: Caller<'_, ()>, ptr: i32, len: i32| { +//! // Use our `caller` context to learn about the memory export of the +//! // module which called this host function. //! let mem = match caller.get_export("memory") { //! Some(Extern::Memory(mem)) => mem, //! _ => return Err(Trap::new("failed to find host memory")), //! }; //! -//! // We're reading raw wasm memory here so we need `unsafe`. Note -//! // though that this should be safe because we don't reenter wasm -//! // while we're reading wasm memory, nor should we clash with -//! // any other memory accessors (assuming they're well-behaved -//! // too). -//! unsafe { -//! let data = mem.data_unchecked() -//! .get(ptr as u32 as usize..) -//! .and_then(|arr| arr.get(..len as u32 as usize)); -//! let string = match data { -//! Some(data) => match str::from_utf8(data) { -//! Ok(s) => s, -//! Err(_) => return Err(Trap::new("invalid utf-8")), -//! }, -//! None => return Err(Trap::new("pointer/length out of bounds")), -//! }; -//! assert_eq!(string, "Hello, world!"); -//! println!("{}", string); -//! } +//! // Use the `ptr` and `len` values to get a subslice of the wasm-memory +//! // which we'll attempt to interpret as utf-8. +//! let data = mem.data(&caller) +//! .get(ptr as u32 as usize..) +//! .and_then(|arr| arr.get(..len as u32 as usize)); +//! let string = match data { +//! Some(data) => match str::from_utf8(data) { +//! Ok(s) => s, +//! Err(_) => return Err(Trap::new("invalid utf-8")), +//! }, +//! None => return Err(Trap::new("pointer/length out of bounds")), +//! }; +//! assert_eq!(string, "Hello, world!"); +//! println!("{}", string); //! Ok(()) //! }); //! let module = Module::new( @@ -262,16 +350,16 @@ //! (data (i32.const 4) "Hello, world!")) //! "#, //! )?; -//! let instance = Instance::new(&store, &module, &[log_str.into()])?; -//! let foo = instance.get_typed_func::<(), ()>("foo")?; -//! foo.call(())?; +//! let instance = Instance::new(&mut store, &module, &[log_str.into()])?; +//! let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?; +//! foo.call(&mut store, ())?; //! # Ok(()) //! # } //! ``` #![allow(unknown_lints)] -//#![deny(missing_docs, broken_intra_doc_links)] TODO -//#![doc(test(attr(deny(warnings))))] TODO +#![deny(missing_docs, broken_intra_doc_links)] +#![doc(test(attr(deny(warnings))))] #![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))] #![cfg_attr(nightlydoc, feature(doc_cfg))] #![cfg_attr(not(feature = "default"), allow(dead_code, unused_imports))] diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index 5089d85aaba8..7e595b08b45a 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -16,15 +16,16 @@ use std::sync::Arc; /// Structure used to link wasm modules/instances together. /// -/// This structure is used to assist in instantiating a [`Module`]. A `Linker` +/// This structure is used to assist in instantiating a [`Module`]. A [`Linker`] /// is a way of performing name resolution to make instantiating a module easier -/// (as opposed to calling [`Instance::new`]). `Linker` is a name-based resolver -/// where names are dynamically defined and then used to instantiate a -/// [`Module`]. The goal of a `Linker` is to have a one-argument method, -/// [`Linker::instantiate`], which takes a [`Module`] and produces an -/// [`Instance`]. This method will automatically select all the right imports -/// for the [`Module`] to be instantiated, and will otherwise return an error -/// if an import isn't satisfied. +/// than specifying positional imports to [`Instance::new`]. [`Linker`] is a +/// name-based resolver where names are dynamically defined and then used to +/// instantiate a [`Module`]. +/// +/// An important method is [`Linker::instantiate`] which takes a module to +/// instantiate into the provided store. This method will automatically select +/// all the right imports for the [`Module`] to be instantiated, and will +/// otherwise return an error if an import isn't satisfied. /// /// ## Name Resolution /// @@ -38,6 +39,38 @@ use std::sync::Arc; /// Names in a `Linker` cannot be defined twice, but allowing duplicates by /// shadowing the previous definition can be controlled with the /// [`Linker::allow_shadowing`] method. +/// +/// ## Commands and Reactors +/// +/// The [`Linker`] type provides conveniences for working with WASI Commands and +/// Reactors through the [`Linker::module`] method. This will automatically +/// handle instantiation and calling `_start` and such as appropriate +/// depending on the inferred type of module. +/// +/// ## Type parameter `T` +/// +/// It's worth pointing out that the type parameter `T` on [`Linker`] does +/// not represent that `T` is stored within a [`Linker`]. Rather the `T` is used +/// to ensure that linker-defined functions and stores instantiated into all use +/// the same matching `T` as host state. +/// +/// ## Multiple `Store`s +/// +/// The [`Linker`] type is designed to be compatible, in some scenarios, with +/// instantiation in multiple [`Store`]s. Specifically host-defined functions +/// created in [`Linker`] with [`Linker::func_new`], [`Linker::func_wrap`], and +/// their async versions are compatible to instantiate into any [`Store`]. This +/// enables programs which want to instantiate lots of modules to create one +/// [`Linker`] value at program start up and use that continuously for each +/// [`Store`] created over the lifetime of the program. +/// +/// Note that once [`Store`]-owned items, such as [`Global`], are defined witin +/// a [`Linker`] then it is no longer compatible with any [`Store`]. At that +/// point only the [`Store`] that owns the [`Global`] can be used to instantiate +/// modules. +/// +/// [`Store`]: crate::Store +/// [`Global`]: crate::Global pub struct Linker { engine: Engine, string2idx: HashMap, usize>, @@ -64,7 +97,10 @@ enum Definition { macro_rules! generate_wrap_async_func { ($num:tt $($args:ident)*) => (paste::paste!{ - /// TODO + /// Asynchronous analog of [`Linker::func_wrap`]. + /// + /// For more information also see + /// [`Func::wrapN_async`](crate::Func::wrap1_async). #[allow(non_snake_case)] #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] @@ -100,21 +136,6 @@ macro_rules! generate_wrap_async_func { impl Linker { /// Creates a new [`Linker`]. - /// - /// This function will create a new [`Linker`] which is ready to start - /// linking modules. All items defined in this linker and produced by this - /// linker will be connected with `store` and must come from the same - /// `store`. - /// - /// # Examples - /// - /// ``` - /// use wasmtime::{Linker, Store}; - /// - /// let store = Store::default(); - /// let mut linker = Linker::new(&store); - /// // ... - /// ``` pub fn new(engine: &Engine) -> Linker { Linker { engine: engine.clone(), @@ -127,7 +148,7 @@ impl Linker { } } - /// TODO + /// Returns the [`Engine`] this is connected to. pub fn engine(&self) -> &Engine { &self.engine } @@ -144,16 +165,16 @@ impl Linker { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let mut linker = Linker::new(&store); - /// linker.func("", "", || {})?; + /// # let engine = Engine::default(); + /// let mut linker = Linker::<()>::new(&engine); + /// linker.func_wrap("", "", || {})?; /// /// // by default, duplicates are disallowed - /// assert!(linker.func("", "", || {}).is_err()); + /// assert!(linker.func_wrap("", "", || {}).is_err()); /// /// // but shadowing can be configured to be allowed as well /// linker.allow_shadowing(true); - /// linker.func("", "", || {})?; + /// linker.func_wrap("", "", || {})?; /// # Ok(()) /// # } /// ``` @@ -175,11 +196,12 @@ impl Linker { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// # let module = Module::new(store.engine(), "(module)")?; - /// let mut linker = Linker::new(&store); + /// # let engine = Engine::default(); + /// # let module = Module::new(&engine, "(module)")?; + /// # let mut store = Store::new(&engine, ()); + /// let mut linker = Linker::new(&engine); /// linker.allow_unknown_exports(true); - /// linker.module("mod", &module)?; + /// linker.module(&mut store, "mod", &module)?; /// # Ok(()) /// # } /// ``` @@ -208,10 +230,11 @@ impl Linker { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let mut linker = Linker::new(&store); + /// # let engine = Engine::default(); + /// # let mut store = Store::new(&engine, ()); + /// let mut linker = Linker::new(&engine); /// let ty = GlobalType::new(ValType::I32, Mutability::Const); - /// let global = Global::new(&store, ty, Val::I32(0x1234))?; + /// let global = Global::new(&mut store, ty, Val::I32(0x1234))?; /// linker.define("host", "offset", global)?; /// /// let wat = r#" @@ -221,8 +244,8 @@ impl Linker { /// (data (global.get 0) "foo") /// ) /// "#; - /// let module = Module::new(store.engine(), wat)?; - /// linker.instantiate(&module)?; + /// let module = Module::new(&engine, wat)?; + /// linker.instantiate(&mut store, &module)?; /// # Ok(()) /// # } /// ``` @@ -249,6 +272,9 @@ impl Linker { Ok(self) } + /// Creates a [`Func::new`]-style function named in this linker. + /// + /// For more information see [`Linker::func_wrap`]. pub fn func_new( &mut self, module: &str, @@ -268,6 +294,9 @@ impl Linker { Ok(self) } + /// Creates a [`Func::new_async`]-style function named in this linker. + /// + /// For more information see [`Linker::func_wrap`]. #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] pub fn func_new_async( @@ -301,10 +330,25 @@ impl Linker { }) } - /// Convenience wrapper to define a function import. - /// - /// This method is a convenience wrapper around [`Linker::define`] which - /// internally delegates to [`Func::wrap`]. + /// Define a host function within this linker. + /// + /// For information about how the host function operates, see + /// [`Func::wrap`]. That includes information about translating Rust types + /// to WebAssembly native types. + /// + /// This method creates a host-provided function in this linker under the + /// provided name. This method is distinct in its capability to create a + /// [`Store`](crate::Store)-independent function. This means that the + /// function defined here can be used to instantiate instances in multiple + /// different stores, or in other words the function can be loaded into + /// different stores. + /// + /// Note that the capability mentioned here applies to all other + /// host-function-defining-methods on [`Linker`] as well. All of them can be + /// used to create instances of [`Func`] within multiple stores. In a + /// multithreaded program, for example, this means that the host functions + /// could be called concurrently if different stores are executing on + /// different threads. /// /// # Errors /// @@ -317,11 +361,11 @@ impl Linker { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let mut linker = Linker::new(&store); - /// linker.func("host", "double", |x: i32| x * 2)?; - /// linker.func("host", "log_i32", |x: i32| println!("{}", x))?; - /// linker.func("host", "log_str", |caller: Caller, ptr: i32, len: i32| { + /// # let engine = Engine::default(); + /// let mut linker = Linker::new(&engine); + /// linker.func_wrap("host", "double", |x: i32| x * 2)?; + /// linker.func_wrap("host", "log_i32", |x: i32| println!("{}", x))?; + /// linker.func_wrap("host", "log_str", |caller: Caller<'_, ()>, ptr: i32, len: i32| { /// // ... /// })?; /// @@ -332,8 +376,13 @@ impl Linker { /// (import "host" "log_str" (func (param i32 i32))) /// ) /// "#; - /// let module = Module::new(store.engine(), wat)?; - /// linker.instantiate(&module)?; + /// let module = Module::new(&engine, wat)?; + /// + /// // instantiate in multiple different stores + /// for _ in 0..10 { + /// let mut store = Store::new(&engine, ()); + /// linker.instantiate(&mut store, &module)?; + /// } /// # Ok(()) /// # } /// ``` @@ -372,23 +421,28 @@ impl Linker { /// [`Store`](crate::Store) than this [`Linker`] originally was created /// with. /// + /// # Panics + /// + /// Panics if `instance` does not belong to `store`. + /// /// # Examples /// /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let mut linker = Linker::new(&store); + /// # let engine = Engine::default(); + /// # let mut store = Store::new(&engine, ()); + /// let mut linker = Linker::new(&engine); /// /// // Instantiate a small instance... /// let wat = r#"(module (func (export "run") ))"#; - /// let module = Module::new(store.engine(), wat)?; - /// let instance = linker.instantiate(&module)?; + /// let module = Module::new(&engine, wat)?; + /// let instance = linker.instantiate(&mut store, &module)?; /// /// // ... and inform the linker that the name of this instance is /// // `instance1`. This defines the `instance1::run` name for our next /// // module to use. - /// linker.instance("instance1", &instance)?; + /// linker.instance(&mut store, "instance1", instance)?; /// /// let wat = r#" /// (module @@ -398,8 +452,8 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(store.engine(), wat)?; - /// let instance = linker.instantiate(&module)?; + /// let module = Module::new(&engine, wat)?; + /// let instance = linker.instantiate(&mut store, &module)?; /// # Ok(()) /// # } /// ``` @@ -444,20 +498,26 @@ impl Linker { /// [`Store`](crate::Store) than this [`Linker`] originally was created /// with, or if a Reactor initialization function traps. /// + /// # Panics + /// + /// Panics if any item used to instantiate the provided [`Module`] is not + /// owned by `store`. + /// /// # Examples /// /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let mut linker = Linker::new(&store); + /// # let engine = Engine::default(); + /// # let mut store = Store::new(&engine, ()); + /// let mut linker = Linker::new(&engine); /// /// // Instantiate a small instance and inform the linker that the name of /// // this instance is `instance1`. This defines the `instance1::run` name /// // for our next module to use. /// let wat = r#"(module (func (export "run") ))"#; - /// let module = Module::new(store.engine(), wat)?; - /// linker.module("instance1", &module)?; + /// let module = Module::new(&engine, wat)?; + /// linker.module(&mut store, "instance1", &module)?; /// /// let wat = r#" /// (module @@ -467,8 +527,8 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(store.engine(), wat)?; - /// let instance = linker.instantiate(&module)?; + /// let module = Module::new(&engine, wat)?; + /// let instance = linker.instantiate(&mut store, &module)?; /// # Ok(()) /// # } /// ``` @@ -478,8 +538,9 @@ impl Linker { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let mut linker = Linker::new(&store); + /// # let engine = Engine::default(); + /// # let mut store = Store::new(&engine, ()); + /// let mut linker = Linker::new(&engine); /// /// // Create a Command that attempts to count the number of times it is run, but is /// // foiled by each call getting a new instance. @@ -494,12 +555,14 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(store.engine(), wat)?; - /// linker.module("commander", &module)?; - /// let run = linker.get_default("")?.typed::<(), ()>()?.clone(); - /// run.call(())?; - /// run.call(())?; - /// run.call(())?; + /// let module = Module::new(&engine, wat)?; + /// linker.module(&mut store, "commander", &module)?; + /// let run = linker.get_default(&mut store, "")? + /// .typed::<(), (), _>(&store)? + /// .clone(); + /// run.call(&mut store, ())?; + /// run.call(&mut store, ())?; + /// run.call(&mut store, ())?; /// /// let wat = r#" /// (module @@ -513,10 +576,10 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(store.engine(), wat)?; - /// linker.module("", &module)?; - /// let run = linker.get_one_by_name("", Some("run"))?.into_func().unwrap(); - /// let count = run.typed::<(), i32>()?.call(())?; + /// let module = Module::new(&engine, wat)?; + /// linker.module(&mut store, "", &module)?; + /// let run = linker.get(&mut store, "", Some("run")).unwrap().into_func().unwrap(); + /// let count = run.typed::<(), i32, _>(&store)?.call(&mut store, ())?; /// assert_eq!(count, 0, "a Command should get a fresh instance on each invocation"); /// /// # Ok(()) @@ -713,22 +776,28 @@ impl Linker { /// instantiation itself may fail. For information on instantiation /// failures see [`Instance::new`]. /// + /// # Panics + /// + /// Panics if any item used to instantiate `module` is not owned by + /// `store`. + /// /// # Examples /// /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let mut linker = Linker::new(&store); - /// linker.func("host", "double", |x: i32| x * 2)?; + /// # let engine = Engine::default(); + /// # let mut store = Store::new(&engine, ()); + /// let mut linker = Linker::new(&engine); + /// linker.func_wrap("host", "double", |x: i32| x * 2)?; /// /// let wat = r#" /// (module /// (import "host" "double" (func (param i32) (result i32))) /// ) /// "#; - /// let module = Module::new(store.engine(), wat)?; - /// linker.instantiate(&module)?; + /// let module = Module::new(&engine, wat)?; + /// linker.instantiate(&mut store, &module)?; /// # Ok(()) /// # } /// ``` @@ -741,8 +810,8 @@ impl Linker { Instance::new(store, module, &imports) } - /// Attempts to instantiate the `module` provided. This is the same as [`Linker::instantiate`], - /// except for async `Store`s. + /// Attempts to instantiate the `module` provided. This is the same as + /// [`Linker::instantiate`], except for async `Store`s. #[cfg(feature = "async")] #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] pub async fn instantiate_async( @@ -765,7 +834,7 @@ impl Linker { module .imports() .map(|import| { - self.get(&mut store, &import) + self.get_by_import(&mut store, &import) .ok_or_else(|| self.link_error(&import)) }) .collect() @@ -779,7 +848,8 @@ impl Linker { anyhow!("unknown import: `{}` has not been defined", desc) } - /// Returns an iterator over all items defined in this `Linker`, in arbitrary order. + /// Returns an iterator over all items defined in this `Linker`, in + /// arbitrary order. /// /// The iterator returned will yield 3-tuples where the first two elements /// are the module name and item name for the external item, and the third @@ -800,149 +870,102 @@ impl Linker { }) } + /// Looks up a previously defined value in this [`Linker`], identified by + /// the names provided. + /// + /// Returns `None` if this name was not previously defined in this + /// [`Linker`]. + pub fn get( + &self, + store: impl AsContextMut, + module: &str, + name: Option<&str>, + ) -> Option { + let key = ImportKey { + module: *self.string2idx.get(module)?, + name: match name { + Some(name) => *self.string2idx.get(name)?, + None => usize::max_value(), + }, + }; + Some(self.map.get(&key)?.to_extern(store)) + } + /// Looks up a value in this `Linker` which matches the `import` type /// provided. /// /// Returns `None` if no match was found. - pub fn get( + pub fn get_by_import( &self, mut store: impl AsContextMut, import: &ImportType, ) -> Option { - if let Some(ext) = self.get_extern(&mut store, import) { - return Some(ext); + if let Some(item) = self.get(&mut store, import.module(), import.name()) { + return Some(item); } - match import.ty() { - ExternType::Instance(t) => { - // This is a key location where the module linking proposal is - // implemented. This logic allows single-level imports of an instance to - // get satisfied by multiple definitions of items within this `Linker`. - // - // The instance being import is iterated over to load the names from - // this `Linker` (recursively calling `get`). If anything isn't defined - // we return `None` since the entire value isn't defined. Otherwise when - // all values are loaded it's assembled into an `Instance` and - // returned`. - // - // Note that this isn't exactly the speediest implementation in the - // world. Ideally we would pre-create the `Instance` instead of creating - // it each time a module is instantiated. For now though while the - // module linking proposal is under development this should hopefully - // suffice. - if import.name().is_none() { - let mut builder = InstanceBuilder::new(); - for export in t.exports() { - let item = - self.get_extern(&mut store, &export.as_import(import.module()))?; - builder.insert(export.name(), item); - } - Some(builder.finish(&mut store.as_context_mut().opaque()).into()) - } else { - None - } - } - _ => None, + if import.name().is_some() { + return None; } - } - - /// Returns all items defined for the `module` and `name` pair. - /// - /// This may return an empty iterator, but it may also return multiple items - /// if the module/name have been defined twice. - pub fn get_by_name<'a: 'p, 'p>( - &'a self, - mut store: impl AsContextMut + 'p, - module: &'p str, - name: Option<&'p str>, - ) -> impl Iterator + 'p { - self.map - .iter() - .filter(move |(key, _item)| { - &*self.strings[key.module] == module - && self.strings.get(key.name).map(|s| &**s) == name - }) - .map(move |(_, item)| item.to_extern(&mut store)) - } - /// Returns the single item defined for the `module` and `name` pair. - /// - /// Unlike the similar [`Linker::get_by_name`] method this function returns - /// a single `Extern` item. If the `module` and `name` pair isn't defined - /// in this linker then an error is returned. If more than one value exists - /// for the `module` and `name` pairs, then an error is returned as well. - pub fn get_one_by_name( - &self, - store: impl AsContextMut, - module: &str, - name: Option<&str>, - ) -> Result { - let err_msg = || match name { - Some(name) => format!("named `{}` in `{}`", name, module), - None => format!("named `{}`", module), - }; - let mut items = self.get_by_name(store, module, name); - let ret = items - .next() - .ok_or_else(|| anyhow!("no item {}", err_msg()))?; - if items.next().is_some() { - bail!("too many items {}", err_msg()); + if let ExternType::Instance(t) = import.ty() { + // This is a key location where the module linking proposal is + // implemented. This logic allows single-level imports of an instance to + // get satisfied by multiple definitions of items within this `Linker`. + // + // The instance being import is iterated over to load the names from + // this `Linker` (recursively calling `get`). If anything isn't defined + // we return `None` since the entire value isn't defined. Otherwise when + // all values are loaded it's assembled into an `Instance` and + // returned`. + // + // Note that this isn't exactly the speediest implementation in the + // world. Ideally we would pre-create the `Instance` instead of creating + // it each time a module is instantiated. For now though while the + // module linking proposal is under development this should hopefully + // suffice. + let mut builder = InstanceBuilder::new(); + for export in t.exports() { + let item = self.get(&mut store, import.module(), Some(export.name()))?; + builder.insert(export.name(), item); + } + return Some(builder.finish(&mut store.as_context_mut().opaque()).into()); } - Ok(ret.clone()) + + None } /// Returns the "default export" of a module. /// /// An export with an empty string is considered to be a "default export". /// "_start" is also recognized for compatibility. + /// + /// # Panics + /// + /// Panics if the default function found is not owned by `store`. pub fn get_default( &self, mut store: impl AsContextMut, module: &str, ) -> Result { - let mut items = self.get_by_name(&mut store, module, Some("")); - if let Some(external) = items.next() { - if items.next().is_some() { - bail!("too many items named `` in `{}`", module); - } + if let Some(external) = self.get(&mut store, module, Some("")) { if let Extern::Func(func) = external { return Ok(func.clone()); } bail!("default export in '{}' is not a function", module); } - drop(items); // For compatibility, also recognize "_start". - let mut items = self.get_by_name(&mut store, module, Some("_start")); - if let Some(external) = items.next() { - if items.next().is_some() { - bail!("too many items named `_start` in `{}`", module); - } + if let Some(external) = self.get(&mut store, module, Some("_start")) { if let Extern::Func(func) = external { return Ok(func.clone()); } bail!("`_start` in '{}' is not a function", module); } - drop(items); // Otherwise return a no-op function. Ok(Func::wrap(store, || {})) } - - fn get_extern( - &self, - store: impl AsContextMut, - import: &ImportType, - ) -> Option { - let key = ImportKey { - module: *self.string2idx.get(import.module())?, - name: match import.name() { - Some(name) => *self.string2idx.get(name)?, - None => usize::max_value(), - }, - }; - Some(self.map.get(&key)?.to_extern(store)) - } } impl Default for Linker { diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/memory.rs index a9beb16923b3..7dca939737a8 100644 --- a/crates/wasmtime/src/memory.rs +++ b/crates/wasmtime/src/memory.rs @@ -26,120 +26,72 @@ impl std::error::Error for MemoryAccessError {} /// that is always a multiple of the WebAssembly page size, currently 64 /// kilobytes. /// -/// WebAssembly memory is used for global data, statics in C/C++/Rust, shadow -/// stack memory, etc. Accessing wasm memory is generally quite fast! +/// WebAssembly memory is used for global data (not to be confused with wasm +/// `global` items), statics in C/C++/Rust, shadow stack memory, etc. Accessing +/// wasm memory is generally quite fast. /// -/// # `Memory` and `Clone` -/// -/// Memories are internally reference counted so you can `clone` a `Memory`. The -/// cloning process only performs a shallow clone, so two cloned `Memory` -/// instances are equivalent in their functionality. -/// -/// # `Memory` and threads -/// -/// It is intended that `Memory` is safe to share between threads. At this time -/// this is not implemented in `wasmtime`, however. This is planned to be -/// implemented though! +/// Memories, like other wasm items, are owned by a [`Store`](crate::Store). /// /// # `Memory` and Safety /// -/// TODO +/// Linear memory is a lynchpin of safety for WebAssembly. In Wasmtime there are +/// safe methods of interacting with a [`Memory`]: /// -/// Linear memory is a lynchpin of safety for WebAssembly, but it turns out -/// there are very few ways to safely inspect the contents of a memory from the -/// host (Rust). This is because memory safety is quite tricky when working with -/// a `Memory` and we're still working out the best idioms to encapsulate -/// everything safely where it's efficient and ergonomic. This section of -/// documentation, however, is intended to help educate a bit what is and isn't -/// safe when working with `Memory`. +/// * [`Memory::read`] +/// * [`Memory::write`] +/// * [`Memory::data`] +/// * [`Memory::data_mut`] /// -/// For safety purposes you can think of a `Memory` as a glorified -/// `Rc>>`. There are a few consequences of this -/// interpretation: +/// Note that all of these consider the entire store context as borrowed for the +/// duration of the call or the duration of the returned slice. This largely +/// means that while the function is running you'll be unable to borrow anything +/// else from the store. This includes getting access to the `T` on +/// [`Store`](crate::Store), but it also means that you can't recursively +/// call into WebAssembly for instance. /// -/// * At any time someone else may have access to the memory (hence the `Rc`). -/// This could be a wasm instance, other host code, or a set of wasm instances -/// which all reference a `Memory`. When in doubt assume someone else has a -/// handle to your `Memory`. +/// If you'd like to dip your toes into handling [`Memory`] in a more raw +/// fashion (e.g. by using raw pointers or raw slices), then there's a few +/// important points to consider when doing so: /// -/// * At any time, memory can be read from or written to (hence the -/// `UnsafeCell`). Anyone with a handle to a wasm memory can read/write to it. -/// Primarily other instances can execute the `load` and `store` family of -/// instructions, as well as any other which modifies or reads memory. +/// * Any recursive calls into WebAssembly can possibly modify any byte of the +/// entire memory. This means that whenever wasm is called Rust can't have any +/// long-lived borrows live across the wasm function call. Slices like `&mut +/// [u8]` will be violated because they're not actually exclusive at that +/// point, and slices like `&[u8]` are also violated because their contents +/// may be mutated. /// -/// * At any time memory may grow (hence the `Vec<..>`). Growth may relocate the -/// base memory pointer (similar to how `vec.push(...)` can change the result -/// of `.as_ptr()`) +/// * WebAssembly memories can grow, and growth may change the base pointer. +/// This means that even holding a raw pointer to memory over a wasm function +/// call is also incorrect. Anywhere in the function call the base address of +/// memory may change. Note that growth can also be requested from the +/// embedding API as well. /// -/// So given that we're working roughly with `Rc>>` that's a -/// lot to keep in mind! It's hopefully though sort of setting the stage as to -/// what you can safely do with memories. +/// As a general rule of thumb it's recommended to stick to the safe methods of +/// [`Memory`] if you can. It's not advised to use raw pointers or `unsafe` +/// operations because of how easy it is to accidentally get things wrong. /// -/// Let's run through a few safe examples first of how you can use a `Memory`. +/// Some examples of safely interacting with memory are: /// /// ```rust -/// use wasmtime::{Memory, MemoryAccessError}; +/// use wasmtime::{Memory, Store, MemoryAccessError}; /// /// // Memory can be read and written safely with the `Memory::read` and /// // `Memory::write` methods. /// // An error is returned if the copy did not succeed. -/// fn safe_examples(mem: &Memory) -> Result<(), MemoryAccessError> { +/// fn safe_examples(mem: Memory, store: &mut Store<()>) -> Result<(), MemoryAccessError> { /// let offset = 5; -/// mem.write(offset, b"hello")?; +/// mem.write(&mut *store, offset, b"hello")?; /// let mut buffer = [0u8; 5]; -/// mem.read(offset, &mut buffer)?; +/// mem.read(&store, offset, &mut buffer)?; /// assert_eq!(b"hello", &buffer); -/// Ok(()) -/// } /// -/// // You can also get direct, unsafe access to the memory, but must manually -/// // ensure that safety invariants are upheld. -/// -/// fn correct_unsafe_examples(mem: &Memory) { -/// // Just like wasm, it's safe to read memory almost at any time. The -/// // gotcha here is that we need to be sure to load from the correct base -/// // pointer and perform the bounds check correctly. So long as this is -/// // all self contained here (e.g. not arbitrary code in the middle) we're -/// // good to go. -/// let byte = unsafe { mem.data_unchecked()[0x123] }; -/// -/// // Short-lived borrows of memory are safe, but they must be scoped and -/// // not have code which modifies/etc `Memory` while the borrow is active. -/// // For example if you want to read a string from memory it is safe to do -/// // so: -/// let string_base = 0xdead; -/// let string_len = 0xbeef; -/// let string = unsafe { -/// let bytes = &mem.data_unchecked()[string_base..][..string_len]; -/// match std::str::from_utf8(bytes) { -/// Ok(s) => s.to_string(), // copy out of wasm memory -/// Err(_) => panic!("not valid utf-8"), -/// } -/// }; -/// -/// // Additionally like wasm you can write to memory at any point in time, -/// // again making sure that after you get the unchecked slice you don't -/// // execute code which could read/write/modify `Memory`: -/// unsafe { -/// mem.data_unchecked_mut()[0x123] = 3; -/// } -/// -/// // When working with *borrows* that point directly into wasm memory you -/// // need to be extremely careful. Any functionality that operates on a -/// // borrow into wasm memory needs to be thoroughly audited to effectively -/// // not touch the `Memory` at all -/// let data_base = 0xfeed; -/// let data_len = 0xface; -/// unsafe { -/// let data = &mem.data_unchecked()[data_base..][..data_len]; -/// host_function_that_doesnt_touch_memory(data); -/// -/// // effectively the same rules apply to mutable borrows -/// let data_mut = &mut mem.data_unchecked_mut()[data_base..][..data_len]; -/// host_function_that_doesnt_touch_memory(data); -/// } +/// // Note that while this is safe care must be taken because the indexing +/// // here may panic if the memory isn't large enough. +/// assert_eq!(&mem.data(&store)[offset..offset + 5], b"hello"); +/// mem.data_mut(&mut *store)[offset..offset + 5].copy_from_slice(b"bye!!"); +/// +/// Ok(()) /// } -/// # fn host_function_that_doesnt_touch_memory(_: &[u8]){} /// ``` /// /// It's worth also, however, covering some examples of **incorrect**, @@ -147,54 +99,52 @@ impl std::error::Error for MemoryAccessError {} /// /// ```rust /// # use anyhow::Result; -/// use wasmtime::Memory; +/// use wasmtime::{Memory, Store}; /// /// // NOTE: All code in this function is not safe to execute and may cause /// // segfaults/undefined behavior at runtime. Do not copy/paste these examples /// // into production code! -/// unsafe fn unsafe_examples(mem: &Memory) -> Result<()> { +/// unsafe fn unsafe_examples(mem: Memory, store: &mut Store<()>) -> Result<()> { /// // First and foremost, any borrow can be invalidated at any time via the /// // `Memory::grow` function. This can relocate memory which causes any /// // previous pointer to be possibly invalid now. -/// let pointer: &u8 = &mem.data_unchecked()[0x100]; -/// mem.grow(1)?; // invalidates `pointer`! +/// let pointer: &u8 = &*mem.data_ptr(&store); +/// mem.grow(&mut *store, 1)?; // invalidates `pointer`! /// // println!("{}", *pointer); // FATAL: use-after-free /// /// // Note that the use-after-free also applies to slices, whether they're /// // slices of bytes or strings. -/// let slice: &[u8] = &mem.data_unchecked()[0x100..0x102]; -/// mem.grow(1)?; // invalidates `slice`! +/// let mem_slice = std::slice::from_raw_parts( +/// mem.data_ptr(&store), +/// mem.data_size(&store), +/// ); +/// let slice: &[u8] = &mem_slice[0x100..0x102]; +/// mem.grow(&mut *store, 1)?; // invalidates `slice`! /// // println!("{:?}", slice); // FATAL: use-after-free /// -/// // Due to the reference-counted nature of `Memory` note that literal -/// // calls to `Memory::grow` are not sufficient to audit for. You'll need -/// // to be careful that any mutation of `Memory` doesn't happen while -/// // you're holding an active borrow. -/// let slice: &[u8] = &mem.data_unchecked()[0x100..0x102]; -/// some_other_function(); // may invalidate `slice` through another `mem` reference -/// // println!("{:?}", slice); // FATAL: maybe a use-after-free +/// // The `Memory` type may be stored in other locations, so if you hand +/// // off access to the `Store` then those locations may also call +/// // `Memory::grow` or similar, so it's not enough to just audit code for +/// // calls to `Memory::grow`. +/// let pointer: &u8 = &*mem.data_ptr(&store); +/// some_other_function(store); // may invalidate `pointer` through use of `store` +/// // println!("{:?}", pointer); // FATAL: maybe a use-after-free /// /// // An especially subtle aspect of accessing a wasm instance's memory is /// // that you need to be extremely careful about aliasing. Anyone at any /// // time can call `data_unchecked()` or `data_unchecked_mut()`, which /// // means you can easily have aliasing mutable references: -/// let ref1: &u8 = &mem.data_unchecked()[0x100]; -/// let ref2: &mut u8 = &mut mem.data_unchecked_mut()[0x100]; +/// let ref1: &u8 = &*mem.data_ptr(&store).add(0x100); +/// let ref2: &mut u8 = &mut *mem.data_ptr(&store).add(0x100); /// // *ref2 = *ref1; // FATAL: violates Rust's aliasing rules /// -/// // Note that aliasing applies to strings as well, for example this is -/// // not valid because the slices overlap. -/// let slice1: &mut [u8] = &mut mem.data_unchecked_mut()[0x100..][..3]; -/// let slice2: &mut [u8] = &mut mem.data_unchecked_mut()[0x102..][..4]; -/// // println!("{:?} {:?}", slice1, slice2); // FATAL: aliasing mutable pointers -/// /// Ok(()) /// } -/// # fn some_other_function() {} +/// # fn some_other_function(store: &mut Store<()>) {} /// ``` /// -/// Overall there's some general rules of thumb when working with `Memory` and -/// getting raw pointers inside of it: +/// Overall there's some general rules of thumb when unsafely working with +/// `Memory` and getting raw pointers inside of it: /// /// * If you never have a "long lived" pointer into memory, you're likely in the /// clear. Care still needs to be taken in threaded scenarios or when/where @@ -202,29 +152,24 @@ impl std::error::Error for MemoryAccessError {} /// * Long-lived pointers must always respect Rust'a aliasing rules. It's ok for /// shared borrows to overlap with each other, but mutable borrows must /// overlap with nothing. -/// * Long-lived pointers are only valid if `Memory` isn't used in an unsafe way -/// while the pointer is valid. This includes both aliasing and growth. +/// * Long-lived pointers are only valid if they're not invalidated for their +/// lifetime. This means that [`Store`](crate::Store) isn't used to reenter +/// wasm or the memory itself is never grown or otherwise modified/aliased. /// -/// At this point it's worth reiterating again that working with `Memory` is -/// pretty tricky and that's not great! Proposals such as [interface types] are -/// intended to prevent wasm modules from even needing to import/export memory -/// in the first place, which obviates the need for all of these safety caveats! -/// Additionally over time we're still working out the best idioms to expose in -/// `wasmtime`, so if you've got ideas or questions please feel free to [open an -/// issue]! +/// At this point it's worth reiterating again that unsafely working with +/// `Memory` is pretty tricky and not recommended! It's highly recommended to +/// use the safe methods to interact with [`Memory`] whenever possible. /// /// ## `Memory` Safety and Threads /// -/// TODO -/// /// Currently the `wasmtime` crate does not implement the wasm threads proposal, -/// but it is planned to do so. It's additionally worthwhile discussing how this +/// but it is planned to do so. It may be interesting to readers to see how this /// affects memory safety and what was previously just discussed as well. /// /// Once threads are added into the mix, all of the above rules still apply. -/// There's an additional, rule, however, that all reads and writes can -/// happen *concurrently*. This effectively means that long-lived borrows into -/// wasm memory are virtually never safe to have. +/// There's an additional consideration that all reads and writes can happen +/// concurrently, though. This effectively means that any borrow into wasm +/// memory are virtually never safe to have. /// /// Mutable pointers are fundamentally unsafe to have in a concurrent scenario /// in the face of arbitrary wasm code. Only if you dynamically know for sure @@ -233,18 +178,19 @@ impl std::error::Error for MemoryAccessError {} /// underlying contents may change, so unless `UnsafeCell` in one form or /// another is used everywhere there's no safety. /// -/// One important point about concurrency is that `Memory::grow` can indeed -/// happen concurrently. This, however, will never relocate the base pointer. -/// Shared memories must always have a maximum size and they will be -/// preallocated such that growth will never relocate the base pointer. The -/// maximum length of the memory, however, will change over time. +/// One important point about concurrency is that while [`Memory::grow`] can +/// happen concurrently it will never relocate the base pointer. Shared +/// memories must always have a maximum size and they will be preallocated such +/// that growth will never relocate the base pointer. The current size of the +/// memory may still change over time though. /// /// Overall the general rule of thumb for shared memories is that you must /// atomically read and write everything. Nothing can be borrowed and everything -/// must be eagerly copied out. -/// -/// [interface types]: https://github.com/webassembly/interface-types -/// [open an issue]: https://github.com/bytecodealliance/wasmtime/issues/new +/// must be eagerly copied out. This means that [`Memory::data`] and +/// [`Memory::data_mut`] won't work in the future (they'll probably return an +/// error) for shared memories when they're implemented. When possible it's +/// recommended to use [`Memory::read`] and [`Memory::write`] which will still +/// be provided. #[derive(Copy, Clone, Debug)] #[repr(transparent)] // here for the C API pub struct Memory(Stored); @@ -252,9 +198,8 @@ pub struct Memory(Stored); impl Memory { /// Creates a new WebAssembly memory given the configuration of `ty`. /// - /// The `store` argument is a general location for cache information, and - /// otherwise the memory will immediately be allocated according to the - /// type's configuration. All WebAssembly memory is initialized to zero. + /// The `store` argument will be the owner of the returned [`Memory`]. All + /// WebAssembly memory is initialized to zero. /// /// # Examples /// @@ -262,13 +207,13 @@ impl Memory { /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { /// let engine = Engine::default(); - /// let store = Store::new(&engine); + /// let mut store = Store::new(&engine, ()); /// /// let memory_ty = MemoryType::new(Limits::new(1, None)); - /// let memory = Memory::new(&store, memory_ty)?; + /// let memory = Memory::new(&mut store, memory_ty)?; /// /// let module = Module::new(&engine, "(module (memory (import \"\" \"\") 1))")?; - /// let instance = Instance::new(&store, &module, &[memory.into()])?; + /// let instance = Instance::new(&mut store, &module, &[memory.into()])?; /// // ... /// # Ok(()) /// # } @@ -286,17 +231,21 @@ impl Memory { /// Returns the underlying type of this memory. /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. + /// /// # Examples /// /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { /// let engine = Engine::default(); - /// let store = Store::new(&engine); + /// let mut store = Store::new(&engine, ()); /// let module = Module::new(&engine, "(module (memory (export \"mem\") 1))")?; - /// let instance = Instance::new(&store, &module, &[])?; - /// let memory = instance.get_memory("mem").unwrap(); - /// let ty = memory.ty(); + /// let instance = Instance::new(&mut store, &module, &[])?; + /// let memory = instance.get_memory(&mut store, "mem").unwrap(); + /// let ty = memory.ty(&store); /// assert_eq!(ty.limits().min(), 1); /// # Ok(()) /// # } @@ -311,8 +260,12 @@ impl Memory { /// /// The entire buffer will be filled. /// - /// If offset + buffer length exceed the current memory capacity, then the + /// If `offset + buffer.len()` exceed the current memory capacity, then the /// buffer is left untouched and a [`MemoryAccessError`] is returned. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. pub fn read( &self, store: impl AsContext, @@ -331,9 +284,13 @@ impl Memory { /// Safely writes contents of a buffer to this memory at the given offset. /// - /// If the offset + buffer length exceed current memory capacity, then none - /// of the buffer is written to memory and a [`MemoryAccessError`] is + /// If the `offset + buffer.len()` exceeds the current memory capacity, then + /// none of the buffer is written to memory and a [`MemoryAccessError`] is /// returned. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. pub fn write( &self, mut store: impl AsContextMut, @@ -349,7 +306,14 @@ impl Memory { Ok(()) } - /// Returns this memory as a slice view that can be read natively in Rust. + /// Returns this memory as a native Rust slice. + /// + /// Note that this method will consider the entire store context provided as + /// borrowed for the duration of the lifetime of the returned slice. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. pub fn data<'a, T: 'a>(&self, store: impl Into>) -> &'a [u8] { unsafe { let store = store.into(); @@ -358,8 +322,14 @@ impl Memory { } } - /// Returns this memory as a slice view that can be read and written - /// natively in Rust. + /// Returns this memory as a native Rust mutable slice. + /// + /// Note that this method will consider the entire store context provided as + /// borrowed for the duration of the lifetime of the returned slice. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. pub fn data_mut<'a, T: 'a>(&self, store: impl Into>) -> &'a mut [u8] { unsafe { let store = store.into(); @@ -371,7 +341,12 @@ impl Memory { /// Returns the base pointer, in the host's address space, that the memory /// is located at. /// - /// TODO + /// For more information and examples see the documentation on the + /// [`Memory`] type. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. pub fn data_ptr(&self, store: impl AsContext) -> *mut u8 { unsafe { (*store.as_context()[self.0].definition).base } } @@ -382,11 +357,19 @@ impl Memory { /// /// For more information and examples see the documentation on the /// [`Memory`] type. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. pub fn data_size(&self, store: impl AsContext) -> usize { unsafe { (*store.as_context()[self.0].definition).current_length } } - /// Returns the size, in pages, of this wasm memory. + /// Returns the size, in WebAssembly pages, of this wasm memory. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. pub fn size(&self, store: impl AsContext) -> u32 { (self.data_size(store) / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32 } @@ -395,8 +378,8 @@ impl Memory { /// /// This will attempt to add `delta` more pages of memory on to the end of /// this `Memory` instance. If successful this may relocate the memory and - /// cause [`Memory::data_ptr`] to return a new value. Additionally previous - /// slices into this memory may no longer be valid. + /// cause [`Memory::data_ptr`] to return a new value. Additionally any + /// unsafetly constructed slices into this memory may no longer be valid. /// /// On success returns the number of pages this memory previously had /// before the growth succeeded. @@ -404,7 +387,13 @@ impl Memory { /// # Errors /// /// Returns an error if memory could not be grown, for example if it exceeds - /// the maximum limits of this memory. + /// the maximum limits of this memory. A + /// [`ResourceLimiter`](crate::ResourceLimiter) is another example of + /// preventing a memory to grow. + /// + /// # Panics + /// + /// Panics if this memory doesn't belong to `store`. /// /// # Examples /// @@ -412,17 +401,17 @@ impl Memory { /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { /// let engine = Engine::default(); - /// let store = Store::new(&engine); + /// let mut store = Store::new(&engine, ()); /// let module = Module::new(&engine, "(module (memory (export \"mem\") 1 2))")?; - /// let instance = Instance::new(&store, &module, &[])?; - /// let memory = instance.get_memory("mem").unwrap(); - /// - /// assert_eq!(memory.size(), 1); - /// assert_eq!(memory.grow(1)?, 1); - /// assert_eq!(memory.size(), 2); - /// assert!(memory.grow(1).is_err()); - /// assert_eq!(memory.size(), 2); - /// assert_eq!(memory.grow(0)?, 2); + /// let instance = Instance::new(&mut store, &module, &[])?; + /// let memory = instance.get_memory(&mut store, "mem").unwrap(); + /// + /// assert_eq!(memory.size(&store), 1); + /// assert_eq!(memory.grow(&mut store, 1)?, 1); + /// assert_eq!(memory.size(&store), 2); + /// assert!(memory.grow(&mut store, 1).is_err()); + /// assert_eq!(memory.size(&store), 2); + /// assert_eq!(memory.grow(&mut store, 0)?, 2); /// # Ok(()) /// # } /// ``` @@ -483,13 +472,14 @@ impl Memory { /// one can supply wasmtime with custom allocated host managed memory. /// /// # Safety +/// /// The memory should be page aligned and a multiple of page size. -/// To prevent possible silent overflows, the memory should be protected by a guard page. -/// Additionally the safety concerns explained in ['Memory'], for accessing the memory -/// apply here as well. +/// To prevent possible silent overflows, the memory should be protected by a +/// guard page. Additionally the safety concerns explained in ['Memory'], for +/// accessing the memory apply here as well. /// -/// Note that this is a relatively new and experimental feature and it is recommended -/// to be familiar with wasmtime runtime code to use it. +/// Note that this is a relatively new and experimental feature and it is +/// recommended to be familiar with wasmtime runtime code to use it. pub unsafe trait LinearMemory: Send + Sync + 'static { /// Returns the number of allocated wasm pages. fn size(&self) -> u32; @@ -512,13 +502,14 @@ pub unsafe trait LinearMemory: Send + Sync + 'static { /// to wasmtime which supplies host managed memory. /// /// # Safety -/// This trait is unsafe, as the memory safety depends on proper implementation of -/// memory management. Memories created by the MemoryCreator should always be treated -/// as owned by wasmtime instance, and any modification of them outside of wasmtime -/// invoked routines is unsafe and may lead to corruption. /// -/// Note that this is a relatively new and experimental feature and it is recommended -/// to be familiar with wasmtime runtime code to use it. +/// This trait is unsafe, as the memory safety depends on proper implementation +/// of memory management. Memories created by the MemoryCreator should always be +/// treated as owned by wasmtime instance, and any modification of them outside +/// of wasmtime invoked routines is unsafe and may lead to corruption. +/// +/// Note that this is a relatively new and experimental feature and it is +/// recommended to be familiar with wasmtime runtime code to use it. pub unsafe trait MemoryCreator: Send + Sync { /// Create a new `LinearMemory` object from the specified parameters. /// diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 555cd88b2905..a5a8b118b177 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -22,31 +22,50 @@ pub use self::context::*; mod data; pub use self::data::*; -/// A `Store` is a collection of WebAssembly instances and host-defined items. +/// A [`Store`] is a collection of WebAssembly instances and host-defined state. /// /// All WebAssembly instances and items will be attached to and refer to a -/// `Store`. For example instances, functions, globals, and tables are all -/// attached to a `Store`. Instances are created by instantiating a -/// [`Module`](crate::Module) within a `Store`. +/// [`Store`]. For example instances, functions, globals, and tables are all +/// attached to a [`Store`]. Instances are created by instantiating a +/// [`Module`](crate::Module) within a [`Store`]. /// -/// `Store` is not thread-safe and cannot be sent to other threads. All items -/// which refer to a `Store` additionally are not threadsafe and can only be -/// used on the original thread that they were created on. +/// A [`Store`] is intended to be a short-lived object in a program. No form +/// of GC is implemented at this time so once an instance is created within a +/// [`Store`] it will not be deallocated until the [`Store`] itself is dropped. +/// This makes [`Store`] unsuitable for creating an unbounded number of +/// instances in it because [`Store`] will never release this memory. It's +/// recommended to have a [`Store`] correspond roughly to the liftime of a "main +/// instance" that an embedding is interested in executing. /// -/// A `Store` is not intended to be a long-lived object in a program. No form of -/// GC is implemented at this time so once an instance is created within a -/// `Store` it will not be deallocated until all references to the `Store` have -/// gone away (this includes all references to items in the store). This makes -/// `Store` unsuitable for creating an unbounded number of instances in it -/// because `Store` will never release this memory. It's instead recommended to -/// have a long-lived [`Engine`] and instead create a `Store` for a more scoped -/// portion of your application. +/// ## Type parameter `T` /// -/// # Stores and `Clone` +/// Each [`Store`] has a type parameter `T` associated with it. This `T` +/// represents state defined by the host. This state will be accessible through +/// the [`Caller`](crate::Caller) type that host-defined functions get access +/// to. This `T` is suitable for storing `Store`-specific information which +/// imported functions may want access to. /// -/// Using `clone` on a `Store` is a cheap operation. It will not create an -/// entirely new store, but rather just a new reference to the existing object. -/// In other words it's a shallow copy, not a deep copy. +/// The data `T` can be accessed through methods like [`Store::data`] and +/// [`Store::data_mut`]. +/// +/// ## Stores, contexts, oh my +/// +/// Most methods in Wasmtime take something of the form +/// [`AsContext`](crate::AsContext) or [`AsContextMut`](crate::AsContextMut) as +/// the first argument. These two traits allow ergonomically passing in the +/// context you currently have to any method. The primary two sources of +/// contexts are: +/// +/// * `Store` +/// * `Caller<'_, T>` +/// +/// corresponding to what you create and what you have access to in a host +/// function. You can also explicitly acquire a [`StoreContext`] or +/// [`StoreContextMut`] and pass that around as well. +/// +/// Note that all methods on [`Store`] are mirrored onto [`StoreContext`], +/// [`StoreContextMut`], and [`Caller`]. This way no matter what form of context +/// you have you can call various methods, create objects, etc. /// /// ## Stores and `Default` /// @@ -144,17 +163,18 @@ impl Store { /// The created [`Store`] will place no additional limits on the size of /// linear memories or tables at runtime. Linear memories and tables will /// be allowed to grow to any upper limit specified in their definitions. - /// /// The store will limit the number of instances, linear memories, and - /// tables created to 10,000. - /// - /// Use [`Store::limiter`] with a - /// [`StoreLimitsBuilder`](crate::StoreLimitsBuilder) to specify different - /// limits for the store. - /// - /// TODO + /// tables created to 10,000. This can be overridden with the + /// [`Store::limiter`] configuration method. pub fn new(engine: &Engine, data: T) -> Self { let finished_functions = &Default::default(); + // Wasmtime uses the callee argument to host functions to learn about + // the original pointer to the `Store` itself, allowing it to + // reconstruct a `StoreContextMut`. When we initially call a `Func`, + // however, there's no "callee" to provide. To fix this we allocate a + // single "default callee" for the entire `Store`. This is then used as + // part of `Func::call` to guarantee that the `callee: *mut VMContext` + // is never null. let default_callee = unsafe { OnDemandInstanceAllocator::default() .allocate(InstanceAllocationRequest { @@ -192,6 +212,9 @@ impl Store { default_callee, data, }); + + // Once we've actually allocated the store itself we can configure the + // trait object pointer of the default callee. let store = StoreContextMut(&mut *inner).opaque().traitobj; unsafe { inner.default_callee.set_store(store); @@ -209,7 +232,12 @@ impl Store { self.inner.data_mut() } - /// TODO + /// Configures the [`ResourceLimiter`](crate::ResourceLimiter) used to limit + /// resource creation within this [`Store`]. + /// + /// Note that this limiter is only used to limit the creation/growth of + /// resources in the future, this does not retroactively attempt to apply + /// limits to the [`Store`]. pub fn limiter(&mut self, limiter: impl crate::ResourceLimiter) { self.inner.limiter = Some(Box::new(crate::limits::ResourceLimiterProxy(limiter))); } @@ -277,15 +305,15 @@ impl Store { /// // Enable interruptable code via `Config` and then create an interrupt /// // handle which we'll use later to interrupt running code. /// let engine = Engine::new(Config::new().interruptable(true))?; - /// let store = Store::new(&engine); + /// let mut store = Store::new(&engine, ()); /// let interrupt_handle = store.interrupt_handle()?; /// /// // Compile and instantiate a small example with an infinite loop. /// let module = Module::new(&engine, r#" /// (func (export "run") (loop br 0)) /// "#)?; - /// let instance = Instance::new(&store, &module, &[])?; - /// let run = instance.get_typed_func::<(), ()>("run")?; + /// let instance = Instance::new(&mut store, &module, &[])?; + /// let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?; /// /// // Spin up a thread to send us an interrupt in a second /// std::thread::spawn(move || { @@ -293,7 +321,7 @@ impl Store { /// interrupt_handle.interrupt(); /// }); /// - /// let trap = run.call(()).unwrap_err(); + /// let trap = run.call(&mut store, ()).unwrap_err(); /// assert!(trap.to_string().contains("wasm trap: interrupt")); /// # Ok(()) /// # } @@ -303,6 +331,10 @@ impl Store { } /// Perform garbage collection of `ExternRef`s. + /// + /// Note that it is not required to actively call this function. GC will + /// automatically happen when internal buffers fill up. This is provided if + /// fine-grained control over the GC is desired. pub fn gc(&mut self) { self.inner.gc() } diff --git a/crates/wasmtime/src/store/context.rs b/crates/wasmtime/src/store/context.rs index a8100251d488..7e89efd60c98 100644 --- a/crates/wasmtime/src/store/context.rs +++ b/crates/wasmtime/src/store/context.rs @@ -1,20 +1,37 @@ use crate::store::{Store, StoreInner}; use std::ops::{Deref, DerefMut}; -/// TODO +/// A temporary handle to a [`&Store`][`Store`]. +/// +/// This type is sutable for [`AsContext`] trait bounds on methods if desired. +/// For more information, see [`Store`]. // NB the repr(transparent) here is for the C API and it's important that the // representation of this `struct` is a pointer for now. If the representation // changes then the C API will need to be updated #[repr(transparent)] pub struct StoreContext<'a, T>(pub(super) &'a StoreInner); -/// TODO +/// A temporary handle to a [`&mut Store`][`Store`]. +/// +/// This type is sutable for [`AsContextMut`] or [`AsContext`] trait bounds on +/// methods if desired. For more information, see [`Store`]. // NB the repr(transparent) here is for the same reason as above. #[repr(transparent)] pub struct StoreContextMut<'a, T>(pub(super) &'a mut StoreInner); impl<'a, T> StoreContextMut<'a, T> { - // TODO + /// One of the unsafe lynchpins of Wasmtime. + /// + /// This method is called from one location, `Caller::with`, and is where we + /// load the raw unsafe trait object pointer from a `*mut VMContext` and + /// then cast it back to a `StoreContextMut`. This is naturally unsafe due + /// to the raw pointer usage, but it's also unsafe because `T` here needs to + /// line up with the `T` used to define the trait object itself. + /// + /// This should generally be achieved with various trait bounds throughout + /// Wasmtime that might give access to the `Caller<'_, T>` type. + /// Unfortunately there's not a ton of debug asserts we can add here, so we + /// rely on testing to largely help show that this is correctly used. pub(crate) unsafe fn from_raw( store: *mut dyn wasmtime_runtime::Store, ) -> StoreContextMut<'a, T> { @@ -33,6 +50,8 @@ impl<'a, T> StoreContextMut<'a, T> { } } + /// Same as `opaque`, but produces a value which implements the `Send` + /// trait. Useful for futures. pub(crate) fn opaque_send(mut self) -> StoreOpaqueSend<'a> where T: Send, @@ -53,18 +72,42 @@ impl<'a, T> StoreContextMut<'a, T> { } } -/// TODO +/// A trait used to get shared access to a [`Store`] in Wasmtime. +/// +/// This trait is used as a bound on the first argument of many methods within +/// Wasmtime. This trait is implemented for types like [`Store`], +/// [`Caller`](crate::Caller), and [`StoreContext`] itself. Implementors of this +/// trait provide access to a [`StoreContext`] via some means, allowing the +/// method in question to get access to the store's internal information. +/// +/// Note that this is only used in contexts where the store's information is +/// read, but not written. For example methods that return type information will +/// use this trait as a bound. More commonly, though, mutation is required and +/// [`AsContextMut`] is needed. pub trait AsContext { - /// TODO + /// The host information associated with the [`Store`], aka the `T` in + /// [`Store`]. type Data; - /// TODO + /// Returns the store context that this type provides access to. fn as_context(&self) -> StoreContext<'_, Self::Data>; } -/// TODO +/// A trait used to get exclusive mutable access to a [`Store`] in Wasmtime. +/// +/// This trait is used as a bound on the first argument of many methods within +/// Wasmtime. This trait is implemented for types like [`Store`], +/// [`Caller`](crate::Caller), and [`StoreContextMut`] itself. Implementors of +/// this trait provide access to a [`StoreContextMut`] via some means, allowing +/// the method in question to get access to the store's internal information. +/// +/// This is notably used for methods that may require some mutation of the +/// [`Store`] itself. For example calling a wasm function can mutate linear +/// memory or globals. Creation of a [`Func`] will update internal data +/// structures. This ends up being quite a common bound in Wasmtime, but +/// typically you can simply pass `&mut store` or `&mut caller` to satisfy it. pub trait AsContextMut: AsContext { - /// TODO + /// Returns the store context that this type provides access to. fn as_context_mut(&mut self) -> StoreContextMut<'_, Self::Data>; } @@ -202,6 +245,8 @@ pub struct StoreOpaqueSend<'a> { pub traitobj: *mut dyn wasmtime_runtime::Store, } +// This is needed due to the raw pointer `traitobj`, which is simply a pointer +// to `inner` which is already `Send`. unsafe impl Send for StoreOpaqueSend<'_> {} impl StoreOpaqueSend<'_> { diff --git a/crates/wasmtime/src/types.rs b/crates/wasmtime/src/types.rs index 0241aee28271..ff1366592959 100644 --- a/crates/wasmtime/src/types.rs +++ b/crates/wasmtime/src/types.rs @@ -655,17 +655,6 @@ impl<'module> ExportType<'module> { EntityOrExtern::Extern(e) => (*e).clone(), } } - - pub(crate) fn as_import<'a>(&self, module: &'a str) -> ImportType<'a> - where - 'module: 'a, - { - ImportType { - module, - name: Some(self.name), - ty: self.ty.clone(), - } - } } impl<'module> fmt::Debug for ExportType<'module> { diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index 3c5d6791a275..f12e7d41b4c8 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -73,7 +73,8 @@ impl WastContext { match module { Some(module) => self .linker - .get_one_by_name(&mut self.store, module, Some(name)), + .get(&mut self.store, module, Some(name)) + .ok_or_else(|| anyhow!("no item named `{}::{}` found", module, name)), None => self .current .as_ref() diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 2a74e30a96dc..32b71d3cd852 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -17,7 +17,6 @@ - [Linking Modules](./examples-rust-linking.md) - [Debugging](./examples-rust-debugging.md) - [Using Multi-Value](./examples-rust-multi-value.md) - - [Multi-threading](./examples-rust-multithreading.md) - [Embedding in C](./examples-c-embed.md) - [Hello, World!](./examples-c-hello-world.md) - [Calculating the GCD](./examples-c-gcd.md) diff --git a/docs/examples-rust-embed.md b/docs/examples-rust-embed.md index 576ea99ac495..14b4b1ebbe27 100644 --- a/docs/examples-rust-embed.md +++ b/docs/examples-rust-embed.md @@ -2,6 +2,5 @@ This section is intended to showcase the Rust embedding API for Wasmtime. This is done through the [`wasmtime` crate](https://crates.io/crates/wasmtime). In -addition to browsing the following examples you can also browse the [specific -section on Rust embedding](./embed-rust.md) or the [full API +addition to browsing the following examples you can also browse the [full API documentation](https://docs.rs/wasmtime). diff --git a/docs/examples-rust-hello-world.md b/docs/examples-rust-hello-world.md index ce8edd24af24..4ab24ceb3d0c 100644 --- a/docs/examples-rust-hello-world.md +++ b/docs/examples-rust-hello-world.md @@ -6,7 +6,10 @@ repository to run the example locally. [code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/hello.rs This example shows off how to instantiate a simple wasm module and interact with -it. +it. For more information about the types used here be sure to review the [core +concepts of the `wasmtime` +API](https://docs.rs/wasmtime/*/wasmtime/#core-concepts) as well as the general +[API documentation](https://docs.rs/wasmtime). ## `hello.wat` diff --git a/docs/examples-rust-linking.md b/docs/examples-rust-linking.md index 14c041ce75f0..e176aec4e1ca 100644 --- a/docs/examples-rust-linking.md +++ b/docs/examples-rust-linking.md @@ -6,7 +6,9 @@ repository to run the example locally. [code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/linking.rs This example shows off how to compile and instantiate modules which link -together. +together. Be sure to read the API documentation for [`Linker`] as well. + +[`Linker`]: https://docs.rs/wasmtime/0.26.0/wasmtime/struct.Linker.html ## `linking1.wat` diff --git a/docs/examples-rust-multithreading.md b/docs/examples-rust-multithreading.md deleted file mode 100644 index 5d3fdcac2c03..000000000000 --- a/docs/examples-rust-multithreading.md +++ /dev/null @@ -1,148 +0,0 @@ -# Multi-threading - -When using Rust you're effectively immune from a whole class of threading issues -such as data races due to the inherent checks in the compiler and traits like -`Send` and `Sync`. The `wasmtime` API, like other safe Rust APIs, is 100% safe -to use relative to threading if you never have any `unsafe` yourself. In -addition to all of this, however, it's important to be aware of the limitations -of `wasmtime` types and how this might affect your embedding use case. - -## Types that are `Send` and `Sync` - -Wasmtime has a number of types which implement both the `Send` and `Sync` -traits: - -* [`Config`](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html) -* [`Engine`](https://docs.wasmtime.dev/api/wasmtime/struct.Engine.html) -* [`Module`](https://docs.wasmtime.dev/api/wasmtime/struct.Module.html) -* [`Trap`](https://docs.wasmtime.dev/api/wasmtime/struct.Trap.html) -* [`InterruptHandle`](https://docs.wasmtime.dev/api/wasmtime/struct.InterruptHandle.html) -* Type-descriptions of items - * [`ValType`](https://docs.wasmtime.dev/api/wasmtime/struct.ValType.html) - * [`ExportType`](https://docs.wasmtime.dev/api/wasmtime/struct.ExportType.html) - * [`ExternType`](https://docs.wasmtime.dev/api/wasmtime/struct.ExternType.html) - * [`ImportType`](https://docs.wasmtime.dev/api/wasmtime/struct.ImportType.html) - * [`FuncType`](https://docs.wasmtime.dev/api/wasmtime/struct.FuncType.html) - * [`GlobalType`](https://docs.wasmtime.dev/api/wasmtime/struct.GlobalType.html) - * [`MemoryType`](https://docs.wasmtime.dev/api/wasmtime/struct.MemoryType.html) - * [`ModuleType`](https://docs.wasmtime.dev/api/wasmtime/struct.ModuleType.html) - * [`TableType`](https://docs.wasmtime.dev/api/wasmtime/struct.TableType.html) - * [`InstanceType`](https://docs.wasmtime.dev/api/wasmtime/struct.InstanceType.html) - -These types, as the traits imply, are safe to send and share across threads. -Note that the major types to call out here are `Module` and `Engine`. The -`Engine` is important because it enables sharing compilation configuration for -an entire application. Each `Engine` is intended to be long-lived for this -reason. - -Additionally `Module`, the compiled version of a WebAssembly module, is safe to -send and share across threads. This notably means that you can compile a module -once and then instantiate it on multiple threads simultaneously. There's no need -to recompile a module on each thread. - -## Types that are neither `Send` nor `Sync` - -Wasmtime also has a number of types which are thread-"unsafe". These types do -not have the `Send` or `Sync` traits implemented which means that you won't be -able to send them across threads by default. - -* [`Store`](https://docs.wasmtime.dev/api/wasmtime/struct.Store.html) -* [`Linker`](https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html) -* [`Instance`](https://docs.wasmtime.dev/api/wasmtime/struct.Instance.html) -* [`Extern`](https://docs.wasmtime.dev/api/wasmtime/struct.Extern.html) -* [`Func`](https://docs.wasmtime.dev/api/wasmtime/struct.Func.html) -* [`Global`](https://docs.wasmtime.dev/api/wasmtime/struct.Global.html) -* [`Table`](https://docs.wasmtime.dev/api/wasmtime/struct.Table.html) -* [`Memory`](https://docs.wasmtime.dev/api/wasmtime/struct.Memory.html) -* [`Val`](https://docs.wasmtime.dev/api/wasmtime/struct.Val.html) -* [`ExternRef`](https://docs.wasmtime.dev/api/wasmtime/struct.ExternRef.html) - -These types are all considered as "connected to a store", and everything -connected to a store is neither `Send` nor `Sync`. The Rust compiler will not -allow you to have values of these types cross thread boundaries or get shared -between multiple threads. Doing so would require some form of `unsafe` glue. - -It's important to note that the WebAssembly specification itself fundamentally -limits some of the concurrent possibilities here. For example it's not allowed -to concurrently call `global.set` or `table.set` on the same global/table. This -means that Wasmtime is designed to prevent at the very least concurrent usage of -these primitives. - -Apart from the WebAssembly specification, though, Wasmtime additionally has some -fundamental design decision which results in these types not implementing either -`Send` or `Sync`: - -* All objects are independently-owned `'static` values that internally retain - anything necessary to implement the API provided. This necessitates some form - of reference counting, and also requires the usage of non-atomic reference - counting. Once reference counting is used Rust only allows shared references - (`&T`) to the internals, and due to the wasm restriction of disallowing - concurrent usage non-atomic reference counting is used. - -* Insertion of user-defined objects into `Store` does not require all objects to - be either `Send` or `Sync`. For example `Func::wrap` will insert the - host-defined function into the `Store`, but there are no extra trait bounds on - this. Similar restrictions apply to `Store::set` as well. - -* The implementation of `ExternRef` allows arbitrary `'static` types `T` to get - wrapped up and is also implemented with non-atomic reference counting. - -Overall the design decisions of Wasmtime itself leads all of these types to not -implement either the `Send` or `Sync` traits. - -## Multithreading without `Send` - -Due to the lack of `Send` on types like `Store` and everything connected, it's -not always as trivial to add multithreaded execution of WebAssembly to an -embedding of Wasmtime as it is for other Rust code in general. The exact way -that multithreading could work for you depends on your specific embedding, but -some possibilities include: - -* If your workload involves instantiating a singular wasm module on a separate - thread, then it will need to live on that thread and communicate to other - threads via threadsafe means (e.g. channels, locks/queues, etc). - -* If you have something like a multithreaded web server, for example, then the - WebAssembly executed for each request will need to live within the thread that - the original `Store` was created on. This could be multithreaded, though, by - having a pool of threads executing WebAssembly. Each request would have a - scheduling decision of which pool to route to which would be up to the - application. In situations such as this it's recommended to [enable fuel - consumption](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.consume_fuel) - as well as [yielding when out of - fuel](https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#method.out_of_fuel_async_yield). - This will ensure that no one request entirely hogs a thread executing - WebAssembly and all requests scheduled onto that thread are able to execute. - It's also worth pointing out that the threads executing WebAssembly may or may - not be the same as the threads performing I/O for your server requests. - -* If absolutely required, Wasmtime is engineered such that it is dynamically safe - to move a `Store` as a whole to a separate thread. This option is not - recommended due to its complexity, but it is one that Wasmtime tests in CI and - considers supported. The principle here is that all objects connected to a - `Store` are safe to move to a separate thread *if and only if*: - - * All objects are moved all at once. For example you can't leave behind - references to a `Func` or perhaps a `Store` in TLS. - - * All host objects living inside of a store (e.g. those inserted via - `Store::set` or `Func::wrap`) implement the `Send` trait. - - If these requirements are met it is technically safe to move a store and its - objects between threads. The reason that this strategy isn't recommended, - however, is that you will receive no assistance from the Rust compiler in - verifying that the transfer across threads is indeed actually safe. This will - require auditing your embedding of Wasmtime itself to ensure it meets these - requirements. - - It's important to note that the requirements here also apply to the futures - returned from `Func::call_async`. These futures are not `Send` due to them - closing over `Store`-related values. In addition to the above requirements - though to safely send across threads embedders must *also* ensure that any - host futures returned from `Func::wrapN_async` are actually `Send` and safe to - send across threads. Again, though, there is no compiler assistance in doing - this. - -Overall the recommended story for multithreading with Wasmtime is "don't move a -`Store` between threads" and to architect your application around this -assumption. diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index acedf9b7b378..3267d357d129 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -5,7 +5,12 @@ repository to run the example locally. [code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/wasi/main.rs -This example shows off how to instantiate a wasm module using WASI imports. +This example shows how to use the [`wasmtime-wasi`] crate to define WASI +functions within a [`Linker`] which can then be used to instantiate a +WebAssembly module. + +[`wasmtime-wasi`]: https://crates.io/crates/wasmtime-wasi +[`Linker`]: https://docs.rs/wasmtime/*/wasmtime/struct.Linker.html ## Wasm Source code @@ -13,9 +18,64 @@ This example shows off how to instantiate a wasm module using WASI imports. {{#include ../examples/wasi/wasm/wasi.rs}} ``` - ## `wasi.rs` ```rust,ignore {{#include ../examples/wasi/main.rs}} ``` + +## WASI state with other custom host state + +The [`add_to_linker`] function requires that the host state of a [`Store`] +implements the [`BorrowMut`] trait. In the above example this is true +because the host state itself is [`WasiCtx`], but you may also have custom host +state you'd like to store adjacent to the [`WasiCtx`]. An example of doing this +looks like: + +[`add_to_linker`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/sync/fn.add_to_linker.html +[`Store`]: https://docs.rs/wasmtime/0.26.0/wasmtime/struct.Store.html +[`BorrowMut`]: https://doc.rust-lang.org/stable/std/borrow/trait.BorrowMut.html +[`WasiCtx`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/struct.WasiCtx.html + +```rust +use anyhow::Result; +use std::borrow::{Borrow, BorrowMut}; +use wasmtime::*; +use wasmtime_wasi::{WasiCtx, WasiCtxBuilder}; + +struct MyState { + message: String, + wasi: WasiCtx, +} + +impl Borrow for MyState { + fn borrow(&self) -> &WasiCtx { + &self.wasi + } +} + +impl BorrowMut for MyState { + fn borrow_mut(&mut self) -> &mut WasiCtx { + &mut self.wasi + } +} + +fn main() -> Result<()> { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + wasmtime_wasi::add_to_linker(&mut linker)?; + + let wasi = WasiCtxBuilder::new() + .inherit_stdio() + .inherit_args()? + .build()?; + let mut store = Store::new(&engine, MyState { + message: format!("hello!"), + wasi, + }); + + // ... + + Ok(()) +} +``` diff --git a/src/commands/run.rs b/src/commands/run.rs index 44754c98b54a..d195c19ad5e7 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -1,7 +1,7 @@ //! The module that implements the `wasmtime run` command. use crate::{CommonOptions, WasiModules}; -use anyhow::{bail, Context as _, Result}; +use anyhow::{anyhow, bail, Context as _, Result}; use std::borrow::{Borrow, BorrowMut}; use std::thread; use std::time::Duration; @@ -287,7 +287,8 @@ impl RunCommand { name: &str, ) -> Result<()> { let func = match linker - .get_one_by_name(&mut *store, "", Some(name))? + .get(&mut *store, "", Some(name)) + .ok_or_else(|| anyhow!("no export named `{}` found", name))? .into_func() { Some(func) => func, diff --git a/tests/all/async_functions.rs b/tests/all/async_functions.rs index c9a1a1deda0b..deea640d5fea 100644 --- a/tests/all/async_functions.rs +++ b/tests/all/async_functions.rs @@ -50,14 +50,16 @@ fn smoke_host_func() -> Result<()> { linker.func_wrap0_async("", "second", move |_caller| Box::new(async { Ok(()) }))?; let func = linker - .get_one_by_name(&mut store, "", Some("first"))? + .get(&mut store, "", Some("first")) + .unwrap() .into_func() .unwrap(); run_smoke_test(&mut store, func); run_smoke_typed_test(&mut store, func); let func = linker - .get_one_by_name(&mut store, "", Some("second"))? + .get(&mut store, "", Some("second")) + .unwrap() .into_func() .unwrap(); run_smoke_test(&mut store, func); @@ -117,14 +119,16 @@ fn smoke_host_func_with_suspension() -> Result<()> { })?; let func = linker - .get_one_by_name(&mut store, "", Some("first"))? + .get(&mut store, "", Some("first")) + .unwrap() .into_func() .unwrap(); run_smoke_test(&mut store, func); run_smoke_typed_test(&mut store, func); let func = linker - .get_one_by_name(&mut store, "", Some("second"))? + .get(&mut store, "", Some("second")) + .unwrap() .into_func() .unwrap(); run_smoke_test(&mut store, func); @@ -469,7 +473,8 @@ fn async_host_func_with_pooling_stacks() -> Result<()> { )?; let func = linker - .get_one_by_name(&mut store, "", Some(""))? + .get(&mut store, "", Some("")) + .unwrap() .into_func() .unwrap(); run_smoke_test(&mut store, func); diff --git a/tests/all/host_funcs.rs b/tests/all/host_funcs.rs index 49fbaa576e08..dd28e93734ca 100644 --- a/tests/all/host_funcs.rs +++ b/tests/all/host_funcs.rs @@ -104,7 +104,7 @@ fn drop_delayed() -> Result<()> { let module = Module::new(&engine, &wat::parse_str(r#"(import "" "" (func))"#)?)?; let mut store = Store::new(&engine, ()); - let func = linker.get_one_by_name(&mut store, "", Some(""))?; + let func = linker.get(&mut store, "", Some("")).unwrap(); Instance::new(&mut store, &module, &[func])?; drop(store); @@ -112,7 +112,7 @@ fn drop_delayed() -> Result<()> { assert_eq!(HITS.load(SeqCst), 0); let mut store = Store::new(&engine, ()); - let func = linker.get_one_by_name(&mut store, "", Some(""))?; + let func = linker.get(&mut store, "", Some("")).unwrap(); Instance::new(&mut store, &module, &[func])?; drop(store); @@ -147,42 +147,48 @@ fn signatures_match() -> Result<()> { let mut store = Store::new(&engine, ()); let f = linker - .get_one_by_name(&mut store, "", Some("f1"))? + .get(&mut store, "", Some("f1")) + .unwrap() .into_func() .unwrap(); assert_eq!(f.ty(&store).params().collect::>(), &[]); assert_eq!(f.ty(&store).results().collect::>(), &[]); let f = linker - .get_one_by_name(&mut store, "", Some("f2"))? + .get(&mut store, "", Some("f2")) + .unwrap() .into_func() .unwrap(); assert_eq!(f.ty(&store).params().collect::>(), &[]); assert_eq!(f.ty(&store).results().collect::>(), &[ValType::I32]); let f = linker - .get_one_by_name(&mut store, "", Some("f3"))? + .get(&mut store, "", Some("f3")) + .unwrap() .into_func() .unwrap(); assert_eq!(f.ty(&store).params().collect::>(), &[]); assert_eq!(f.ty(&store).results().collect::>(), &[ValType::I64]); let f = linker - .get_one_by_name(&mut store, "", Some("f4"))? + .get(&mut store, "", Some("f4")) + .unwrap() .into_func() .unwrap(); assert_eq!(f.ty(&store).params().collect::>(), &[]); assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F32]); let f = linker - .get_one_by_name(&mut store, "", Some("f5"))? + .get(&mut store, "", Some("f5")) + .unwrap() .into_func() .unwrap(); assert_eq!(f.ty(&store).params().collect::>(), &[]); assert_eq!(f.ty(&store).results().collect::>(), &[ValType::F64]); let f = linker - .get_one_by_name(&mut store, "", Some("f6"))? + .get(&mut store, "", Some("f6")) + .unwrap() .into_func() .unwrap(); assert_eq!( @@ -439,7 +445,8 @@ fn trap_smoke() -> Result<()> { let mut store = Store::new(&engine, ()); let f = linker - .get_one_by_name(&mut store, "", Some(""))? + .get(&mut store, "", Some("")) + .unwrap() .into_func() .unwrap(); @@ -492,7 +499,8 @@ fn new_from_signature() -> Result<()> { let mut store = Store::new(&engine, ()); let f = linker - .get_one_by_name(&mut store, "", Some("f1"))? + .get(&mut store, "", Some("f1")) + .unwrap() .into_func() .unwrap(); assert!(f.typed::<(), (), _>(&store).is_ok()); @@ -500,7 +508,8 @@ fn new_from_signature() -> Result<()> { assert!(f.typed::(&store).is_err()); let f = linker - .get_one_by_name(&mut store, "", Some("f2"))? + .get(&mut store, "", Some("f2")) + .unwrap() .into_func() .unwrap(); assert!(f.typed::<(), (), _>(&store).is_err()); @@ -534,7 +543,8 @@ fn call_wrapped_func() -> Result<()> { let mut store = Store::new(&engine, ()); let f = linker - .get_one_by_name(&mut store, "", Some("f1"))? + .get(&mut store, "", Some("f1")) + .unwrap() .into_func() .unwrap(); f.call( @@ -545,7 +555,8 @@ fn call_wrapped_func() -> Result<()> { .call(&mut store, (1, 2, 3.0, 4.0))?; let f = linker - .get_one_by_name(&mut store, "", Some("f2"))? + .get(&mut store, "", Some("f2")) + .unwrap() .into_func() .unwrap(); let results = f.call(&mut store, &[])?; @@ -554,7 +565,8 @@ fn call_wrapped_func() -> Result<()> { assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1); let f = linker - .get_one_by_name(&mut store, "", Some("f3"))? + .get(&mut store, "", Some("f3")) + .unwrap() .into_func() .unwrap(); let results = f.call(&mut store, &[])?; @@ -563,7 +575,8 @@ fn call_wrapped_func() -> Result<()> { assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2); let f = linker - .get_one_by_name(&mut store, "", Some("f4"))? + .get(&mut store, "", Some("f4")) + .unwrap() .into_func() .unwrap(); let results = f.call(&mut store, &[])?; @@ -572,7 +585,8 @@ fn call_wrapped_func() -> Result<()> { assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0); let f = linker - .get_one_by_name(&mut store, "", Some("f5"))? + .get(&mut store, "", Some("f5")) + .unwrap() .into_func() .unwrap(); let results = f.call(&mut store, &[])?; @@ -592,7 +606,8 @@ fn func_return_nothing() -> Result<()> { let mut store = Store::new(&engine, ()); let f = linker - .get_one_by_name(&mut store, "", Some(""))? + .get(&mut store, "", Some("")) + .unwrap() .into_func() .unwrap(); let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; @@ -639,7 +654,8 @@ fn call_via_funcref() -> Result<()> { let instance = Instance::new(&mut store, &module, &[])?; let f = linker - .get_one_by_name(&mut store, "", Some(""))? + .get(&mut store, "", Some("")) + .unwrap() .into_func() .unwrap(); let results = instance @@ -686,7 +702,8 @@ fn store_with_context() -> Result<()> { let mut store = Store::new(&engine, Ctx { called: false }); let f = linker - .get_one_by_name(&mut store, "", Some(""))? + .get(&mut store, "", Some("")) + .unwrap() .into_func() .unwrap(); f.call(&mut store, &[])?; diff --git a/tests/all/linker.rs b/tests/all/linker.rs index 128d0be7f821..e86d6ae109b8 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -258,7 +258,7 @@ fn get_host_function() -> Result<()> { linker.func_wrap("mod", "f1", || {})?; let mut store = Store::<()>::default(); assert!(linker - .get(&mut store, &module.imports().nth(0).unwrap()) + .get_by_import(&mut store, &module.imports().nth(0).unwrap()) .is_some()); Ok(()) @@ -284,7 +284,7 @@ fn funcs_live_on_to_fight_another_day() -> Result<()> { let get_and_call = || -> Result<()> { assert_eq!(flag.load(SeqCst), 0); let mut store = Store::new(&engine, ()); - let func = linker.get_one_by_name(&mut store, "", Some(""))?; + let func = linker.get(&mut store, "", Some("")).unwrap(); func.into_func().unwrap().call(&mut store, &[])?; assert_eq!(flag.load(SeqCst), 0); Ok(()) From 0b87eb096a1c3fd78ef5297be53258ea4ad6cbfb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 May 2021 08:31:46 -0700 Subject: [PATCH 39/90] Update instantiation benchmark --- benches/instantiation.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/benches/instantiation.rs b/benches/instantiation.rs index 67109d43a033..dba68ece8eea 100644 --- a/benches/instantiation.rs +++ b/benches/instantiation.rs @@ -3,21 +3,14 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use rayon::{prelude::*, ThreadPoolBuilder}; use std::{path::PathBuf, process::Command}; use wasmtime::*; -use wasmtime_wasi::{sync::WasiCtxBuilder, Wasi}; +use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx}; -fn instantiate(module: &Module) -> Result { - let store = Store::new(&module.engine()); +fn instantiate(linker: &Linker, module: &Module) -> Result<()> { + let wasi = WasiCtxBuilder::new().build()?; + let mut store = Store::new(module.engine(), wasi); + let _instance = linker.instantiate(&mut store, module)?; - // As we don't actually invoke Wasm code in this benchmark, we still add - // the WASI context to the store as it is considered part of getting a - // module that depends on WASI "ready to run". - Wasi::set_context(&store, WasiCtxBuilder::new().build()?) - .map_err(|_| anyhow::anyhow!("wasi set_context failed"))?; - - let linker = Linker::new(&store); - let instance = linker.instantiate(module)?; - - Ok(instance) + Ok(()) } fn benchmark_name<'a>(strategy: &InstanceAllocationStrategy) -> &'static str { @@ -46,15 +39,16 @@ fn bench_sequential(c: &mut Criterion, modules: &[&str]) { path.push(file_name); let mut config = Config::default(); - Wasi::add_to_config(&mut config); config.allocation_strategy(strategy.clone()); let engine = Engine::new(&config).expect("failed to create engine"); let module = Module::from_file(&engine, &path) .expect(&format!("failed to load benchmark `{}`", path.display())); + let mut linker = Linker::new(&engine); + wasmtime_wasi::add_to_linker(&mut linker).unwrap(); group.bench_function(BenchmarkId::new(benchmark_name(strategy), file_name), |b| { - b.iter(|| instantiate(&module).expect("failed to instantiate module")); + b.iter(|| instantiate(&linker, &module).expect("failed to instantiate module")); }); } } @@ -74,12 +68,13 @@ fn bench_parallel(c: &mut Criterion) { InstanceAllocationStrategy::pooling(), ] { let mut config = Config::default(); - Wasi::add_to_config(&mut config); config.allocation_strategy(strategy.clone()); let engine = Engine::new(&config).expect("failed to create engine"); let module = Module::from_file(&engine, "benches/instantiation/wasi.wasm") .expect("failed to load WASI example module"); + let mut linker = Linker::new(&engine); + wasmtime_wasi::add_to_linker(&mut linker).unwrap(); for threads in 1..=num_cpus::get_physical() { let pool = ThreadPoolBuilder::new() @@ -101,7 +96,8 @@ fn bench_parallel(c: &mut Criterion) { b.iter(|| { pool.install(|| { (0..PARALLEL_INSTANCES).into_par_iter().for_each(|_| { - instantiate(&module).expect("failed to instantiate module"); + instantiate(&linker, &module) + .expect("failed to instantiate module"); }) }) }); From d5b959c31b4dff59d2ac9222ed1e55c4a3f69117 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 May 2021 08:59:30 -0700 Subject: [PATCH 40/90] Comment why `Mutex` is used --- crates/wiggle/borrow/src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/wiggle/borrow/src/lib.rs b/crates/wiggle/borrow/src/lib.rs index 7abcf5abbe0f..629733a02cf9 100644 --- a/crates/wiggle/borrow/src/lib.rs +++ b/crates/wiggle/borrow/src/lib.rs @@ -5,7 +5,16 @@ use wiggle::{BorrowHandle, GuestError, Region}; pub struct BorrowChecker { /// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking /// overlap, the method calls on this member will be confusing. - // TODO + /// + /// Also, unfortunately, for now this uses a `Mutex`. The reason for that is + /// that this is shared as `&BorrowChecker` in a bunch of `GuestPtr` values. + /// Through this sharing we still want each `GuestPtr` to be `Send` and the + /// "naive" way to make `&T` `Send` with interior mutability is to use a + /// `Mutex`. Fixing this will likely require rethinking `GuestPtr` one way + /// or another. That needs to happen for other reasons as well (for example + /// to allow for wasm calls to happen while `GuestPtr` values are active), + /// so it's hoped that in a later rethinking of `GuestPtr` we can revisit + /// this and remove this `Mutex`. bc: Mutex, } From 7631646db7beb3d87fbc4bf8a619df0d32ce05e9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 May 2021 13:39:15 -0700 Subject: [PATCH 41/90] Document the C API --- crates/c-api/include/doc-wasm.h | 158 +++------------------- crates/c-api/include/wasmtime.h | 159 +++++++++++++++++++++++ crates/c-api/include/wasmtime/config.h | 2 +- crates/c-api/include/wasmtime/error.h | 9 +- crates/c-api/include/wasmtime/extern.h | 131 +++++++++++++------ crates/c-api/include/wasmtime/func.h | 99 +++++++++----- crates/c-api/include/wasmtime/global.h | 47 +++++-- crates/c-api/include/wasmtime/instance.h | 73 +++++++++-- crates/c-api/include/wasmtime/linker.h | 48 ++++--- crates/c-api/include/wasmtime/memory.h | 45 ++++++- crates/c-api/include/wasmtime/module.h | 5 +- crates/c-api/include/wasmtime/store.h | 82 ++++++++++-- crates/c-api/include/wasmtime/table.h | 71 ++++++---- crates/c-api/include/wasmtime/trap.h | 13 +- crates/c-api/include/wasmtime/val.h | 141 ++++++++++---------- crates/c-api/src/func.rs | 5 + crates/c-api/src/global.rs | 7 +- crates/c-api/src/linker.rs | 2 +- crates/c-api/src/memory.rs | 7 +- crates/c-api/src/table.rs | 7 +- 20 files changed, 725 insertions(+), 386 deletions(-) diff --git a/crates/c-api/include/doc-wasm.h b/crates/c-api/include/doc-wasm.h index 9bbf5c1026ed..d95a42aced5d 100644 --- a/crates/c-api/include/doc-wasm.h +++ b/crates/c-api/include/doc-wasm.h @@ -1,94 +1,7 @@ -/** - * \mainpage Wasmtime C API - * - * This documentation is an overview and API reference for the C API of - * Wasmtime. The C API is spread between three different header files: - * - * * \ref wasm.h - * * \ref wasi.h - * * \ref wasmtime.h - * - * The \ref wasm.h header file comes directly from the - * [WebAssembly/wasm-c-api](https://github.com/WebAssembly/wasm-c-api) - * repository. At this time the upstream header file does not have documentation - * so Wasmtime provides documentation here. It should be noted, however, that - * some semantics may be Wasmtime-specific and may not be portable to other - * engines. The Wasmtime project intends to assist in the development of the - * upstream C API. - * - * The \ref wasi.h and \ref wasmtime.h header files are specific to the Wasmtime - * project. It's hoped to standardize \ref wasi.h in the WASI standard - * eventually, but \ref wasmtime.h provides Wasmtime-specific extensions to the - * C API which are not intended to standardize. - * - * ## Installing the C API - * - * To install the C API from precompiled binaries you can download the - * appropriate binary from the [releases page of - * Wasmtime](https://github.com/bytecodealliance/wasmtime/releases). Artifacts - * for the C API all end in "-c-api" for the filename. - * - * Each archive contains an `include` directory with necessary headers, as well - * as a `lib` directory with both a static archive and a dynamic library of - * Wasmtime. You can link to either of them as you see fit. - * - * ## Linking against the C API - * - * You'll want to arrange the `include` directory of the C API to be in your - * compiler's header path (e.g. the `-I` flag). If you're compiling for Windows - * and you're using the static library then you'll also need to pass - * `-DWASM_API_EXTERN=` and `-DWASI_API_EXTERN=` to disable dllimport. - * - * Your final artifact can then be linked with `-lwasmtime`. If you're linking - * against the static library you may need to pass other system libraries - * depending on your platform: - * - * * Linux - `-lpthread -ldl -lm` - * * macOS - no extra flags needed - * * Windows - `ws2_32.lib advapi32.lib userenv.lib ntdll.lib shell32.lib ole32.lib` - * - * ## Building from Source - * - * The C API is located in the - * [`crates/c-api`](https://github.com/bytecodealliance/wasmtime/tree/main/crates/c-api) - * directory of the [Wasmtime - * repository](https://github.com/bytecodealliance/wasmtime). To build from - * source you'll need a Rust compiler and a checkout of the `wasmtime` project. - * Afterwards you can execute: - * - * ``` - * $ cargo build --release -p wasmtime-c-api - * ``` - * - * This will place the final artifacts in `target/release`, with names depending - * on what platform you're compiling for. - * - * ## Other resources - * - * Some other handy resources you might find useful when exploring the C API - * documentation are: - * - * * [Rust `wasmtime` crate - * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/) - - * although this documentation is for Rust and not C, you'll find that many - * functions mirror one another and there may be extra documentation in Rust - * you find helpful. If you find yourself having to frequently do this, - * though, please feel free to [file an - * issue](https://github.com/bytecodealliance/wasmtime/issues/new). - * - * * [C embedding - * examples](https://bytecodealliance.github.io/wasmtime/examples-c-embed.html) - * are available online and are tested from the Wasmtime repository itself. - * - * * [Contribution documentation for - * Wasmtime](https://bytecodealliance.github.io/wasmtime/contributing.html) in - * case you're interested in helping out! - */ - /** * \file wasm.h * - * Embedding API for WebAssembly. + * Upstream Embedding API for WebAssembly. * * This API is defined by the upstream wasm-c-api proposal at * https://github.com/WebAssembly/wasm-c-api. That proposal is in flux but @@ -131,51 +44,6 @@ * #wasm_name_t on the stack and pass it to #wasm_importtype_new. The memory * pointed to by #wasm_name_t must be properly initialized, however, and cannot * reside on the stack. - * - * ### Thread Safety - * - * All objects are not thread safe unless otherwise noted. This means that all - * objects, once created, cannot be used simultaneously on more than one thread. - * - * In general the split happens at the #wasm_store_t layer. Everything within a - * #wasm_store_t is pinned to that store, and that store is pinned to a thread. - * Objects like #wasm_config_t and #wasm_engine_t, however, are safe to share - * across threads. - * - * Note that if you move a #wasm_store_t between threads this is ok so long as - * you move *all* references within that store to the new thread. If any - * reference lingers on the previous thread then that is unsafe. - * - * ### Stores - * - * A foundational construct in this API is the #wasm_store_t. A store is a - * collection of host-provided objects and instantiated wasm modules. Stores are - * often treated as a "single unit" and items within a store are all allowed to - * reference one another. References across stores cannot currently be created. - * For example you cannot instantiate a module in one store with an instance - * from another store. - * - * Stores are entirely single-threaded. All objects referencing a #wasm_store_t - * are pinned to a single thread and must reside in the same thread as all other - * objects referencing the same store. - * - * A store is not intended to be a global long-lived object. Memory associated - * with a #wasm_instance_t is not deallocated until the entire #wasm_store_t has - * been deallocated. This means that if you instantiate a module and then delete - * the instance, the instance's backing memory isn't actually deleted (it's - * still referenced by the #wasm_store_t). This aspect makes a #wasm_store_t - * unsuitable for repeatedly instantiating modules over a long period of time - * because memory usage will continue to grow. - * - * If you're working with a web server, for example, then it's recommended to - * think of a store as a "one per request" sort of construct. Globally you'd - * have one #wasm_engine_t and a cache of #wasm_module_t instances compiled into - * that engine. Each request would create a new #wasm_store_t and then - * instantiate a #wasm_module_t into the store. This process of creating a store - * and instantiating a module is expected to be quite fast. When the request is - * finished you'd delete the #wasm_store_t and #wasm_instance_t (along with any - * other references), keeping memory usage reasonable for the lifetime of the - * server. */ /** @@ -204,7 +72,7 @@ * * This structure represents global configuration used when constructing a * #wasm_engine_t. There are now functions to modify this from wasm.h but the - * wasmtime.h header provides a number of Wasmtime-specific functions to + * wasmtime/config.h header provides a number of Wasmtime-specific functions to * tweak configuration options. * * This object is created with #wasm_config_new. @@ -233,17 +101,20 @@ * \brief Convenience alias for #wasm_engine_t * * \struct wasm_engine_t - * \brief Typically global object to create #wasm_store_t from. + * \brief Compilation environment and configuration. * * An engine is typically global in a program and contains all the configuration * necessary for compiling wasm code. From an engine you'll typically create a - * #wasm_store_t. Engines are created with #wasm_engine_new or + * #wasmtime_store_t. Engines are created with #wasm_engine_new or * #wasm_engine_new_with_config. * * An engine is safe to share between threads. Multiple stores can be created * within the same engine with each store living on a separate thread. Typically * you'll create one #wasm_engine_t for the lifetime of your program. * + * Engines are reference counted internally so #wasm_engine_delete can be called + * at any time after a #wasmtime_store_t has been created from one. + * * \fn wasm_engine_t *wasm_engine_new(void); * \brief Creates a new engine with the default configuration. * @@ -1224,9 +1095,7 @@ * See #wasm_byte_vec_delete for more information. * * \fn wasm_frame_t *wasm_frame_copy(const wasm_frame_t *) - * \brief Copies a #wasm_frame_t to a new one. - * - * The caller is responsible for deleting the returned #wasm_frame_t. + * \brief Unimplemented in Wasmtime. * * \fn wasm_instance_t *wasm_frame_instance(const wasm_frame_t *); * \brief Unimplemented in Wasmtime, aborts the process if called. @@ -1478,10 +1347,17 @@ * uninitialized when passed to this function. * * \fn void wasm_module_serialize(const wasm_module_t *, wasm_byte_vec_t *out); - * \brief Unimplemented in Wasmtime. + * \brief Serializes the provided module to a byte vector. + * + * Does not take ownership of the input module but expects the caller will + * deallocate the `out` vector. The byte vector can later be deserialized + * through #wasm_module_deserialize. * * \fn wasm_module_t *wasm_module_deserialize(wasm_store_t *, const wasm_byte_vec_t *); - * \brief Unimplemented in Wasmtime. + * \brief Deserializes a previously-serialized module. + * + * The input bytes must have been created from a previous call to + * #wasm_module_serialize. */ /** diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 61ec1b15452d..31753c533c5c 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -1,5 +1,164 @@ +/** + * \mainpage Wasmtime C API + * + * This documentation is an overview and API reference for the C API of + * Wasmtime. The C API is spread between three different header files: + * + * * \ref wasmtime.h + * * \ref wasi.h + * * \ref wasm.h + * + * The \ref wasmtime.h header file includes all the other header files and is + * the main header file you'll likely be using. The \ref wasm.h header file + * comes directly from the + * [WebAssembly/wasm-c-api](https://github.com/WebAssembly/wasm-c-api) + * repository, and at this time the upstream header file does not have + * documentation so Wasmtime provides documentation here. It should be noted + * some semantics may be Wasmtime-specific and may not be portable to other + * engines. + * + * ## Installing the C API + * + * To install the C API from precompiled binaries you can download the + * appropriate binary from the [releases page of + * Wasmtime](https://github.com/bytecodealliance/wasmtime/releases). Artifacts + * for the C API all end in "-c-api" for the filename. + * + * Each archive contains an `include` directory with necessary headers, as well + * as a `lib` directory with both a static archive and a dynamic library of + * Wasmtime. You can link to either of them as you see fit. + * + * ## Linking against the C API + * + * You'll want to arrange the `include` directory of the C API to be in your + * compiler's header path (e.g. the `-I` flag). If you're compiling for Windows + * and you're using the static library then you'll also need to pass + * `-DWASM_API_EXTERN=` and `-DWASI_API_EXTERN=` to disable dllimport. + * + * Your final artifact can then be linked with `-lwasmtime`. If you're linking + * against the static library you may need to pass other system libraries + * depending on your platform: + * + * * Linux - `-lpthread -ldl -lm` + * * macOS - no extra flags needed + * * Windows - `ws2_32.lib advapi32.lib userenv.lib ntdll.lib shell32.lib ole32.lib` + * + * ## Building from Source + * + * The C API is located in the + * [`crates/c-api`](https://github.com/bytecodealliance/wasmtime/tree/main/crates/c-api) + * directory of the [Wasmtime + * repository](https://github.com/bytecodealliance/wasmtime). To build from + * source you'll need a Rust compiler and a checkout of the `wasmtime` project. + * Afterwards you can execute: + * + * ``` + * $ cargo build --release -p wasmtime-c-api + * ``` + * + * This will place the final artifacts in `target/release`, with names depending + * on what platform you're compiling for. + * + * ## Other resources + * + * Some other handy resources you might find useful when exploring the C API + * documentation are: + * + * * [Rust `wasmtime` crate + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/) - + * although this documentation is for Rust and not C, you'll find that many + * functions mirror one another and there may be extra documentation in Rust + * you find helpful. If you find yourself having to frequently do this, + * though, please feel free to [file an + * issue](https://github.com/bytecodealliance/wasmtime/issues/new). + * + * * [C embedding + * examples](https://bytecodealliance.github.io/wasmtime/examples-c-embed.html) + * are available online and are tested from the Wasmtime repository itself. + * + * * [Contribution documentation for + * Wasmtime](https://bytecodealliance.github.io/wasmtime/contributing.html) in + * case you're interested in helping out! + */ + /** * \file wasmtime.h + * + * \brief Wasmtime's C API + * + * This file is the central inclusion point for Wasmtime's C API. There are a + * number of sub-header files but this file includes them all. The C API is + * based on \ref wasm.h but there are many Wasmtime-specific APIs which are + * tailored to Wasmtime's implementation. + * + * The #wasm_config_t and #wasm_engine_t types are used from \ref wasm.h. + * Additionally all type-level information (like #wasm_functype_t) is also + * used from \ref wasm.h. Otherwise, though, all wasm objects (like + * #wasmtime_store_t or #wasmtime_func_t) are used from this header file. + * + * ### Thread Safety + * + * The multithreading story of the C API very closely follows the + * multithreading story of the Rust API for Wasmtime. All objects are safe to + * send to other threads so long as user-specific data is also safe to send to + * other threads. Functions are safe to call from any thread but some functions + * cannot be called concurrently. For examples functions which correspond to + * `&T` in Rust can be called concurrently with any other methods that take + * `&T`. Functions that take `&mut T` in Rust, however, cannot be called + * concurrently with any other function (but can still be invoked on any + * thread). + * + * This generally equates to mutation of internal state. Functions which don't + * mutate anything, such as learning type information through + * #wasmtime_func_type, can be called concurrently. Functions which do require + * mutation, for example #wasmtime_func_call, cannot be called concurrently. + * This is conveyed in the C API with either `const wasmtime_context_t*` + * (concurrency is ok as it's read-only) or `wasmtime_context_t*` (concurrency + * is not ok, mutation may happen). + * + * When in doubt assume that functions cannot be called concurrently with + * aliasing objects. + * + * ### Aliasing + * + * The C API for Wasmtime is intended to be a relatively thin layer over the + * Rust API for Wasmtime. Rust has much more strict rules about aliasing than C + * does, and the Rust API for Wasmtime is designed around these rules to be + * used safely. These same rules must be upheld when using the C API of + * Wasmtime. + * + * The main consequence of this is that the #wasmtime_context_t pointer into + * the #wasmtime_store_t must be carefully used. Since the context is an + * internal pointer into the store it must be used carefully to ensure you're + * not doing something that Rust would otherwise forbid at compile time. A + * #wasmtime_context_t can only be used when you would otherwise have been + * provided access to it. For example in a host function created with + * #wasmtime_func_new you can use #wasmtime_context_t in the host function + * callback. This is because an argument, a #wasmtime_caller_t, provides access + * to #wasmtime_context_t. On the other hand a destructor passed to + * #wasmtime_externref_new, however, cannot use a #wasmtime_context_t because + * it was not provided access to one. Doing so may lead to memory unsafety. + * + * ### Stores + * + * A foundational construct in this API is the #wasmtime_store_t. A store is a + * collection of host-provided objects and instantiated wasm modules. Stores are + * often treated as a "single unit" and items within a store are all allowed to + * reference one another. References across stores cannot currently be created. + * For example you cannot pass a function from one store into another store. + * + * A store is not intended to be a global long-lived object. Stores provide no + * means of internal garbage collections of wasm objects (such as instances), + * meaning that no memory from a store will be deallocated until you call + * #wasmtime_store_delete. If you're working with a web server, for example, + * then it's recommended to think of a store as a "one per request" sort of + * construct. Globally you'd have one #wasm_engine_t and a cache of + * #wasmtime_module_t instances compiled into that engine. Each request would + * create a new #wasmtime_store_t and then instantiate a #wasmtime_module_t + * into the store. This process of creating a store and instantiating a module + * is expected to be quite fast. When the request is finished you'd delete the + * #wasmtime_store_t keeping memory usage reasonable for the lifetime of the + * server. */ #ifndef WASMTIME_API_H diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h index e23277b45768..6c9e051f9a1b 100644 --- a/crates/c-api/include/wasmtime/config.h +++ b/crates/c-api/include/wasmtime/config.h @@ -1,7 +1,7 @@ /** * \file wasmtime/config.h * - * TODO + * \brief Wasmtime-specific extensions to #wasm_config_t */ #ifndef WASMTIME_CONFIG_H diff --git a/crates/c-api/include/wasmtime/error.h b/crates/c-api/include/wasmtime/error.h index f42447a9bd04..2ffee72bed8f 100644 --- a/crates/c-api/include/wasmtime/error.h +++ b/crates/c-api/include/wasmtime/error.h @@ -1,7 +1,7 @@ /** * \file wasmtime/error.h * - * TODO + * \brief Definition and accessors of #wasmtime_error_t */ #ifndef WASMTIME_ERROR_H @@ -15,7 +15,7 @@ extern "C" { /** * \typedef wasmtime_error_t - * \brief Convenience alias for #wasmtime_error_t + * \brief Convenience alias for #wasmtime_error * * \struct wasmtime_error * \brief Errors generated by Wasmtime. @@ -23,7 +23,10 @@ extern "C" { * * This opaque type represents an error that happened as part of one of the * functions below. Errors primarily have an error message associated with them - * at this time, which you can acquire by calling TODO wasmtime_error_message. + * at this time, which you can acquire by calling #wasmtime_error_message. + * + * Errors are safe to share across threads and must be deleted with + * #wasmtime_error_delete. */ typedef struct wasmtime_error wasmtime_error_t; diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index 5ca2ed0abaf8..587f942dce2d 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -1,7 +1,7 @@ /** * \file wasmtime/extern.h * - * TODO + * \brief Definition of #wasmtime_extern_t and external items. */ #ifndef WASMTIME_EXTERN_H @@ -14,80 +14,129 @@ extern "C" { #endif -/// TODO +/// \brief Representation of a function in Wasmtime. +/// +/// Functions are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Functions cannot +/// interoperate between #wasmtime_store_t instances and if the wrong function +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. typedef uint64_t wasmtime_func_t; -/// TODO + +/// \brief Representation of a table in Wasmtime. +/// +/// Tables are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Tables cannot +/// interoperate between #wasmtime_store_t instances and if the wrong table +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. typedef uint64_t wasmtime_table_t; -/// TODO + +/// \brief Representation of a memory in Wasmtime. +/// +/// Memories are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Memories cannot +/// interoperate between #wasmtime_store_t instances and if the wrong memory +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. typedef uint64_t wasmtime_memory_t; -/// TODO + +/// \brief Representation of a instance in Wasmtime. +/// +/// Instances are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Instances cannot +/// interoperate between #wasmtime_store_t instances and if the wrong instance +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. typedef uint64_t wasmtime_instance_t; -/// TODO + +/// \brief Representation of a global in Wasmtime. +/// +/// Globals are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Globals cannot +/// interoperate between #wasmtime_store_t instances and if the wrong global +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. typedef uint64_t wasmtime_global_t; -/// TODO +/// \brief Disciminant of #wasmtime_extern_t typedef uint8_t wasmtime_extern_kind_t; -/// TODO + +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// function #define WASMTIME_EXTERN_FUNC 0 -/// TODO +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// global #define WASMTIME_EXTERN_GLOBAL 1 -/// TODO +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// table #define WASMTIME_EXTERN_TABLE 2 -/// TODO +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// memory #define WASMTIME_EXTERN_MEMORY 3 -/// TODO +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is +/// an instance #define WASMTIME_EXTERN_INSTANCE 4 -/// TODO +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is +/// a module #define WASMTIME_EXTERN_MODULE 5 /** - * TODO + * \typedef wasmtime_extern_union_t + * \brief Convenience alias for #wasmtime_extern_union + * + * \union wasmtime_extern_union + * \brief Container for different kinds of extern items. + * + * This type is contained in #wasmtime_extern_t and contains the payload for the + * various kinds of items an extern wasm item can be. */ typedef union wasmtime_extern_union { - /** - * TODO - */ + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_FUNC wasmtime_func_t func; - /** - * TODO - */ + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_GLOBAL wasmtime_global_t global; - /** - * TODO - */ + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_TABLE wasmtime_table_t table; - /** - * TODO - */ + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY wasmtime_memory_t memory; - /** - * TODO - */ + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_INSTANCE wasmtime_instance_t instance; - /** - * TODO - */ + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MODULE + /// + /// Note that this may be an owned pointer depending on the ownership of the + /// #wasmtime_extern_t container value. wasmtime_module_t *module; } wasmtime_extern_union_t; /** - * TODO + * \typedef wasmtime_extern_t + * \brief Convenience alias for #wasmtime_extern_t + * + * \union wasmtime_extern + * \brief Container for different kinds of extern items. + * + * Note that this structure may contain an owned value, namely + * #wasmtime_module_t, depending on the context in which this is used. APIs + * which consume a #wasmtime_extern_t do not take ownership, but APIs that + * return #wasmtime_extern_t require that #wasmtime_extern_delete is called to + * deallocate the value. */ typedef struct wasmtime_extern { - /** - * TODO - */ + /// Discriminant of which field of #of is valid. wasmtime_extern_kind_t kind; - /** - * TODO - */ + /// Container for the extern item's value. wasmtime_extern_union_t of; } wasmtime_extern_t; -/// TODO +/// \brief Deletes a #wasmtime_extern_t. void wasmtime_extern_delete(wasmtime_extern_t *val); -/// TODO +/// \brief Returns the type of the #wasmtime_extern_t defined within the given +/// store. +/// +/// Does not take ownership of `context` or `val`, but the returned +/// #wasm_externtype_t is an owned value that needs to be deleted. wasm_externtype_t *wasmtime_extern_type(wasmtime_context_t *context, wasmtime_extern_t *val); #ifdef __cplusplus diff --git a/crates/c-api/include/wasmtime/func.h b/crates/c-api/include/wasmtime/func.h index f77dcc6f26bd..150f458cc786 100644 --- a/crates/c-api/include/wasmtime/func.h +++ b/crates/c-api/include/wasmtime/func.h @@ -1,7 +1,7 @@ /** * \file wasmtime/func.h * - * TODO + * Wasmtime definitions of how to interact with host and wasm functions. */ #ifndef WASMTIME_FUNC_H @@ -22,25 +22,37 @@ extern "C" { * * \brief Structure used to learn about the caller of a host-defined function. * \struct wasmtime_caller - * \headerfile wasmtime/func.h * - * This structure is the first argument of #wasmtime_func_callback_t and - * wasmtime_func_callback_with_env_t. The main purpose of this structure is for - * building a WASI-like API which can inspect the memory of the caller, - * regardless of the caller. + * This structure is an argument to #wasmtime_func_callback_t. The purpose + * of this structure is acquire a #wasmtime_context_t pointer to interact with + * objects, but it can also be used for inspect the state of the caller (such as + * getting memories and functions) with #wasmtime_caller_export_get. * - * This is intended to be a temporary API extension until interface types have - * become more prevalent. This is not intended to be supported until the end of - * time, but it will be supported so long as WASI requires it. + * This object is never owned and does not need to be deleted. */ typedef struct wasmtime_caller wasmtime_caller_t; /** * \brief Callback signature for #wasmtime_func_new. * - * This function is the same as #wasm_func_callback_t except that its first - * argument is a #wasmtime_caller_t which allows learning information about the - * caller. + * This is the function signature for host functions that can be made accessible + * to WebAssembly. The arguments to this function are: + * + * \param env user-provided argument passed to #wasmtime_func_new + * \param caller a temporary object that can only be used during this function + * call. Used to acquire #wasmtime_context_t or caller's state + * \param args the arguments provided to this function invocation + * \param nargs how many arguments are provided + * \param results where to write the results of this function + * \param nresults how many results must be produced + * + * Callbacks are guaranteed to get called with the right types of arguments, but + * they must produce the correct number and types of results. Failure to do so + * will cause traps to get raised on the wasm side. + * + * This callback can optionally return a #wasm_trap_t indicating that a trap + * should be raised in WebAssembly. It's expected that in this case the caller + * relinquishes ownership of the trap and it is passed back to the engine. */ typedef wasm_trap_t* (*wasmtime_func_callback_t)( void *env, @@ -53,11 +65,17 @@ typedef wasm_trap_t* (*wasmtime_func_callback_t)( /** * \brief Creates a new host-defined function. * - * TODO + * Inserts a host-defined function into the `store` provided which can be used + * to then instantiate a module with or define within a #wasmtime_linker_t. * - * This function is the same as #wasm_func_new, except the callback has the type - * signature #wasmtime_func_callback_t which gives a #wasmtime_caller_t as its - * first argument. + * \param store the store in which to create the function + * \param type the wasm type of the function that's being created + * \param callback the host-defined callback to invoke + * \param env host-specific data passed to the callback invocation, can be + * `NULL` + * \param finalizer optional finalizer for `env`, can be `NULL` + * + * The returned function can only be used with the specified `store`. */ WASM_API_EXTERN wasmtime_func_t wasmtime_func_new( wasmtime_context_t *store, @@ -67,7 +85,11 @@ WASM_API_EXTERN wasmtime_func_t wasmtime_func_new( void (*finalizer)(void*) ); -/// TODO +/** + * \brief Returns the type of the function specified + * + * The returned #wasm_functype_t is owned by the caller. + */ WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( const wasmtime_context_t *store, wasmtime_func_t func @@ -76,52 +98,65 @@ WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( /** * \brief Call a WebAssembly function. * - * This function is similar to #wasm_func_call, but with a few tweaks: + * This function is used to invoke a function defined within a store. For + * example this might be used after extracting a function from a + * #wasmtime_instance_t. * - * * An error *and* a trap can be returned - * * Errors are returned if `args` have the wrong types, if the args/results - * arrays have the wrong lengths, or if values come from the wrong store. + * \param store the store which owns `func` + * \param func the function to call + * \param args the arguments to the function call + * \param nargs the number of arguments provided + * \param results where to write the results of the function call + * \param nresults the number of results expected + * \param trap where to store a trap, if one happens. * * There are three possible return states from this function: * * 1. The returned error is non-null. This means `results` * wasn't written to and `trap` will have `NULL` written to it. This state - * means that programmer error happened when calling the function (e.g. the - * size of the args/results were wrong) + * means that programmer error happened when calling the function, for + * example when the size of the arguments/results was wrong, the types of the + * arguments were wrong, or arguments may come from the wrong store. * 2. The trap pointer is filled in. This means the returned error is `NULL` and * `results` was not written to. This state means that the function was * executing but hit a wasm trap while executing. * 3. The error and trap returned are both `NULL` and `results` are written to. - * This means that the function call worked and the specified results were + * This means that the function call succeeded and the specified results were * produced. * * The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be * `NULL` if the corresponding length is zero. * - * Does not take ownership of `wasm_val_t` arguments. Gives ownership of - * `wasm_val_t` results. + * Does not take ownership of #wasmtime_val_t arguments. Gives ownership of + * #wasmtime_val_t results. */ WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call( wasmtime_context_t *store, wasmtime_func_t func, const wasmtime_val_t *args, size_t nargs, - wasmtime_val_t *results, + wasmtime_val_t *results, size_t nresults, wasm_trap_t **trap ); /** - * \brief Loads a #wasm_extern_t from the caller's context + * \brief Loads a #wasmtime_extern_t from the caller's context * * This function will attempt to look up the export named `name` on the caller - * instance provided. If it is found then the #wasm_extern_t for that is + * instance provided. If it is found then the #wasmtime_extern_t for that is * returned, otherwise `NULL` is returned. * * Note that this only works for exported memories right now for WASI * compatibility. * - * TODO + * \param caller the caller object to look up the export from + * \param name the name that's being looked up + * \param name_len the byte length of `name` + * \param item where to store the return value + * + * Returns a nonzero value if the export was found, or 0 if the export wasn't + * found. If the export wasn't found then `item` isn't written to. */ WASM_API_EXTERN bool wasmtime_caller_export_get( wasmtime_caller_t *caller, @@ -130,7 +165,9 @@ WASM_API_EXTERN bool wasmtime_caller_export_get( wasmtime_extern_t *item ); -/// TODO +/** + * \brief Returns the store context of the caller object. + */ WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* caller); #ifdef __cplusplus diff --git a/crates/c-api/include/wasmtime/global.h b/crates/c-api/include/wasmtime/global.h index 854a448c55c0..86631a1dbf7c 100644 --- a/crates/c-api/include/wasmtime/global.h +++ b/crates/c-api/include/wasmtime/global.h @@ -1,7 +1,7 @@ /** * \file wasmtime/global.h * - * TODO + * Wasmtime APIs for interacting with WebAssembly globals. */ #ifndef WASMTIME_GLOBAL_H @@ -19,15 +19,19 @@ extern "C" { /** * \brief Creates a new global value. * - * Similar to #wasm_global_new, but with a few tweaks: + * Creates a new host-defined global value within the provided `store` * - * * An error is returned instead of #wasm_global_t, which is taken as an - * out-parameter - * * An error happens when the `type` specified does not match the type of the - * value `val`, or if it comes from a different store than `store`. + * \param store the store in which to create the global + * \param type the wasm type of the global being created + * \param val the initial value of the global + * \param ret a return pointer for the created global. * - * This function does not take ownership of any of its arguments but returned - * values are owned by the caller. + * This function may return an error if the `val` argument does not match the + * specified type of the global, or if `val` comes from a different store than + * the one provided. + * + * This function does not take ownership of any of its arguments but error is + * owned by the caller. */ WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new( wasmtime_context_t *store, @@ -36,13 +40,26 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new( wasmtime_global_t *ret ); -/// TODO +/** + * \brief Returns the wasm type of the specified global. + * + * The returned #wasm_globaltype_t is owned by the caller. + */ WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( const wasmtime_context_t *store, wasmtime_global_t global ); -/// TODO +/** + * \brief Get the value of the specified global. + * + * \param store the store that owns `global` + * \param global the global to get + * \param out where to store the value in this global. + * + * This function returns ownership of the contents of `out`, so + * #wasmtime_val_delete may need to be called on the value. + */ WASM_API_EXTERN void wasmtime_global_get( wasmtime_context_t *store, wasmtime_global_t global, @@ -52,8 +69,14 @@ WASM_API_EXTERN void wasmtime_global_get( /** * \brief Sets a global to a new value. * - * This function is the same as #wasm_global_set, except in the case of an error - * a #wasmtime_error_t is returned. + * \param store the store that owns `global` + * \param global the global to set + * \param val the value to store in the global + * + * This function may return an error if `global` is not mutable or if `val` has + * the wrong type for `global`. + * + * THis does not take ownership of any argument but returns ownership of the error. */ WASM_API_EXTERN wasmtime_error_t *wasmtime_global_set( wasmtime_context_t *store, diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h index 4abad99c421b..123752ad254d 100644 --- a/crates/c-api/include/wasmtime/instance.h +++ b/crates/c-api/include/wasmtime/instance.h @@ -1,7 +1,7 @@ /** * \file wasmtime/instance.h * - * TODO + * Wasmtime APIs for interacting with wasm instances. */ #ifndef WASMTIME_INSTANCE_H @@ -21,7 +21,7 @@ extern "C" { */ typedef struct wasmtime_instancetype wasmtime_instancetype_t; -/// TODO +/// \brief Deletes an instance type WASM_API_EXTERN void wasmtime_instancetype_delete(wasmtime_instancetype_t *ty); /** @@ -51,12 +51,24 @@ WASM_API_EXTERN wasm_externtype_t* wasmtime_instancetype_as_externtype(wasmtime_ WASM_API_EXTERN wasmtime_instancetype_t* wasmtime_externtype_as_instancetype(wasm_externtype_t*); /** - * \brief Wasmtime-specific function to instantiate a module. + * \brief Instantiate a wasm module. * - * This function is similar to #wasm_instance_new, but with a few tweaks: + * This function will instantiate a WebAssembly module with the provided + * imports, creating a WebAssembly instance. The returned instance can then + * afterwards be inspected for exports. * - * * An error message can be returned from this function. - * * The `trap` pointer is required to not be NULL. + * \param store the store in which to create the instance + * \param module the module that's being instantiated + * \param imports the imports provided to the module + * \param nimports the size of `imports` + * \param instance where to store the returned instance + * \param trap where to store the returned trap + * + * This function requires that `imports` is the same size as the imports that + * `module` has. Additionally the `imports` array must be 1:1 lined up with the + * imports of the `module` specified. This is intended to be relatively low + * level, and #wasmtime_linker_instantiate is provided for a more ergonomic + * name-based resolution API. * * The states of return values from this function are similar to * #wasmtime_func_call where an error can be returned meaning something like a @@ -64,11 +76,11 @@ WASM_API_EXTERN wasmtime_instancetype_t* wasmtime_externtype_as_instancetype(was * instance is returned), or an instance can be returned (meaning no error or * trap is returned). * + * Note that this function requires that all `imports` specified must be owned + * by the `store` provided as well. + * * This function does not take ownership of any of its arguments, but all return * values are owned by the caller. - * - * See #wasm_instance_new for information about how to fill in the `imports` - * array. */ WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( wasmtime_context_t *store, @@ -79,22 +91,57 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( wasm_trap_t **trap ); -/// TODO +/** + * \brief Returns the type of the specified instance. + * + * The returned type is owned by the caller. + */ WASM_API_EXTERN wasmtime_instancetype_t *wasmtime_instance_type( const wasmtime_context_t *store, wasmtime_instance_t instance ); -/// TODO +/** + * \brief Get an export by name from an instance. + * + * \param store the store that owns `instance` + * \param instance the instance to lookup within + * \param name the export name to lookup + * \param name_len the byte length of `name` + * \param item where to store the returned value + * + * Returns nonzero if the export was found, and `item` is filled in. Otherwise + * returns 0. + * + * Doesn't take ownership of any arguments but does return ownership of the + * #wasmtime_extern_t. + */ WASM_API_EXTERN bool wasmtime_instance_export_get( wasmtime_context_t *store, wasmtime_instance_t instance, - char *name, + const char *name, size_t name_len, wasmtime_extern_t *item ); -/// TODO +/** + * \brief Get an export by index from an instance. + * + * \param store the store that owns `instance` + * \param instance the instance to lookup within + * \param index the index to lookup + * \param name where to store the name of the export + * \param name_len where to store the byte length of the name + * \param item where to store the export itself + * + * Returns nonzero if the export was found, and `name`, `name_len`, and `item` + * are filled in. Otherwise returns 0. + * + * Doesn't take ownership of any arguments but does return ownership of the + * #wasmtime_extern_t. The `name` pointer return value is owned by the `store` + * and must be immediately used before calling any other APIs on + * #wasmtime_context_t. + */ WASM_API_EXTERN bool wasmtime_instance_export_nth( wasmtime_context_t *store, wasmtime_instance_t instance, diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index 05cbb8a0a55e..b81e822a9bb3 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -1,7 +1,7 @@ /** * \file wasmtime/linker.h * - * TODO + * Wasmtime API for a name-based linker used to instantiate modules. */ #ifndef WASMTIME_LINKER_H @@ -17,13 +17,16 @@ extern "C" { #endif /** + * \typedef wasmtime_linker_t + * \brief Alias to #wasmtime_linker + * + * \struct #wasmtime_linker * \brief Object used to conveniently link together and instantiate wasm * modules. * * This type corresponds to the `wasmtime::Linker` type in Rust. This - * Wasmtime-specific extension is intended to make it easier to manage a set of - * modules that link together, or to make it easier to link WebAssembly modules - * to WASI. + * type is intended to make it easier to manage a set of modules that link + * together, or to make it easier to link WebAssembly modules to WASI. * * A #wasmtime_linker_t is a higher level way to instantiate a module than * #wasm_instance_new since it works at the "string" level of imports rather @@ -32,15 +35,16 @@ extern "C" { typedef struct wasmtime_linker wasmtime_linker_t; /** - * \brief Creates a new linker which will link together objects in the specified - * store. + * \brief Creates a new linker for the specified engine. * - * This function does not take ownership of the store argument, and the caller + * This function does not take ownership of the engine argument, and the caller * is expected to delete the returned linker. */ WASM_API_EXTERN wasmtime_linker_t* wasmtime_linker_new(wasm_engine_t* engine); -/// TODO +/** + * \brief Deletes a linker + */ WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker); /** @@ -77,13 +81,19 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( ); /** - * \brief Defines a WASI instance in this linker. + * \brief Defines WASI functions in this linker. * * \param linker the linker the name is being defined in. * * \return On success `NULL` is returned, otherwise an error is returned which * describes why the definition failed. * + * This function will provide WASI function names in the specified linker. Note + * that when an instance is created within a store then the store also needs to + * have its WASI settings configured with #wasmtime_context_set_wasi for WASI + * functions to work, otherwise an assert will be tripped that will abort the + * process. + * * For more information about name resolution consult the [Rust * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). */ @@ -95,7 +105,7 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( * \brief Defines an instance under the specified name in this linker. * * \param linker the linker the name is being defined in. - * \param store TODO + * \param store the store that owns `instance` * \param name the module name to define `instance` under. * \param name_len the byte length of `name` * \param instance a previously-created instance. @@ -122,7 +132,7 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance( * \brief Instantiates a #wasm_module_t with the items defined in this linker. * * \param linker the linker used to instantiate the provided module. - * \param store TODO + * \param store the store that is used to instantiate within * \param module the module that is being instantiated. * \param instance the returned instance, if successful. * \param trap a trap returned, if the start function traps. @@ -152,9 +162,9 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate( * \brief Defines automatic instantiations of a #wasm_module_t in this linker. * * \param linker the linker the module is being added to - * \param store TODO + * \param store the store that is used to instantiate `module` * \param name the name of the module within the linker - * \param name_len TODO + * \param name_len the byte length of `name` * \param module the module that's being instantiated * * \return An error if the module could not be instantiated or added or `NULL` @@ -179,9 +189,9 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module( * \brief Acquires the "default export" of the named module in this linker. * * \param linker the linker to load from - * \param store TODO + * \param store the store to load a function into * \param name the name of the module to get the default export for - * \param name_len TODO + * \param name_len the byte length of `name` * \param func where to store the extracted default function. * * \return An error is returned if the default export could not be found, or @@ -202,17 +212,17 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default( * \brief Loads an item by name from this linker. * * \param linker the linker to load from - * \param store TODO + * \param store the store to load the item into * \param module the name of the module to get - * \param module_len TODO + * \param module_len the byte length of `module` * \param name the name of the field to get - * \param name_len TODO + * \param name_len the byte length of `name` * \param item where to store the extracted item * * \return An error is returned if the item isn't defined or has more than one * definition, or `NULL` is returned and `item` is filled in otherwise. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_one_by_name( +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get( const wasmtime_linker_t *linker, wasmtime_context_t *store, const char *module, diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h index 6b18a37de7e8..83dd82e83c3e 100644 --- a/crates/c-api/include/wasmtime/memory.h +++ b/crates/c-api/include/wasmtime/memory.h @@ -1,7 +1,7 @@ /** * \file wasmtime/memory.h * - * TODO + * Wasmtime API for interacting with wasm memories. */ #ifndef WASMTIME_MEMORY_H @@ -16,35 +16,66 @@ extern "C" { #endif -/// TODO +/** + * \brief Creates a new WebAssembly linear memory + * + * \param store the store to create the memory within + * \param ty the type of the memory to create + * \param ret where to store the returned memory + * + * If an error happens when creating the memory it's returned and owned by the + * caller. If an error happens then `ret` is not filled in. + */ WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( wasmtime_context_t *store, const wasm_memorytype_t* ty, wasmtime_memory_t *ret ); -/// TODO +/** + * \brief Returns the tyep of the memory specified + */ WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type( const wasmtime_context_t *store, wasmtime_memory_t memory ); -/// TODO +/** + * \brief Returns the base pointer in memory where the linear memory starts. + */ WASM_API_EXTERN uint8_t *wasmtime_memory_data( const wasmtime_context_t *store, wasmtime_memory_t memory ); -/// TODO + +/** + * \brief Returns the byte length of this linear memory. + */ WASM_API_EXTERN size_t wasmtime_memory_data_size( const wasmtime_context_t *store, wasmtime_memory_t memory ); -/// TODO + +/** + * \brief Returns the length, in WebAssembly pages, of this linear memory + */ WASM_API_EXTERN uint32_t wasmtime_memory_size( const wasmtime_context_t *store, wasmtime_memory_t memory ); -/// TODO + +/** + * \brief Attempts to grow the specified memory by `delta` pages. + * + * \param store the store that owns `memory` + * \param memory the memory to grow + * \param delta the number of pages to grow by + * \param prev_size where to store the previous size of memory + * + * If memory cannot be grown then `prev_size` is left unchanged and an error is + * returned. Otherwise `prev_size` is set to the previous size of the memory, in + * WebAssembly pages, and `NULL` is returned. + */ WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow( wasmtime_context_t *store, wasmtime_memory_t memory, diff --git a/crates/c-api/include/wasmtime/module.h b/crates/c-api/include/wasmtime/module.h index c134883eb000..c5e6e1a5de2e 100644 --- a/crates/c-api/include/wasmtime/module.h +++ b/crates/c-api/include/wasmtime/module.h @@ -1,7 +1,7 @@ /** * \file wasmtime/module.h * - * TODO + * APIs for interacting with modules in Wasmtime */ #ifndef WASMTIME_MODULE_H @@ -99,7 +99,8 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m); /** - * TODO + * \brief Creates a shallow clone of the specified module, increasing the + * internal reference count. */ WASM_API_EXTERN wasmtime_module_t *wasmtime_module_clone(wasmtime_module_t *m); diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h index 58833c1a5c13..e4e76173eedf 100644 --- a/crates/c-api/include/wasmtime/store.h +++ b/crates/c-api/include/wasmtime/store.h @@ -1,7 +1,7 @@ /** * \file wasmtime/store.h * - * TODO + * Wasmtime definition of a "store". */ #ifndef WASMTIME_STORE_H @@ -20,23 +20,63 @@ extern "C" { * \brief Convenience alias for #wasmtime_store_t * * \struct wasmtime_store - * \brief TODO - * - * TODO + * \brief Storage of WebAssembly objects + * + * A store is the unit of isolation between WebAssembly instances in an + * embedding of Wasmtime. Values in one #wasmtime_store_t cannot flow into + * another #wasmtime_store_t. Stores are cheap to create and cheap to dispose. + * It's expected that one-off stores are common in embeddings. + * + * Objects stored within a #wasmtime_store_t are referenced with integer handles + * rather than interior pointers. This means that most APIs require that the + * store be explicitly passed in, which is done via #wasmtime_context_t. It is + * safe to move a #wasmtime_store_t to any thread at any time. A store generally + * cannot be concurrently used, however. */ typedef struct wasmtime_store wasmtime_store_t; -/// TODO +/** + * \typedef wasmtime_context_t + * \brief Convenience alias for #wasmtime_context + * + * \struct wasmtime_context + * \brief An interior pointer into a #wasmtime_store_t which is used as + * "context" for many functions. + * + * This context pointer is used pervasively throught Wasmtime's API. This can be + * acquired from #wasmtime_store_context or #wasmtime_caller_context. The + * context pointer for a store is the same for the entire lifetime of a store, + * so it can safely be stored adjacent to a #wasmtime_store_t itself. + * + * Usage of a #wasmtime_context_t must not outlive the original + * #wasmtime_store_t. Additionally #wasmtime_context_t can only be used in + * situations where it has explicitly been granted access to doing so. For + * example finalizers cannot use #wasmtime_context_t because they are not given + * access to it. + */ typedef struct wasmtime_context wasmtime_context_t; -/// TODO +/** + * \brief Creates a new store within the specified engine. + * + * \param engine the compilation environment with configuration this store is + * connected to + * \param data user-provided data to store, can later be acquired with + * #wasmtime_context_get_data. + * \param finalizer an optional finalizer for `data` + * + * This function creates a fresh store with the provided configuration settings. + * The returned store must be deleted with #wasmtime_store_delete. + */ WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new( wasm_engine_t *engine, void *data, - void(*finalizer)(void*) + void (*finalizer)(void*) ); -/// TODO +/** + * \brief Returns the interior #wasmtime_context_t pointer to this store + */ WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *store); /** @@ -44,10 +84,18 @@ WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *sto */ WASM_API_EXTERN void wasmtime_store_delete(wasmtime_store_t *store); -/// TODO +/** + * \brief Returns the user-specified data associated with the specified store + */ WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* context); -/// TODO +/** + * \brief Overwrites the user-specified data associated with this store. + * + * Note that this does not execute the original finalizer for the provided data, + * and the original finalizer will be executed for the provided data when the + * store is deleted. + */ WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void *data); /** @@ -57,7 +105,7 @@ WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void * `externref`s that are discovered to be unreachable by other code or objects * will have their finalizers run. * - * The `store` argument must not be NULL. + * The `context` argument must not be NULL. */ WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context); @@ -91,7 +139,17 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t * */ WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel); -/// TODO +/** + * \brief Configres WASI state within the specified store. + * + * This function is required if #wasmtime_linker_define_wasi is called. This + * will configure the WASI state for instances defined within this store to the + * configuration specified. + * + * This function does not take ownership of `context` but it does take ownership + * of `wasi`. The caller should no longer use `wasi` after calling this function + * (even if an error is returned). + */ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); /** diff --git a/crates/c-api/include/wasmtime/table.h b/crates/c-api/include/wasmtime/table.h index 129bcd8f3e9c..84a99e675829 100644 --- a/crates/c-api/include/wasmtime/table.h +++ b/crates/c-api/include/wasmtime/table.h @@ -1,7 +1,7 @@ /** * \file wasmtime/table.h * - * TODO + * Wasmtime APIs for interacting with WebAssembly tables. */ #ifndef WASMTIME_TABLE_H @@ -20,21 +20,27 @@ extern "C" { /** * \brief Creates a new host-defined wasm table. * - * This function is the same as #wasm_table_new except that it's specialized for - * funcref tables by taking a `wasm_func_t` initialization value. Additionally - * it returns errors via #wasmtime_error_t. + * \param store the store to create the table within + * \param ty the type of the table to create + * \param init the initial value for this table's elements + * \param table where to store the returned table * * This function does not take ownership of any of its parameters, but yields - * ownership of returned values (the table and error). + * ownership of returned error. This function may return an error if the `init` + * value does not match `ty`, for example. */ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new( wasmtime_context_t *store, - const wasm_tabletype_t *element_ty, + const wasm_tabletype_t *ty, wasmtime_val_t *init, wasmtime_table_t *table ); -/// TODO +/** + * \brief Returns the type of this table. + * + * The caller has ownership of the returned #wasm_tabletype_t + */ WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( const wasmtime_context_t *store, wasmtime_table_t table @@ -43,12 +49,14 @@ WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( /** * \brief Gets a value in a table. * - * This function is the same as #wasm_table_get except that it's specialized for - * funcref tables by returning a `wasm_func_t` value. Additionally a `bool` - * return value indicates whether the `index` provided was in bounds. + * \param store the store that owns `table` + * \param table the table to access + * \param index the table index to access + * \param val where to store the table's value * - * This function does not take ownership of any of its parameters, but yields - * ownership of returned #wasm_func_t. + * This function will attempt to access a table element. If a nonzero value is + * returned then `val` is filled in and is owned by the caller. Otherwise zero + * is returned because the `index` is out-of-bounds. */ WASM_API_EXTERN bool wasmtime_table_get( wasmtime_context_t *store, @@ -60,14 +68,15 @@ WASM_API_EXTERN bool wasmtime_table_get( /** * \brief Sets a value in a table. * - * This function is similar to #wasm_table_set, but has a few differences: - * - * * An error is returned through #wasmtime_error_t describing erroneous - * situations. - * * The value being set is specialized to #wasm_func_t. + * \param store the store that owns `table` + * \param table the table to write to + * \param index the table index to write + * \param value the value to store. * - * This function does not take ownership of any of its parameters, but yields - * ownership of returned #wasmtime_error_t. + * This function will store `value` into the specified index in the table. This + * does not take ownership of any argument but yields ownership of the error. + * This function can fail if `value` has the wrong type for the table, or if + * `index` is out of bounds. */ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( wasmtime_context_t *store, @@ -76,7 +85,9 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( const wasmtime_val_t *value ); -/// TODO +/** + * \brief Returns the size, in elements, of the specified table + */ WASM_API_EXTERN uint32_t wasmtime_table_size( const wasmtime_context_t *store, wasmtime_table_t table @@ -85,22 +96,26 @@ WASM_API_EXTERN uint32_t wasmtime_table_size( /** * \brief Grows a table. * - * This function is similar to #wasm_table_grow, but has a few differences: + * \param store the store that owns `table` + * \param table the table to grow + * \param delta the number of elements to grow the table by + * \param init the initial value for new table element slots + * \param prev_size where to store the previous size of the table before growth * - * * An error is returned through #wasmtime_error_t describing erroneous - * situations. - * * The initialization value is specialized to #wasm_func_t. - * * The previous size of the table is returned through `prev_size`. + * This function will attempt to grow the table by `delta` table elements. This + * can fail if `delta` would exceed the maximum size of the table or if `init` + * is the wrong type for this table. If growth is successful then `NULL` is + * returned and `prev_size` is filled in with the previous size of the table, in + * elements, before the growth happened. * - * This function does not take ownership of any of its parameters, but yields - * ownership of returned #wasmtime_error_t. + * This function does not take ownership of any of its arguments. */ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_grow( wasmtime_context_t *store, wasmtime_table_t table, uint32_t delta, const wasmtime_val_t *init, - wasm_table_size_t *prev_size + uint32_t *prev_size ); #ifdef __cplusplus diff --git a/crates/c-api/include/wasmtime/trap.h b/crates/c-api/include/wasmtime/trap.h index 2811492fef62..2ad0800f0a68 100644 --- a/crates/c-api/include/wasmtime/trap.h +++ b/crates/c-api/include/wasmtime/trap.h @@ -1,7 +1,7 @@ /** * \file wasmtime/trap.h * - * TODO + * Wasmtime APIs for interacting with traps and extensions to #wasm_trap_t. */ #ifndef WASMTIME_TRAP_H @@ -13,8 +13,15 @@ extern "C" { #endif -/// TODO -WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(char *msg, size_t msg_len); +/** + * \brief Creates a new trap. + * + * \param msg the message to associate with this trap + * \param msg_len the byte length of `msg` + * + * The #wasm_trap_t returned is owned by the caller. + */ +WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(const char *msg, size_t msg_len); /** * \brief Attempts to extract a WASI-specific exit status from this trap. diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h index ee79ce144054..6acacd61c992 100644 --- a/crates/c-api/include/wasmtime/val.h +++ b/crates/c-api/include/wasmtime/val.h @@ -1,7 +1,7 @@ /** * \file wasmtime/val.h * - * TODO + * APIs for interacting with WebAssembly values in Wasmtime. */ #ifndef WASMTIME_VAL_H @@ -14,134 +14,137 @@ extern "C" { #endif -/// TODO +/** + * \typedef wasmtime_externref_t + * \brief Convenience alias for #wasmtime_externref + * + * \struct wasmtime_externref + * \brief A host-defined un-forgeable reference to pass into WebAssembly. + * + * This structure represents an `externref` that can be passed to WebAssembly. + * It cannot be forged by WebAssembly itself and is guaranteed to have been + * created by the host. + */ typedef struct wasmtime_externref wasmtime_externref_t; /** - * \brief Create a new `externref` value with a finalizer. + * \brief Create a new `externref` value. * - * TODO + * Creates a new `externref` value wrapping the provided data, returning the + * pointer to the externref. * - * Creates a new `externref` value wrapping the provided data, and writes it to - * `valp`. + * \param data the host-specific data to wrap + * \param finalizer an optional finalizer for `data` * * When the reference is reclaimed, the wrapped data is cleaned up with the - * provided finalizer. If you do not need to clean up the wrapped data, then use - * #wasmtime_externref_new. + * provided `finalizer`. * - * Gives ownership of the newly created `externref` value. + * The returned value must be deleted with #wasmtime_externref_delete */ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (*finalizer)(void*)); /** * \brief Get an `externref`'s wrapped data * - * TODO - * - * If the given value is a reference to a non-null `externref`, writes the - * wrapped data that was passed into #wasmtime_externref_new - * when creating the given `externref` to - * `datap`, and returns `true`. - * - * If the value is a reference to a null `externref`, writes `NULL` to `datap` - * and returns `true`. - * - * If the given value is not an `externref`, returns `false` and leaves `datap` - * unmodified. - * - * Does not take ownership of `val`. Does not give up ownership of the `void*` - * data written to `datap`. - * - * Both `val` and `datap` must not be `NULL`. + * Returns the original `data` passed to #wasmtime_externref_new. It is required + * that `data` is not `NULL`. */ WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_externref_t *data); -/// TODO +/** + * \brief Creates a shallow copy of the `externref` argument, returning a + * separately owned pointer (increases the reference count). + */ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externref_t *ref); /** - * TODO + * \brief Decrements the reference count of the `ref`, deleting it if it's the + * last reference. */ WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref); -/// TODO +/// \brief Discriminant stored in #wasmtime_val::kind typedef uint8_t wasmtime_valkind_t; -/// TODO +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i32 #define WASMTIME_I32 0 -/// TODO +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i64 #define WASMTIME_I64 1 -/// TODO +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f32 #define WASMTIME_F32 2 -/// TODO +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f64 #define WASMTIME_F64 3 -/// TODO +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a v128 #define WASMTIME_V128 4 -/// TODO +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a funcref #define WASMTIME_FUNCREF 5 -/// TODO +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an externref #define WASMTIME_EXTERNREF 6 -/// TODO +/// \brief A 128-bit value representing the WebAssembly `v128` type. Bytes are +/// stored in little-endian order. typedef uint8_t wasmtime_v128[16]; /** - * TODO + * \typedef wasmtime_valunion_t + * \brief Convenience alias for #wasmtime_valunion + * + * \union wasmtime_valunion + * \brief Container for different kinds of wasm values. + * + * This type is contained in #wasmtime_val_t and contains the payload for the + * various kinds of items a value can be. */ typedef union wasmtime_valunion { - /** - * TODO - */ + /// Field used if #wasmtime_val_t::kind is #WASMTIME_I32 int32_t i32; - /** - * TODO - */ + /// Field used if #wasmtime_val_t::kind is #WASMTIME_I64 int64_t i64; - /** - * TODO - */ + /// Field used if #wasmtime_val_t::kind is #WASMTIME_F32 float32_t f32; - /** - * TODO - */ + /// Field used if #wasmtime_val_t::kind is #WASMTIME_F64 float64_t f64; - /** - * TODO - */ + /// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF wasmtime_func_t funcref; - /** - * TODO - */ + /// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF wasmtime_externref_t *externref; - /** - * TODO - */ + /// Field used if #wasmtime_val_t::kind is #WASMTIME_V128 wasmtime_v128 v128; } wasmtime_valunion_t; /** - * TODO + * \typedef wasmtime_val_t + * \brief Convenience alias for #wasmtime_val_t + * + * \union wasmtime_val + * \brief Container for different kinds of wasm values. + * + * Note that this structure may contain an owned value, namely + * #wasmtime_externref_t, depending on the context in which this is used. APIs + * which consume a #wasmtime_val_t do not take ownership, but APIs that return + * #wasmtime_val_t require that #wasmtime_val_delete is called to deallocate + * the value. */ typedef struct wasmtime_val { - /** - * TODO - */ + /// Discriminant of which field of #of is valid. wasmtime_valkind_t kind; - /** - * TODO - */ + /// Container for the extern item's value. wasmtime_valunion_t of; } wasmtime_val_t; -/// TODO +/// \brief value for #wasmtime_valunion::funcref indicating that the funcref is +/// null. #define WASMTIME_FUNCREF_NULL ((uint64_t) 0xffffffffffffffff) /** - * TODO + * \brief Delets an owned #wasmtime_val_t. + * + * Note that this only deletes the contents, not the memory that `val` points to + * itself (which is owned by the caller). */ WASM_API_EXTERN void wasmtime_val_delete(wasmtime_val_t *val); /** - * TODO + * \brief Copies `src` into `dst`. */ WASM_API_EXTERN void wasmtime_val_copy(wasmtime_val_t *dst, const wasmtime_val_t *src); diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 8511d2c8522b..6792aebe0a66 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -177,6 +177,11 @@ pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t &mut (*f).ext } +#[no_mangle] +pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t { + &(*f).ext +} + #[repr(C)] pub struct wasmtime_caller_t<'a> { caller: Caller<'a, crate::StoreData>, diff --git a/crates/c-api/src/global.rs b/crates/c-api/src/global.rs index ebb48a3f2b90..2bd33700b8a6 100644 --- a/crates/c-api/src/global.rs +++ b/crates/c-api/src/global.rs @@ -47,7 +47,12 @@ pub unsafe extern "C" fn wasm_global_new( } #[no_mangle] -pub extern "C" fn wasm_global_as_extern(g: &wasm_global_t) -> &wasm_extern_t { +pub extern "C" fn wasm_global_as_extern(g: &mut wasm_global_t) -> &mut wasm_extern_t { + &mut g.ext +} + +#[no_mangle] +pub extern "C" fn wasm_global_as_extern_const(g: &wasm_global_t) -> &wasm_extern_t { &g.ext } diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 9013b3828294..f27d3f582bc8 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -117,7 +117,7 @@ pub unsafe extern "C" fn wasmtime_linker_get_default( } #[no_mangle] -pub unsafe extern "C" fn wasmtime_linker_get_one_by_name( +pub unsafe extern "C" fn wasmtime_linker_get( linker: &wasmtime_linker_t, store: CStoreContextMut<'_>, module: *const u8, diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs index c4b7f2047786..72c4c493a1b4 100644 --- a/crates/c-api/src/memory.rs +++ b/crates/c-api/src/memory.rs @@ -45,7 +45,12 @@ pub unsafe extern "C" fn wasm_memory_new( } #[no_mangle] -pub extern "C" fn wasm_memory_as_extern(m: &wasm_memory_t) -> &wasm_extern_t { +pub extern "C" fn wasm_memory_as_extern(m: &mut wasm_memory_t) -> &mut wasm_extern_t { + &mut m.ext +} + +#[no_mangle] +pub extern "C" fn wasm_memory_as_extern_const(m: &wasm_memory_t) -> &wasm_extern_t { &m.ext } diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs index 4853cbde7070..c2211046e9e4 100644 --- a/crates/c-api/src/table.rs +++ b/crates/c-api/src/table.rs @@ -106,7 +106,12 @@ pub unsafe extern "C" fn wasm_table_grow( } #[no_mangle] -pub extern "C" fn wasm_table_as_extern(t: &wasm_table_t) -> &wasm_extern_t { +pub extern "C" fn wasm_table_as_extern(t: &mut wasm_table_t) -> &mut wasm_extern_t { + &mut t.ext +} + +#[no_mangle] +pub extern "C" fn wasm_table_as_extern_const(t: &wasm_table_t) -> &wasm_extern_t { &t.ext } From e5074d009370a2273900c7a61c9a6a189fb0106b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 May 2021 13:45:18 -0700 Subject: [PATCH 42/90] Comments & build fixes --- crates/c-api/include/wasmtime/linker.h | 6 +++--- crates/c-api/src/linker.rs | 22 ++++++++++++++++------ crates/runtime/src/externref.rs | 16 +++++++--------- crates/wasmtime/src/ref.rs | 3 +++ 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index b81e822a9bb3..08344f6db212 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -219,10 +219,10 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default( * \param name_len the byte length of `name` * \param item where to store the extracted item * - * \return An error is returned if the item isn't defined or has more than one - * definition, or `NULL` is returned and `item` is filled in otherwise. + * \return A nonzero value if the item is defined, in which case `item` is also + * filled in. Otherwise zero is returned. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get( +WASM_API_EXTERN bool wasmtime_linker_get( const wasmtime_linker_t *linker, wasmtime_context_t *store, const char *module, diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index f27d3f582bc8..14c8b28531af 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -125,15 +125,25 @@ pub unsafe extern "C" fn wasmtime_linker_get( name: *const u8, name_len: usize, item_ptr: &mut MaybeUninit, -) -> Option> { +) -> bool { let linker = &linker.linker; - let module = to_str!(module, module_len); + let module = match str::from_utf8(crate::slice_from_raw_parts(module, module_len)) { + Ok(s) => s, + Err(_) => return false, + }; let name = if name.is_null() { None } else { - Some(to_str!(name, name_len)) + match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) { + Ok(s) => Some(s), + Err(_) => return false, + } }; - handle_result(linker.get_one_by_name(store, module, name), |which| { - crate::initialize(item_ptr, which.into()) - }) + match linker.get(store, module, name) { + Some(which) => { + crate::initialize(item_ptr, which.into()); + true + } + None => false, + } } diff --git a/crates/runtime/src/externref.rs b/crates/runtime/src/externref.rs index 8983eb0f45fc..4a6d250a109c 100644 --- a/crates/runtime/src/externref.rs +++ b/crates/runtime/src/externref.rs @@ -165,7 +165,7 @@ use wasmtime_environ::ir::StackMap; #[repr(transparent)] pub struct VMExternRef(NonNull); -// data contained is always Send+Sync so these should be safe +// Data contained is always Send+Sync so these should be safe. unsafe impl Send for VMExternRef {} unsafe impl Sync for VMExternRef {} @@ -255,7 +255,7 @@ impl VMExternData { // resides within after this block. let (alloc_ptr, layout) = { let data = data.as_mut(); - debug_assert_eq!(data.get_ref_count(), 0); + debug_assert_eq!(data.ref_count.load(Ordering::SeqCst), 0); // Same thing, but for the dropping the reference to `value` before // we drop it itself. @@ -274,11 +274,6 @@ impl VMExternData { std::alloc::dealloc(alloc_ptr.as_ptr(), layout); } - #[inline] - fn get_ref_count(&self) -> usize { - self.ref_count.load(Ordering::SeqCst) - } - #[inline] fn increment_ref_count(&self) { // This is only using during cloning operations, and like the standard @@ -331,7 +326,7 @@ impl VMExternRef { extern_data_ptr, VMExternData { ref_count: AtomicUsize::new(1), - // cast from `*mut T` to `*mut dyn Any` here + // Cast from `*mut T` to `*mut dyn Any` here. value_ptr: NonNull::new_unchecked(value_ptr.as_ptr()), }, ); @@ -400,8 +395,11 @@ impl VMExternRef { } /// Get the strong reference count for this `VMExternRef`. + /// + /// Note that this loads with a `SeqCst` ordering to synchronize with other + /// threads. pub fn strong_count(&self) -> usize { - self.extern_data().get_ref_count() + self.extern_data().ref_count.load(Ordering::SeqCst) } #[inline] diff --git a/crates/wasmtime/src/ref.rs b/crates/wasmtime/src/ref.rs index 32aefc54d0ae..9af6057a43d7 100644 --- a/crates/wasmtime/src/ref.rs +++ b/crates/wasmtime/src/ref.rs @@ -26,6 +26,9 @@ impl ExternRef { } /// Get the strong reference count for this `ExternRef`. + /// + /// Note that this loads the reference count with a `SeqCst` ordering to + /// synchronize with other threads. pub fn strong_count(&self) -> usize { self.inner.strong_count() } From a5d94bdb6bcf05f3b82a804daedbaa1de4d1fbef Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 May 2021 13:56:40 -0700 Subject: [PATCH 43/90] Dox and build fixes --- cranelift/interpreter/src/interpreter.rs | 5 +++-- crates/wasmtime/src/store.rs | 4 ++-- crates/wasmtime/src/store/context.rs | 4 ++-- docs/examples-rust-wasi.md | 6 +++++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cranelift/interpreter/src/interpreter.rs b/cranelift/interpreter/src/interpreter.rs index 89d2ae32a071..77ed5b0ffc5c 100644 --- a/cranelift/interpreter/src/interpreter.rs +++ b/cranelift/interpreter/src/interpreter.rs @@ -42,7 +42,8 @@ impl<'a> Interpreter<'a> { self.call_by_index(index, arguments) } - /// Call a function by its index in the [FunctionStore]; this is a proxy for [Interpreter::call]. + /// Call a function by its index in the [FunctionStore]; this is a proxy for + /// `Interpreter::call`. pub fn call_by_index( &mut self, index: FuncIndex, @@ -287,7 +288,7 @@ mod tests { v1 = iadd_imm v0, -1 return v1 } - + function %parent(i32) -> i32 { fn42 = %child(i32) -> i32 block0(v0: i32): diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index a5a8b118b177..667fd61be5ad 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -64,8 +64,8 @@ pub use self::data::*; /// [`StoreContextMut`] and pass that around as well. /// /// Note that all methods on [`Store`] are mirrored onto [`StoreContext`], -/// [`StoreContextMut`], and [`Caller`]. This way no matter what form of context -/// you have you can call various methods, create objects, etc. +/// [`StoreContextMut`], and [`Caller`](crate::Caller). This way no matter what +/// form of context you have you can call various methods, create objects, etc. /// /// ## Stores and `Default` /// diff --git a/crates/wasmtime/src/store/context.rs b/crates/wasmtime/src/store/context.rs index 7e89efd60c98..b46a7072a773 100644 --- a/crates/wasmtime/src/store/context.rs +++ b/crates/wasmtime/src/store/context.rs @@ -103,8 +103,8 @@ pub trait AsContext { /// /// This is notably used for methods that may require some mutation of the /// [`Store`] itself. For example calling a wasm function can mutate linear -/// memory or globals. Creation of a [`Func`] will update internal data -/// structures. This ends up being quite a common bound in Wasmtime, but +/// memory or globals. Creation of a [`Func`](crate::Func) will update internal +/// data structures. This ends up being quite a common bound in Wasmtime, but /// typically you can simply pass `&mut store` or `&mut caller` to satisfy it. pub trait AsContextMut: AsContext { /// Returns the store context that this type provides access to. diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index 3267d357d129..71c22d08a9d6 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -38,10 +38,13 @@ looks like: [`WasiCtx`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/struct.WasiCtx.html ```rust +# extern crate wasmtime; +# extern crate wasmtime_wasi; +# extern crate anyhow; use anyhow::Result; use std::borrow::{Borrow, BorrowMut}; use wasmtime::*; -use wasmtime_wasi::{WasiCtx, WasiCtxBuilder}; +use wasmtime_wasi::{WasiCtx, sync::WasiCtxBuilder}; struct MyState { message: String, @@ -76,6 +79,7 @@ fn main() -> Result<()> { // ... +# let _linker: Linker = linker; Ok(()) } ``` From ed774d83bdd0f280add2fafa903e1e2ce3c2da2e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 May 2021 14:15:07 -0700 Subject: [PATCH 44/90] Update book build on CI --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0afe505f30ed..ba8b1bc338ac 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,7 +54,7 @@ jobs: curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.4.4/mdbook-v0.4.4-x86_64-unknown-linux-gnu.tar.gz | tar xzf - echo `pwd` >> $GITHUB_PATH - run: (cd docs && mdbook build) - - run: cargo build -p wasmtime + - run: cargo build -p wasmtime-wasi - run: (cd docs && mdbook test -L ../target/debug/deps) - uses: actions/upload-artifact@v1 with: From caf4c300c9f977ffea41f1b51118b7537f2a5d92 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 19 May 2021 07:29:01 -0700 Subject: [PATCH 45/90] Add `Store::into_data` --- crates/wasmtime/src/store.rs | 61 +++++++++++++++++++++++++++++++++--- tests/all/main.rs | 1 + tests/all/store.rs | 22 +++++++++++++ 3 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 tests/all/store.rs diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 667fd61be5ad..fa56935e4a25 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -7,6 +7,7 @@ use std::error::Error; use std::fmt; use std::future::Future; use std::marker; +use std::mem::ManuallyDrop; use std::pin::Pin; use std::ptr; use std::sync::Arc; @@ -73,7 +74,8 @@ pub use self::data::*; /// `Store::default()`. This will create a brand new [`Engine`] with default /// ocnfiguration (see [`Config`](crate::Config) for more information). pub struct Store { - inner: Box>, + // for comments about `ManuallyDrop`, see `Store::into_data` + inner: ManuallyDrop>>, } pub struct StoreInner { @@ -119,7 +121,8 @@ pub struct StoreInner { store_data: StoreData, limiter: Option>, default_callee: InstanceHandle, - data: T, + // for comments about `ManuallyDrop`, see `Store::into_data` + data: ManuallyDrop, } struct AsyncState { @@ -210,7 +213,7 @@ impl Store { store_data: StoreData::new(), limiter: None, default_callee, - data, + data: ManuallyDrop::new(data), }); // Once we've actually allocated the store itself we can configure the @@ -219,7 +222,9 @@ impl Store { unsafe { inner.default_callee.set_store(store); } - Self { inner } + Self { + inner: ManuallyDrop::new(inner), + } } /// Access the underlying data owned by this `Store`. @@ -232,6 +237,39 @@ impl Store { self.inner.data_mut() } + /// Consumes this [`Store`], destroying it, and returns the underlying data. + pub fn into_data(mut self) -> T { + // This is an unsafe operation because we want to avoid having a runtime + // check or boolean for whether the data is actually contained within a + // `Store`. The data itself is stored as `ManuallyDrop` since we're + // manually managing the memory here, and there's also a `ManuallyDrop` + // around the `Box>`. The way this works though is a bit + // tricky, so here's how things get dropped appropriately: + // + // * When a `Store` is normally dropped, the custom destructor for + // `Store` will drop `T`, then the `self.inner` field. The + // rustc-glue destructor runs for `Box>` which drops + // `StoreInner`. This cleans up all internal fields and doesn't + // touch `T` because it's wrapped in `ManuallyDrop`. + // + // * When calling this method we skip the top-level destructor for + // `Store` with `mem::forget`. This skips both the destructor for + // `T` and the destructor for `StoreInner`. We do, however, run the + // destructor for `Box>` which, like above, will skip + // the destructor for `T` since it's `ManuallyDrop`. + // + // In both cases all the other fields of `StoreInner` should all get + // dropped, and the manual management of destructors is basically + // between this method and `Drop for Store`. Note that this also + // means that `Drop for StoreInner` cannot access `self.data`, so + // there is a comment indicating this as well. + unsafe { + let mut inner = ManuallyDrop::take(&mut self.inner); + std::mem::forget(self); + ManuallyDrop::take(&mut inner.data) + } + } + /// Configures the [`ResourceLimiter`](crate::ResourceLimiter) used to limit /// resource creation within this [`Store`]. /// @@ -1141,7 +1179,7 @@ impl Default for Store { impl fmt::Debug for Store { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let inner = &*self.inner as *const StoreInner; + let inner = &**self.inner as *const StoreInner; f.debug_struct("Store") .field("inner", &inner) .field("data", &self.inner.data) @@ -1149,8 +1187,21 @@ impl fmt::Debug for Store { } } +impl Drop for Store { + fn drop(&mut self) { + // for documentation on this `unsafe`, see `into_data`. + unsafe { + ManuallyDrop::drop(&mut self.inner.data); + ManuallyDrop::drop(&mut self.inner); + } + } +} + impl Drop for StoreInner { fn drop(&mut self) { + // NB it's important that this destructor does not access `T`. That is + // deallocated by `Drop for Store` above. + let allocator = self.engine.allocator(); unsafe { let ondemand = OnDemandInstanceAllocator::default(); diff --git a/tests/all/main.rs b/tests/all/main.rs index 838a78e5e651..37bb68d7a692 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -22,6 +22,7 @@ mod module_serialize; mod name; mod pooling_allocator; mod stack_overflow; +mod store; mod table; mod traps; mod wast; diff --git a/tests/all/store.rs b/tests/all/store.rs new file mode 100644 index 000000000000..837660916944 --- /dev/null +++ b/tests/all/store.rs @@ -0,0 +1,22 @@ +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use wasmtime::{Engine, Store}; + +#[test] +fn into_inner() { + static HITS: AtomicUsize = AtomicUsize::new(0); + + struct A; + + impl Drop for A { + fn drop(&mut self) { + HITS.fetch_add(1, SeqCst); + } + } + + let engine = Engine::default(); + assert_eq!(HITS.load(SeqCst), 0); + drop(Store::new(&engine, A)); + assert_eq!(HITS.load(SeqCst), 1); + Store::new(&engine, A).into_data(); + assert_eq!(HITS.load(SeqCst), 2); +} From f86f2745fb8b2d00100cb26bbfaaca26301b10d9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 19 May 2021 07:30:33 -0700 Subject: [PATCH 46/90] Build mdbook tests with `wat` support --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ba8b1bc338ac..c0e84ea1c368 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,7 +54,7 @@ jobs: curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.4.4/mdbook-v0.4.4-x86_64-unknown-linux-gnu.tar.gz | tar xzf - echo `pwd` >> $GITHUB_PATH - run: (cd docs && mdbook build) - - run: cargo build -p wasmtime-wasi + - run: cargo build -p wasmtime-wasi --features wasmtime/wat - run: (cd docs && mdbook test -L ../target/debug/deps) - uses: actions/upload-artifact@v1 with: From 8d526b89fa0656207609e8ffa88e1e6021d9bf37 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 19 May 2021 07:42:21 -0700 Subject: [PATCH 47/90] Add aborting stubs for the rest of the C API --- crates/c-api/include/doc-wasm.h | 4 +- crates/c-api/src/ref.rs | 168 +++++++++++++++++++++++++++++++- crates/c-api/src/trap.rs | 5 + 3 files changed, 172 insertions(+), 5 deletions(-) diff --git a/crates/c-api/include/doc-wasm.h b/crates/c-api/include/doc-wasm.h index d95a42aced5d..5170de920cc1 100644 --- a/crates/c-api/include/doc-wasm.h +++ b/crates/c-api/include/doc-wasm.h @@ -1095,7 +1095,9 @@ * See #wasm_byte_vec_delete for more information. * * \fn wasm_frame_t *wasm_frame_copy(const wasm_frame_t *) - * \brief Unimplemented in Wasmtime. + * \brief Returns a copy of the provided frame. + * + * The caller is expected to call #wasm_frame_delete on the returned frame. * * \fn wasm_instance_t *wasm_frame_instance(const wasm_frame_t *); * \brief Unimplemented in Wasmtime, aborts the process if called. diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs index 5e966ce95cee..f6be82acafbb 100644 --- a/crates/c-api/src/ref.rs +++ b/crates/c-api/src/ref.rs @@ -50,6 +50,11 @@ pub extern "C" fn wasm_ref_copy(r: Option<&wasm_ref_t>) -> Option ! { + eprintln!("`{}` is not implemented", name); + std::process::abort(); +} + #[no_mangle] pub extern "C" fn wasm_ref_same(a: Option<&wasm_ref_t>, b: Option<&wasm_ref_t>) -> bool { match (a.map(|a| &a.r), b.map(|b| &b.r)) { @@ -68,8 +73,7 @@ pub extern "C" fn wasm_ref_get_host_info(_ref: Option<&wasm_ref_t>) -> *mut c_vo #[no_mangle] pub extern "C" fn wasm_ref_set_host_info(_ref: Option<&wasm_ref_t>, _info: *mut c_void) { - eprintln!("`wasm_ref_set_host_info` is not implemented"); - std::process::abort(); + abort("wasm_ref_set_host_info") } #[no_mangle] @@ -78,6 +82,162 @@ pub extern "C" fn wasm_ref_set_host_info_with_finalizer( _info: *mut c_void, _finalizer: Option, ) { - eprintln!("`wasm_ref_set_host_info_with_finalizer` is not implemented"); - std::process::abort(); + abort("wasm_ref_set_host_info_with_finalizer") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_extern(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_extern_t> { + abort("wasm_ref_as_extern") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_extern_const( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_extern_t> { + abort("wasm_ref_as_extern_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_foreign(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_foreign_t> { + abort("wasm_ref_as_foreign") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_foreign_const( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_foreign_t> { + abort("wasm_ref_as_foreign_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_func(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_func_t> { + abort("wasm_ref_as_func") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_func_const(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_func_t> { + abort("wasm_ref_as_func_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_global(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_global_t> { + abort("wasm_ref_as_global") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_global_const( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_global_t> { + abort("wasm_ref_as_global_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_instance( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_instance_t> { + abort("wasm_ref_as_instance") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_instance_const( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_instance_t> { + abort("wasm_ref_as_instance_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_memory(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_memory_t> { + abort("wasm_ref_as_memory") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_memory_const( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_memory_t> { + abort("wasm_ref_as_memory_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_module(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_module_t> { + abort("wasm_ref_as_module") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_module_const( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_module_t> { + abort("wasm_ref_as_module_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_table(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_table_t> { + abort("wasm_ref_as_table") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_table_const( + _ref: Option<&wasm_ref_t>, +) -> Option<&crate::wasm_table_t> { + abort("wasm_ref_as_table_const") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_trap(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_trap_t> { + abort("wasm_ref_as_trap") +} + +#[no_mangle] +pub extern "C" fn wasm_ref_as_trap_const(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_trap_t> { + abort("wasm_ref_as_trap_const") +} + +#[derive(Clone)] +#[repr(C)] +pub struct wasm_foreign_t {} + +#[no_mangle] +pub extern "C" fn wasm_foreign_new(_store: &crate::wasm_store_t) -> Box { + abort("wasm_foreign_new") +} + +#[no_mangle] +pub extern "C" fn wasm_foreign_delete(_foreign: Box) {} + +#[no_mangle] +pub extern "C" fn wasm_foreign_copy(r: &wasm_foreign_t) -> Box { + Box::new(r.clone()) +} + +#[no_mangle] +pub extern "C" fn wasm_foreign_same(_a: &wasm_foreign_t, _b: &wasm_foreign_t) -> bool { + abort("wasm_foreign_same") +} + +#[no_mangle] +pub extern "C" fn wasm_foreign_get_host_info(_foreign: &wasm_foreign_t) -> *mut c_void { + std::ptr::null_mut() +} + +#[no_mangle] +pub extern "C" fn wasm_foreign_set_host_info(_foreign: &wasm_foreign_t, _info: *mut c_void) { + abort("wasm_foreign_set_host_info") +} + +#[no_mangle] +pub extern "C" fn wasm_foreign_set_host_info_with_finalizer( + _foreign: &wasm_foreign_t, + _info: *mut c_void, + _finalizer: Option, +) { + abort("wasm_foreign_set_host_info_with_finalizer") +} + +#[no_mangle] +pub extern "C" fn wasm_foreign_as_ref(_: &wasm_foreign_t) -> &wasm_ref_t { + abort("wasm_foreign_as_ref") +} + +#[no_mangle] +pub extern "C" fn wasm_foreign_as_ref_const(_: &wasm_foreign_t) -> Option<&wasm_ref_t> { + abort("wasm_foreign_as_ref_const") } diff --git a/crates/c-api/src/trap.rs b/crates/c-api/src/trap.rs index 63d393fc8cf1..5bd5256fefc7 100644 --- a/crates/c-api/src/trap.rs +++ b/crates/c-api/src/trap.rs @@ -145,3 +145,8 @@ pub extern "C" fn wasm_frame_instance(_arg1: *const wasm_frame_t) -> *mut wasm_i pub extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t) -> usize { frame.trap.trace()[frame.idx].module_offset() } + +#[no_mangle] +pub extern "C" fn wasm_frame_copy(frame: &wasm_frame_t) -> Box { + Box::new(frame.clone()) +} From e88e1d57dd45dbc801536bf2f901c179e86957aa Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 19 May 2021 13:45:45 -0700 Subject: [PATCH 48/90] Add a method to alias one item in a linker This allows aliasing individual items one-at-a-time instead of entire modules all at once. --- crates/wasmtime/src/linker.rs | 27 ++++++++++++++++++++++++++- crates/wast/src/wast.rs | 2 +- tests/all/linker.rs | 12 ++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index 7e595b08b45a..0329c1b0b069 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -684,6 +684,31 @@ impl Linker { Ok(self) } + /// Aliases one item's name as another. + /// + /// This method will alias an item with the specified `module` and `name` + /// under a new name of `as_module` and `as_name`. + /// + /// # Errors + /// + /// Returns an error if any shadowing violations happen while defining new + /// items, or if the original item wasn't defined. + pub fn alias( + &mut self, + module: &str, + name: &str, + as_module: &str, + as_name: &str, + ) -> Result<&mut Self> { + let src = self.import_key(module, Some(name)); + let dst = self.import_key(as_module, Some(as_name)); + match self.map.get(&src).cloned() { + Some(item) => self.insert(dst, item)?, + None => bail!("no item named `{}::{}` defined", module, name), + } + Ok(self) + } + /// Aliases one module's name as another. /// /// This method will alias all currently defined under `module` to also be @@ -693,7 +718,7 @@ impl Linker { /// /// Returns an error if any shadowing violations happen while defining new /// items. - pub fn alias(&mut self, module: &str, as_module: &str) -> Result<()> { + pub fn alias_module(&mut self, module: &str, as_module: &str) -> Result<()> { let module = self.intern_str(module); let as_module = self.intern_str(as_module); let items = self diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index f12e7d41b4c8..32e95e1aaf95 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -140,7 +140,7 @@ impl WastContext { /// Register an instance to make it available for performing actions. fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> { match name { - Some(name) => self.linker.alias(name, as_name), + Some(name) => self.linker.alias_module(name, as_name), None => { let current = *self .current diff --git a/tests/all/linker.rs b/tests/all/linker.rs index e86d6ae109b8..26809ce9faee 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -296,3 +296,15 @@ fn funcs_live_on_to_fight_another_day() -> Result<()> { assert_eq!(flag.load(SeqCst), 1); Ok(()) } + +#[test] +fn alias_one() -> Result<()> { + let mut store = Store::<()>::default(); + let mut linker = Linker::new(store.engine()); + assert!(linker.alias("a", "b", "c", "d").is_err()); + linker.func_wrap("a", "b", || {})?; + assert!(linker.alias("a", "b", "c", "d").is_ok()); + assert!(linker.get(&mut store, "a", Some("b")).is_some()); + assert!(linker.get(&mut store, "c", Some("d")).is_some()); + Ok(()) +} From 860713ce3b8985948d5bd802dad7a25b9de37089 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 19 May 2021 14:42:09 -0700 Subject: [PATCH 49/90] wasmtime-wiggle: reduce to generating a single add_to_linker func --- crates/wiggle/wasmtime/macro/src/config.rs | 125 ++----------------- crates/wiggle/wasmtime/macro/src/lib.rs | 126 ++++---------------- crates/wiggle/wasmtime/tests/atoms_async.rs | 5 +- crates/wiggle/wasmtime/tests/atoms_sync.rs | 5 +- 4 files changed, 41 insertions(+), 220 deletions(-) diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index 30815817b255..c6eea76d3a8d 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -1,12 +1,11 @@ use wiggle_generate::config::AsyncFunctions; use { proc_macro2::Span, - std::collections::HashMap, syn::{ braced, parse::{Parse, ParseStream}, punctuated::Punctuated, - Error, Ident, Path, Result, Token, + Error, Path, Result, Token, }, wiggle_generate::config::WitxConf, }; @@ -15,7 +14,6 @@ pub struct Config { pub target: TargetConf, pub witx: WitxConf, pub ctx: CtxConf, - pub modules: ModulesConf, pub async_: AsyncConf, } @@ -24,7 +22,6 @@ pub enum ConfigField { Target(TargetConf), Witx(WitxConf), Ctx(CtxConf), - Modules(ModulesConf), Async(AsyncConf), } @@ -33,10 +30,6 @@ mod kw { syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); syn::custom_keyword!(ctx); - syn::custom_keyword!(modules); - syn::custom_keyword!(name); - syn::custom_keyword!(docs); - syn::custom_keyword!(function_override); syn::custom_keyword!(block_on); } @@ -59,10 +52,6 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::Ctx(input.parse()?)) - } else if lookahead.peek(kw::modules) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Modules(input.parse()?)) } else if lookahead.peek(Token![async]) { input.parse::()?; input.parse::()?; @@ -88,7 +77,6 @@ impl Config { let mut target = None; let mut witx = None; let mut ctx = None; - let mut modules = None; let mut async_ = None; for f in fields { match f { @@ -110,12 +98,6 @@ impl Config { } ctx = Some(c); } - ConfigField::Modules(c) => { - if modules.is_some() { - return Err(Error::new(err_loc, "duplicate `modules` field")); - } - modules = Some(c); - } ConfigField::Async(c) => { if async_.is_some() { return Err(Error::new(err_loc, "duplicate `async` field")); @@ -128,7 +110,6 @@ impl Config { target: target.ok_or_else(|| Error::new(err_loc, "`target` field required"))?, witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, ctx: ctx.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, - modules: modules.ok_or_else(|| Error::new(err_loc, "`modules` field required"))?, async_: async_.unwrap_or_default(), }) } @@ -179,100 +160,6 @@ impl Parse for TargetConf { } } -enum ModuleConfField { - Name(Ident), - Docs(String), -} - -impl Parse for ModuleConfField { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(kw::name) { - input.parse::()?; - input.parse::()?; - Ok(ModuleConfField::Name(input.parse()?)) - } else if lookahead.peek(kw::docs) { - input.parse::()?; - input.parse::()?; - let docs: syn::LitStr = input.parse()?; - Ok(ModuleConfField::Docs(docs.value())) - } else { - Err(lookahead.error()) - } - } -} - -#[derive(Debug, Clone)] -pub struct ModuleConf { - pub name: Ident, - pub docs: Option, -} - -impl ModuleConf { - fn build(fields: impl Iterator, err_loc: Span) -> Result { - let mut name = None; - let mut docs = None; - for f in fields { - match f { - ModuleConfField::Name(c) => { - if name.is_some() { - return Err(Error::new(err_loc, "duplicate `name` field")); - } - name = Some(c); - } - ModuleConfField::Docs(c) => { - if docs.is_some() { - return Err(Error::new(err_loc, "duplicate `docs` field")); - } - docs = Some(c); - } - } - } - Ok(ModuleConf { - name: name.ok_or_else(|| Error::new(err_loc, "`name` field required"))?, - docs, - }) - } -} - -impl Parse for ModuleConf { - fn parse(input: ParseStream) -> Result { - let contents; - let _lbrace = braced!(contents in input); - let fields: Punctuated = - contents.parse_terminated(ModuleConfField::parse)?; - Ok(ModuleConf::build(fields.into_iter(), input.span())?) - } -} - -#[derive(Debug, Clone)] -pub struct ModulesConf { - pub mods: HashMap, -} - -impl ModulesConf { - pub fn iter(&self) -> impl Iterator { - self.mods.iter() - } -} - -impl Parse for ModulesConf { - fn parse(input: ParseStream) -> Result { - let contents; - let _lbrace = braced!(contents in input); - let fields: Punctuated<(String, ModuleConf), Token![,]> = - contents.parse_terminated(|i| { - let name = i.parse::()?.to_string(); - i.parse::]>()?; - let val = i.parse()?; - Ok((name, val)) - })?; - Ok(ModulesConf { - mods: fields.into_iter().collect(), - }) - } -} - #[derive(Clone, Default, Debug)] /// Modules and funcs that have async signatures pub struct AsyncConf { @@ -321,4 +208,14 @@ impl AsyncConf { AsyncFunctions::All => a, } } + + pub fn contains_async(&self, module: &witx::Module) -> bool { + for f in module.funcs() { + match self.is_async(module.name.as_str(), f.name.as_str()) { + Asyncness::Async => return true, + _ => {} + } + } + false + } } diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 77fca091daf7..c0d16364d38e 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,7 +6,7 @@ use wiggle_generate::Names; mod config; -use config::{AsyncConf, Asyncness, ModuleConf, TargetConf}; +use config::{AsyncConf, Asyncness, TargetConf}; /// Define the structs required to integrate a Wiggle implementation with Wasmtime. /// @@ -20,41 +20,15 @@ use config::{AsyncConf, Asyncness, ModuleConf, TargetConf}; /// "./path/to_file2.witx"]`. Relative paths are relative to the root of the crate /// where the macro is invoked. `witx_literal` takes a string of the witx document, e.g. /// `"(typename $foo u8)"`. -/// * `ctx`: The context struct used for the Wiggle implementation. This must be the same -/// type as the `wasmtime_wiggle::from_witx` macro at `target` was invoked with. However, it -/// must be imported to the current scope so that it is a bare identifier e.g. `CtxType`, not -/// `path::to::CtxType`. -/// * `modules`: Describes how any modules in the witx document will be implemented as Wasmtime -/// instances. `modules` takes a map from the witx module name to a configuration struct, e.g. -/// `foo => { name: Foo }, bar => { name: Bar }` will generate integrations for the modules -/// named `foo` and `bar` in the witx document, as `pub struct Foo` and `pub struct Bar` -/// respectively. -/// The module configuration uses struct syntax with the following fields: -/// * `name`: required, gives the name of the struct which encapsulates the instance for -/// Wasmtime. -/// * `docs`: optional, a doc string that will be used for the definition of the struct. -/// * `function_override`: A map of witx function names to Rust function symbols for -/// functions that should not call the Wiggle-generated functions, but instead use -/// a separate implementation. This is typically used for functions that need to interact -/// with Wasmtime in a manner that Wiggle does not permit, e.g. wasi's `proc_exit` function -/// needs to return a Trap directly to the runtime. -/// Example: -/// `modules: { some_module => { name: SomeTypeName, docs: "Doc string for definition of -/// SomeTypeName here", function_override: { foo => my_own_foo } }`. -/// #[proc_macro] pub fn wasmtime_integration(args: TokenStream) -> TokenStream { let config = parse_macro_input!(args as config::Config); let doc = config.load_document(); let names = Names::new(quote!(wasmtime_wiggle)); - let modules = config.modules.iter().map(|(name, module_conf)| { - let module = doc - .module(&witx::Id::new(name)) - .unwrap_or_else(|| panic!("witx document did not contain module named '{}'", name)); + let modules = doc.modules().map(|module| { generate_module( &module, - &module_conf, &names, &config.target, &config.ctx.name, @@ -66,92 +40,48 @@ pub fn wasmtime_integration(args: TokenStream) -> TokenStream { fn generate_module( module: &witx::Module, - module_conf: &ModuleConf, names: &Names, target_conf: &TargetConf, ctx_type: &syn::Type, async_conf: &AsyncConf, ) -> TokenStream2 { - let target_path = &target_conf.path; - let module_id = names.module(&module.name); - let target_module = quote! { #target_path::#module_id }; - - let mut host_funcs = Vec::new(); - - let mut any_async = false; - for f in module.funcs() { - let asyncness = async_conf.is_async(module.name.as_str(), f.name.as_str()); - match asyncness { - Asyncness::Blocking => {} - Asyncness::Async => { - assert!( - cfg!(feature = "async"), - "generating async wasmtime Funcs requires cargo feature \"async\"" - ); - any_async = true; - } - _ => {} - } - generate_func(&f, names, &target_module, asyncness, &mut host_funcs); - } - - let send_bound = if any_async { + let send_bound = if async_conf.contains_async(module) { quote! { + Send } } else { quote! {} }; - let linker_add_definitions = host_funcs.iter().map(|(func_name, body)| { - let adder_func = format_ident!("add_{}_to_linker", names.func(&func_name)); - let docs = format!( - "Add the host function for `{}` to a linker under a given module and field name.", - func_name.as_str() - ); - quote! { - #[doc = #docs] - pub fn #adder_func(linker: &mut wasmtime::Linker, module: &str, field: &str) - -> anyhow::Result<()> - where - T: std::borrow::BorrowMut<#ctx_type> #send_bound - { - #body - } - } - }); - let linker_add_invocations = host_funcs.iter().map(|(func_name, _body)| { - let adder_func = format_ident!("add_{}_to_linker", names.func(&func_name)); - let module = module.name.as_str(); - let field = func_name.as_str(); - quote! { - #adder_func(linker, #module, #field)?; - } + let bodies = module.funcs().map(|f| { + let asyncness = async_conf.is_async(module.name.as_str(), f.name.as_str()); + generate_func(&module, &f, names, &target_conf.path, asyncness) }); - let type_name = module_conf.name.clone(); - let add_to_linker = format_ident!("add_{}_to_linker", type_name); quote! { /// Adds all instance items to the specified `Linker`. - pub fn #add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> + pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> where T: std::borrow::BorrowMut<#ctx_type> #send_bound { - #(#linker_add_invocations)* + #(#bodies)* Ok(()) } - - #(#linker_add_definitions)* } } fn generate_func( + module: &witx::Module, func: &witx::InterfaceFunc, names: &Names, - target_module: &TokenStream2, + target_path: &syn::Path, asyncness: Asyncness, - host_funcs: &mut Vec<(witx::Id, TokenStream2)>, -) { +) -> TokenStream2 { let rt = names.runtime_mod(); - let name_ident = names.func(&func.name); + + let module_str = module.name.as_str(); + let module_ident = names.module(&module.name); + + let field_str = func.name.as_str(); + let field_ident = names.func(&func.name); let (params, results) = func.wasm_signature(); @@ -208,54 +138,50 @@ fn generate_func( let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); (caller.data_mut().borrow_mut(), #runtime::WasmtimeGuestMemory::new(mem)) }; - match #target_module::#name_ident(ctx, &mem #(, #arg_names)*) #await_ { + match #target_path::#module_ident::#field_ident(ctx, &mem #(, #arg_names)*) #await_ { Ok(r) => Ok(<#ret_ty>::from(r)), Err(wasmtime_wiggle::Trap::String(err)) => Err(wasmtime::Trap::new(err)), Err(wasmtime_wiggle::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), } }; - let host_wrapper = match asyncness { + match asyncness { Asyncness::Async => { let wrapper = format_ident!("func_wrap{}_async", params.len()); quote! { linker.#wrapper( - module, - field, + #module_str, + #field_str, move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| { Box::new(async move { #body }) }, )?; - Ok(()) } } Asyncness::Blocking => { quote! { linker.func_wrap( - module, - field, + #module_str, + #field_str, move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { let result = async { #body }; #rt::run_in_dummy_executor(result) }, )?; - Ok(()) } } Asyncness::Sync => { quote! { linker.func_wrap( - module, - field, + #module_str, + #field_str, move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { #body }, )?; - Ok(()) } } - }; - host_funcs.push((func.name.clone(), host_wrapper)); + } } diff --git a/crates/wiggle/wasmtime/tests/atoms_async.rs b/crates/wiggle/wasmtime/tests/atoms_async.rs index fbb86388faa9..a022acfc830f 100644 --- a/crates/wiggle/wasmtime/tests/atoms_async.rs +++ b/crates/wiggle/wasmtime/tests/atoms_async.rs @@ -14,7 +14,6 @@ wasmtime_wiggle::wasmtime_integration!({ target: crate, witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], ctx: Ctx, - modules: { atoms => { name: atoms } }, async: { atoms::double_int_return_float } @@ -45,7 +44,7 @@ impl atoms::Atoms for Ctx { fn test_sync_host_func() { let mut store = async_store(); let mut linker = Linker::new(store.engine()); - add_atoms_to_linker(&mut linker).unwrap(); + add_to_linker(&mut linker).unwrap(); let shim_mod = shim_module(linker.engine()); let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); @@ -67,7 +66,7 @@ fn test_sync_host_func() { fn test_async_host_func() { let mut store = async_store(); let mut linker = Linker::new(store.engine()); - add_atoms_to_linker(&mut linker).unwrap(); + add_to_linker(&mut linker).unwrap(); let shim_mod = shim_module(linker.engine()); let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); diff --git a/crates/wiggle/wasmtime/tests/atoms_sync.rs b/crates/wiggle/wasmtime/tests/atoms_sync.rs index 2d6a1a579f4b..416917d2d4c1 100644 --- a/crates/wiggle/wasmtime/tests/atoms_sync.rs +++ b/crates/wiggle/wasmtime/tests/atoms_sync.rs @@ -11,7 +11,6 @@ wasmtime_wiggle::wasmtime_integration!({ target: crate, witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], ctx: Ctx, - modules: { atoms => { name: atoms } }, block_on: { atoms::double_int_return_float } @@ -42,7 +41,7 @@ impl atoms::Atoms for Ctx { fn test_sync_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - add_atoms_to_linker(&mut linker).unwrap(); + add_to_linker(&mut linker).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); @@ -65,7 +64,7 @@ fn test_sync_host_func() { fn test_async_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - add_atoms_to_linker(&mut linker).unwrap(); + add_to_linker(&mut linker).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); From 7387f4ac1c1837be8aa301ac7b7bc5fa1d02db58 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 19 May 2021 14:48:53 -0700 Subject: [PATCH 50/90] wasmtime-wiggle: eliminate ctx argument to macro --- crates/wiggle/wasmtime/macro/src/config.rs | 28 --------------------- crates/wiggle/wasmtime/macro/src/lib.rs | 27 +++++++++----------- crates/wiggle/wasmtime/tests/atoms_async.rs | 1 - crates/wiggle/wasmtime/tests/atoms_sync.rs | 1 - 4 files changed, 12 insertions(+), 45 deletions(-) diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index c6eea76d3a8d..64827f4a042f 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -13,7 +13,6 @@ use { pub struct Config { pub target: TargetConf, pub witx: WitxConf, - pub ctx: CtxConf, pub async_: AsyncConf, } @@ -21,7 +20,6 @@ pub struct Config { pub enum ConfigField { Target(TargetConf), Witx(WitxConf), - Ctx(CtxConf), Async(AsyncConf), } @@ -29,7 +27,6 @@ mod kw { syn::custom_keyword!(target); syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); - syn::custom_keyword!(ctx); syn::custom_keyword!(block_on); } @@ -48,10 +45,6 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?))) - } else if lookahead.peek(kw::ctx) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Ctx(input.parse()?)) } else if lookahead.peek(Token![async]) { input.parse::()?; input.parse::()?; @@ -76,7 +69,6 @@ impl Config { pub fn build(fields: impl Iterator, err_loc: Span) -> Result { let mut target = None; let mut witx = None; - let mut ctx = None; let mut async_ = None; for f in fields { match f { @@ -92,12 +84,6 @@ impl Config { } witx = Some(c); } - ConfigField::Ctx(c) => { - if ctx.is_some() { - return Err(Error::new(err_loc, "duplicate `ctx` field")); - } - ctx = Some(c); - } ConfigField::Async(c) => { if async_.is_some() { return Err(Error::new(err_loc, "duplicate `async` field")); @@ -109,7 +95,6 @@ impl Config { Ok(Config { target: target.ok_or_else(|| Error::new(err_loc, "`target` field required"))?, witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, - ctx: ctx.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, async_: async_.unwrap_or_default(), }) } @@ -134,19 +119,6 @@ impl Parse for Config { } } -#[derive(Debug, Clone)] -pub struct CtxConf { - pub name: syn::Type, -} - -impl Parse for CtxConf { - fn parse(input: ParseStream) -> Result { - Ok(CtxConf { - name: input.parse()?, - }) - } -} - #[derive(Debug, Clone)] pub struct TargetConf { pub path: Path, diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index c0d16364d38e..5605ba50214b 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,7 +6,7 @@ use wiggle_generate::Names; mod config; -use config::{AsyncConf, Asyncness, TargetConf}; +use config::{AsyncConf, Asyncness}; /// Define the structs required to integrate a Wiggle implementation with Wasmtime. /// @@ -26,25 +26,21 @@ pub fn wasmtime_integration(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = Names::new(quote!(wasmtime_wiggle)); - let modules = doc.modules().map(|module| { - generate_module( - &module, - &names, - &config.target, - &config.ctx.name, - &config.async_, - ) - }); + let modules = doc + .modules() + .map(|module| generate_module(&module, &names, &config.target.path, &config.async_)); quote!( #(#modules)* ).into() } fn generate_module( module: &witx::Module, names: &Names, - target_conf: &TargetConf, - ctx_type: &syn::Type, + target_path: &syn::Path, async_conf: &AsyncConf, ) -> TokenStream2 { + let module_ident = names.module(&module.name); + let trait_ident = names.trait_name(&module.name); + let send_bound = if async_conf.contains_async(module) { quote! { + Send } } else { @@ -53,14 +49,15 @@ fn generate_module( let bodies = module.funcs().map(|f| { let asyncness = async_conf.is_async(module.name.as_str(), f.name.as_str()); - generate_func(&module, &f, names, &target_conf.path, asyncness) + generate_func(&module, &f, names, &target_path, asyncness) }); quote! { /// Adds all instance items to the specified `Linker`. - pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> + pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> where - T: std::borrow::BorrowMut<#ctx_type> #send_bound + T: std::borrow::BorrowMut #send_bound, + C: #target_path::#module_ident::#trait_ident #send_bound, { #(#bodies)* Ok(()) diff --git a/crates/wiggle/wasmtime/tests/atoms_async.rs b/crates/wiggle/wasmtime/tests/atoms_async.rs index a022acfc830f..65a5ae98d711 100644 --- a/crates/wiggle/wasmtime/tests/atoms_async.rs +++ b/crates/wiggle/wasmtime/tests/atoms_async.rs @@ -13,7 +13,6 @@ wasmtime_wiggle::from_witx!({ wasmtime_wiggle::wasmtime_integration!({ target: crate, witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - ctx: Ctx, async: { atoms::double_int_return_float } diff --git a/crates/wiggle/wasmtime/tests/atoms_sync.rs b/crates/wiggle/wasmtime/tests/atoms_sync.rs index 416917d2d4c1..4cfd0e6d006c 100644 --- a/crates/wiggle/wasmtime/tests/atoms_sync.rs +++ b/crates/wiggle/wasmtime/tests/atoms_sync.rs @@ -10,7 +10,6 @@ wasmtime_wiggle::from_witx!({ wasmtime_wiggle::wasmtime_integration!({ target: crate, witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - ctx: Ctx, block_on: { atoms::double_int_return_float } From 983a46c0e9449ea2dd2967e8b74abac103a143f9 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 19 May 2021 16:37:37 -0700 Subject: [PATCH 51/90] wiggle: port in wiggle-wasmtimes AsyncConf --- .../wiggle/generate/src/codegen_settings.rs | 7 ++- crates/wiggle/generate/src/config.rs | 62 +++++++++++++++++-- crates/wiggle/generate/src/funcs.rs | 4 +- crates/wiggle/generate/src/module_trait.rs | 2 +- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/crates/wiggle/generate/src/codegen_settings.rs b/crates/wiggle/generate/src/codegen_settings.rs index c51c87e9dde7..13d51c796617 100644 --- a/crates/wiggle/generate/src/codegen_settings.rs +++ b/crates/wiggle/generate/src/codegen_settings.rs @@ -6,6 +6,8 @@ use std::collections::HashMap; use std::rc::Rc; use witx::{Document, Id, InterfaceFunc, Module, NamedType, TypeRef}; +pub use crate::config::Asyncness; + pub struct CodegenSettings { pub errors: ErrorTransform, async_: AsyncConf, @@ -18,9 +20,8 @@ impl CodegenSettings { async_: async_.clone(), }) } - pub fn is_async(&self, module: &Module, func: &InterfaceFunc) -> bool { - self.async_ - .is_async(module.name.as_str(), func.name.as_str()) + pub fn get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness { + self.async_.get(module.name.as_str(), func.name.as_str()) } } diff --git a/crates/wiggle/generate/src/config.rs b/crates/wiggle/generate/src/config.rs index bed0e5c834e1..73bac9dca59e 100644 --- a/crates/wiggle/generate/src/config.rs +++ b/crates/wiggle/generate/src/config.rs @@ -26,6 +26,7 @@ pub enum ConfigField { mod kw { syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); + syn::custom_keyword!(block_on); syn::custom_keyword!(errors); } @@ -48,6 +49,14 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::Async(AsyncConf { + blocking: false, + functions: input.parse()?, + })) + } else if lookahead.peek(kw::block_on) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::Async(AsyncConf { + blocking: true, functions: input.parse()?, })) } else { @@ -284,9 +293,29 @@ impl Parse for ErrorConfField { #[derive(Clone, Default, Debug)] /// Modules and funcs that have async signatures pub struct AsyncConf { + blocking: bool, functions: AsyncFunctions, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Asyncness { + /// Wiggle function is synchronous, wasmtime Func is synchronous + Sync, + /// Wiggle function is asynchronous, but wasmtime Func is synchronous + Blocking, + /// Wiggle function and wasmtime Func are asynchronous. + Async, +} + +impl Asyncness { + pub fn is_async(&self) -> bool { + match self { + Self::Blocking | Self::Async => true, + Self::Sync => false, + } + } +} + #[derive(Clone, Debug)] pub enum AsyncFunctions { Some(HashMap>), @@ -299,14 +328,35 @@ impl Default for AsyncFunctions { } impl AsyncConf { - pub fn is_async(&self, module: &str, function: &str) -> bool { + pub fn get(&self, module: &str, function: &str) -> Asyncness { + let a = if self.blocking { + Asyncness::Blocking + } else { + Asyncness::Async + }; match &self.functions { - AsyncFunctions::Some(fs) => fs - .get(module) - .and_then(|fs| fs.iter().find(|f| *f == function)) - .is_some(), - AsyncFunctions::All => true, + AsyncFunctions::Some(fs) => { + if fs + .get(module) + .and_then(|fs| fs.iter().find(|f| *f == function)) + .is_some() + { + a + } else { + Asyncness::Sync + } + } + AsyncFunctions::All => a, + } + } + + pub fn contains_async(&self, module: &witx::Module) -> bool { + for f in module.funcs() { + if self.get(module.name.as_str(), f.name.as_str()).is_async() { + return true; + } } + false } } diff --git a/crates/wiggle/generate/src/funcs.rs b/crates/wiggle/generate/src/funcs.rs index 33998ee57bd4..1cf28caca942 100644 --- a/crates/wiggle/generate/src/funcs.rs +++ b/crates/wiggle/generate/src/funcs.rs @@ -53,7 +53,7 @@ pub fn define_func( }, ); - let asyncness = if settings.is_async(&module, &func) { + let asyncness = if settings.get_async(&module, &func).is_async() { quote!(async) } else { quote!() @@ -222,7 +222,7 @@ impl witx::Bindgen for Rust<'_> { let trait_name = self.names.trait_name(&self.module.name); let ident = self.names.func(&func.name); - if self.settings.is_async(&self.module, &func) { + if self.settings.get_async(&self.module, &func).is_async() { self.src.extend(quote! { let ret = #trait_name::#ident(ctx, #(#args),*).await; }) diff --git a/crates/wiggle/generate/src/module_trait.rs b/crates/wiggle/generate/src/module_trait.rs index 24995600b21e..402ac004226d 100644 --- a/crates/wiggle/generate/src/module_trait.rs +++ b/crates/wiggle/generate/src/module_trait.rs @@ -75,7 +75,7 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings _ => unimplemented!(), }; - let asyncness = if settings.is_async(&m, &f) { + let asyncness = if settings.get_async(&m, &f).is_async() { quote!(async) } else { quote!() From e1edf6cbf98f6fc15b57f5c9e6f001593a07b344 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 19 May 2021 17:26:11 -0700 Subject: [PATCH 52/90] all wasmtime-wiggle functionality is available through wiggle crate. --- Cargo.lock | 2 + crates/wiggle/Cargo.toml | 19 +- .../wiggle/generate/src/codegen_settings.rs | 9 +- crates/wiggle/generate/src/config.rs | 112 +++++++- crates/wiggle/generate/src/lib.rs | 3 +- crates/wiggle/generate/src/wasmtime.rs | 155 +++++++++++ crates/wiggle/macro/Cargo.toml | 1 + crates/wiggle/macro/src/lib.rs | 30 +- crates/wiggle/src/borrow.rs | 259 ++++++++++++++++++ crates/wiggle/src/lib.rs | 7 + crates/wiggle/src/wasmtime.rs | 54 ++++ crates/wiggle/tests/wasmtime_async.rs | 168 ++++++++++++ crates/wiggle/tests/wasmtime_sync.rs | 130 +++++++++ 13 files changed, 936 insertions(+), 13 deletions(-) create mode 100644 crates/wiggle/generate/src/wasmtime.rs create mode 100644 crates/wiggle/src/borrow.rs create mode 100644 crates/wiggle/src/wasmtime.rs create mode 100644 crates/wiggle/tests/wasmtime_async.rs create mode 100644 crates/wiggle/tests/wasmtime_sync.rs diff --git a/Cargo.lock b/Cargo.lock index f99996ba61cc..d9c25f10e809 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3928,11 +3928,13 @@ dependencies = [ name = "wiggle" version = "0.26.0" dependencies = [ + "anyhow", "async-trait", "bitflags", "proptest", "thiserror", "tracing", + "wasmtime", "wiggle-macro", "wiggle-test", "witx", diff --git a/crates/wiggle/Cargo.toml b/crates/wiggle/Cargo.toml index d0c4709ac86b..c476eed1beca 100644 --- a/crates/wiggle/Cargo.toml +++ b/crates/wiggle/Cargo.toml @@ -17,14 +17,26 @@ wiggle-macro = { path = "macro", version = "0.26.0" } tracing = "0.1.15" bitflags = "1.2" async-trait = "0.1.42" +wasmtime = { path = "../wasmtime", version = "0.26.0", optional = true } [badges] maintenance = { status = "actively-developed" } [dev-dependencies] wiggle-test = { path = "test-helpers" } +anyhow = "1" proptest = "1.0.0" +[[test]] +name = "wasmtime_async" +path = "tests/wasmtime_async.rs" +required-features = ["wasmtime_async", "wasmtime/wat"] + +[[test]] +name = "wasmtime_sync" +path = "tests/wasmtime_sync.rs" +required-features = ["wasmtime_integration", "wasmtime/wat"] + [features] # The wiggle proc-macro emits some code (inside `pub mod metadata`) guarded # by the `wiggle_metadata` feature flag. We use this feature flag so that @@ -38,4 +50,9 @@ wiggle_metadata = ['witx', "wiggle-macro/wiggle_metadata"] # the logs out of wiggle-generated libraries. tracing_log = [ "tracing/log" ] -default = ["wiggle_metadata" ] +# Generate adapters for wasmtime, and expose the wasmtime_integration macro. +wasmtime_integration = [ "wasmtime", "wiggle-macro/wasmtime" ] +# Support for async in the wasmtime crates. +wasmtime_async = [ "wasmtime_integration", "wasmtime/async" ] + +default = ["wiggle_metadata", "wasmtime_integration" ] diff --git a/crates/wiggle/generate/src/codegen_settings.rs b/crates/wiggle/generate/src/codegen_settings.rs index 13d51c796617..4351e95a9796 100644 --- a/crates/wiggle/generate/src/codegen_settings.rs +++ b/crates/wiggle/generate/src/codegen_settings.rs @@ -11,13 +11,20 @@ pub use crate::config::Asyncness; pub struct CodegenSettings { pub errors: ErrorTransform, async_: AsyncConf, + pub wasmtime: bool, } impl CodegenSettings { - pub fn new(error_conf: &ErrorConf, async_: &AsyncConf, doc: &Document) -> Result { + pub fn new( + error_conf: &ErrorConf, + async_: &AsyncConf, + doc: &Document, + wasmtime: bool, + ) -> Result { let errors = ErrorTransform::new(error_conf, doc)?; Ok(Self { errors, async_: async_.clone(), + wasmtime, }) } pub fn get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness { diff --git a/crates/wiggle/generate/src/config.rs b/crates/wiggle/generate/src/config.rs index 73bac9dca59e..73db2fc5f31d 100644 --- a/crates/wiggle/generate/src/config.rs +++ b/crates/wiggle/generate/src/config.rs @@ -16,18 +16,19 @@ pub struct Config { pub async_: AsyncConf, } -#[derive(Debug, Clone)] -pub enum ConfigField { - Witx(WitxConf), - Error(ErrorConf), - Async(AsyncConf), -} - mod kw { syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); syn::custom_keyword!(block_on); syn::custom_keyword!(errors); + syn::custom_keyword!(target); +} + +#[derive(Debug, Clone)] +pub enum ConfigField { + Witx(WitxConf), + Error(ErrorConf), + Async(AsyncConf), } impl Parse for ConfigField { @@ -314,6 +315,9 @@ impl Asyncness { Self::Sync => false, } } + pub fn is_sync(&self) -> bool { + !self.is_async() + } } #[derive(Clone, Debug)] @@ -428,3 +432,97 @@ impl Parse for AsyncConfField { } } } + +#[derive(Clone)] +pub struct WasmtimeConfig { + pub c: Config, + pub target: syn::Path, +} + +#[derive(Clone)] +pub enum WasmtimeConfigField { + Core(ConfigField), + Target(syn::Path), +} +impl WasmtimeConfig { + pub fn build(fields: impl Iterator, err_loc: Span) -> Result { + let mut target = None; + let mut cs = Vec::new(); + for f in fields { + match f { + WasmtimeConfigField::Target(c) => { + if target.is_some() { + return Err(Error::new(err_loc, "duplicate `target` field")); + } + target = Some(c); + } + WasmtimeConfigField::Core(c) => cs.push(c), + } + } + let c = Config::build(cs.into_iter(), err_loc)?; + Ok(WasmtimeConfig { + c, + target: target + .take() + .ok_or_else(|| Error::new(err_loc, "`target` field required"))?, + }) + } +} + +impl Parse for WasmtimeConfig { + fn parse(input: ParseStream) -> Result { + let contents; + let _lbrace = braced!(contents in input); + let fields: Punctuated = + contents.parse_terminated(WasmtimeConfigField::parse)?; + Ok(WasmtimeConfig::build(fields.into_iter(), input.span())?) + } +} + +impl Parse for WasmtimeConfigField { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(kw::target) { + input.parse::()?; + input.parse::()?; + Ok(WasmtimeConfigField::Target(input.parse()?)) + + // The remainder of this function is the ConfigField impl, wrapped in + // WasmtimeConfigField::Core. This is required to get the correct lookahead error. + } else if lookahead.peek(kw::witx) { + input.parse::()?; + input.parse::()?; + Ok(WasmtimeConfigField::Core(ConfigField::Witx( + WitxConf::Paths(input.parse()?), + ))) + } else if lookahead.peek(kw::witx_literal) { + input.parse::()?; + input.parse::()?; + Ok(WasmtimeConfigField::Core(ConfigField::Witx( + WitxConf::Literal(input.parse()?), + ))) + } else if lookahead.peek(kw::errors) { + input.parse::()?; + input.parse::()?; + Ok(WasmtimeConfigField::Core(ConfigField::Error( + input.parse()?, + ))) + } else if lookahead.peek(Token![async]) { + input.parse::()?; + input.parse::()?; + Ok(WasmtimeConfigField::Core(ConfigField::Async(AsyncConf { + blocking: false, + functions: input.parse()?, + }))) + } else if lookahead.peek(kw::block_on) { + input.parse::()?; + input.parse::()?; + Ok(WasmtimeConfigField::Core(ConfigField::Async(AsyncConf { + blocking: true, + functions: input.parse()?, + }))) + } else { + Err(lookahead.error()) + } + } +} diff --git a/crates/wiggle/generate/src/lib.rs b/crates/wiggle/generate/src/lib.rs index 66ecf4baccdc..4a21134ec0f4 100644 --- a/crates/wiggle/generate/src/lib.rs +++ b/crates/wiggle/generate/src/lib.rs @@ -5,6 +5,7 @@ mod lifetimes; mod module_trait; mod names; mod types; +pub mod wasmtime; use heck::ShoutySnakeCase; use lifetimes::anon_lifetime; @@ -12,7 +13,7 @@ use proc_macro2::{Literal, TokenStream}; use quote::quote; pub use codegen_settings::{CodegenSettings, UserErrorType}; -pub use config::Config; +pub use config::{Config, WasmtimeConfig}; pub use funcs::define_func; pub use module_trait::define_module_trait; pub use names::Names; diff --git a/crates/wiggle/generate/src/wasmtime.rs b/crates/wiggle/generate/src/wasmtime.rs new file mode 100644 index 000000000000..78bb23e076a7 --- /dev/null +++ b/crates/wiggle/generate/src/wasmtime.rs @@ -0,0 +1,155 @@ +use crate::config::{AsyncConf, Asyncness}; +use crate::Names; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{format_ident, quote}; + +pub fn link_module( + module: &witx::Module, + names: &Names, + target_path: &syn::Path, + async_conf: &AsyncConf, +) -> TokenStream { + let module_ident = names.module(&module.name); + let trait_ident = names.trait_name(&module.name); + + let send_bound = if async_conf.contains_async(module) { + quote! { + Send } + } else { + quote! {} + }; + + let bodies = module.funcs().map(|f| { + let asyncness = async_conf.get(module.name.as_str(), f.name.as_str()); + generate_func(&module, &f, names, &target_path, asyncness) + }); + + quote! { + /// Adds all instance items to the specified `Linker`. + pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> + where + T: std::borrow::BorrowMut #send_bound, + C: #target_path::#module_ident::#trait_ident #send_bound, + { + #(#bodies)* + Ok(()) + } + } +} + +fn generate_func( + module: &witx::Module, + func: &witx::InterfaceFunc, + names: &Names, + target_path: &syn::Path, + asyncness: Asyncness, +) -> TokenStream { + let rt = names.runtime_mod(); + + let module_str = module.name.as_str(); + let module_ident = names.module(&module.name); + + let field_str = func.name.as_str(); + let field_ident = names.func(&func.name); + + let (params, results) = func.wasm_signature(); + + let arg_names = (0..params.len()) + .map(|i| Ident::new(&format!("arg{}", i), Span::call_site())) + .collect::>(); + let arg_decls = params + .iter() + .enumerate() + .map(|(i, ty)| { + let name = &arg_names[i]; + let wasm = names.wasm_type(*ty); + quote! { #name: #wasm } + }) + .collect::>(); + + let ret_ty = match results.len() { + 0 => quote!(()), + 1 => names.wasm_type(results[0]), + _ => unimplemented!(), + }; + + let await_ = if asyncness.is_sync() { + quote!() + } else { + quote!(.await) + }; + + let runtime = names.runtime_mod(); + + let body = quote! { + let mem = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + return Err(wasmtime::Trap::new("missing required memory export")); + } + }; + // Note the unsafety here. Our goal is to simultaneously borrow the + // memory and custom data from `caller`, and the store it's connected + // to. Rust will not let us do that, however, because we must call two + // separate methods (both of which borrow the whole `caller`) and one of + // our borrows is mutable (the custom data). + // + // This operation, however, is safe because these borrows do not overlap + // and in the process of borrowing them mutability doesn't actually + // touch anything. This is akin to mutably borrowing two indices in an + // array, which is safe so long as the indices are separate. + // + // TODO: depending on how common this is for other users to run into we + // may wish to consider adding a dedicated method for this. For now the + // future of `GuestPtr` may be a bit hazy, so let's just get this + // working from the previous iteration for now. + let (ctx, mem) = unsafe { + let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); + (caller.data_mut().borrow_mut(), #runtime::wasmtime::WasmtimeGuestMemory::new(mem)) + }; + match #target_path::#module_ident::#field_ident(ctx, &mem #(, #arg_names)*) #await_ { + Ok(r) => Ok(<#ret_ty>::from(r)), + Err(#runtime::Trap::String(err)) => Err(wasmtime::Trap::new(err)), + Err(#runtime::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), + } + }; + + match asyncness { + Asyncness::Async => { + let wrapper = format_ident!("func_wrap{}_async", params.len()); + quote! { + linker.#wrapper( + #module_str, + #field_str, + move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| { + Box::new(async move { #body }) + }, + )?; + } + } + + Asyncness::Blocking => { + quote! { + linker.func_wrap( + #module_str, + #field_str, + move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { + let result = async { #body }; + #rt::run_in_dummy_executor(result) + }, + )?; + } + } + + Asyncness::Sync => { + quote! { + linker.func_wrap( + #module_str, + #field_str, + move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { + #body + }, + )?; + } + } + } +} diff --git a/crates/wiggle/macro/Cargo.toml b/crates/wiggle/macro/Cargo.toml index 93c6c03c2408..90ec1c0a5d07 100644 --- a/crates/wiggle/macro/Cargo.toml +++ b/crates/wiggle/macro/Cargo.toml @@ -31,4 +31,5 @@ proc-macro2 = "1.0" wiggle = { path = ".." } [features] +wasmtime = [] wiggle_metadata = [] diff --git a/crates/wiggle/macro/src/lib.rs b/crates/wiggle/macro/src/lib.rs index a386ea2626d4..4c40d975faf8 100644 --- a/crates/wiggle/macro/src/lib.rs +++ b/crates/wiggle/macro/src/lib.rs @@ -144,9 +144,13 @@ pub fn from_witx(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = wiggle_generate::Names::new(quote!(wiggle)); - let error_transform = - wiggle_generate::CodegenSettings::new(&config.errors, &config.async_, &doc) - .expect("validating codegen settings"); + let error_transform = wiggle_generate::CodegenSettings::new( + &config.errors, + &config.async_, + &doc, + cfg!(feature = "wasmtime"), + ) + .expect("validating codegen settings"); let code = wiggle_generate::generate(&doc, &names, &error_transform); let metadata = if cfg!(feature = "wiggle_metadata") { @@ -167,3 +171,23 @@ pub fn async_trait(attr: TokenStream, item: TokenStream) -> TokenStream { #item }) } + +#[cfg(feature = "wasmtime")] +/// Define the structs required to integrate a Wiggle implementation with Wasmtime. +/// +/// ## Arguments +/// +/// Arguments are provided using struct syntax e.g. `{ arg_name: value }`. +/// +/// * `target`: The path of the module where the Wiggle implementation is defined. +#[proc_macro] +pub fn wasmtime_integration(args: TokenStream) -> TokenStream { + let config = parse_macro_input!(args as wiggle_generate::WasmtimeConfig); + let doc = config.c.load_document(); + let names = wiggle_generate::Names::new(quote!(wiggle)); + + let modules = doc.modules().map(|module| { + wiggle_generate::wasmtime::link_module(&module, &names, &config.target, &config.c.async_) + }); + quote!( #(#modules)* ).into() +} diff --git a/crates/wiggle/src/borrow.rs b/crates/wiggle/src/borrow.rs new file mode 100644 index 000000000000..cf42757a6541 --- /dev/null +++ b/crates/wiggle/src/borrow.rs @@ -0,0 +1,259 @@ +use std::collections::HashMap; +use std::sync::Mutex; +use crate::{BorrowHandle, GuestError, Region}; + +pub struct BorrowChecker { + /// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking + /// overlap, the method calls on this member will be confusing. + /// + /// Also, unfortunately, for now this uses a `Mutex`. The reason for that is + /// that this is shared as `&BorrowChecker` in a bunch of `GuestPtr` values. + /// Through this sharing we still want each `GuestPtr` to be `Send` and the + /// "naive" way to make `&T` `Send` with interior mutability is to use a + /// `Mutex`. Fixing this will likely require rethinking `GuestPtr` one way + /// or another. That needs to happen for other reasons as well (for example + /// to allow for wasm calls to happen while `GuestPtr` values are active), + /// so it's hoped that in a later rethinking of `GuestPtr` we can revisit + /// this and remove this `Mutex`. + bc: Mutex, +} + +impl BorrowChecker { + /// A `BorrowChecker` manages run-time validation of borrows from a + /// `GuestMemory`. It keeps track of regions of guest memory which are + /// possible to alias with Rust references (via the `GuestSlice` and + /// `GuestStr` structs, which implement `std::ops::Deref` and + /// `std::ops::DerefMut`. It also enforces that `GuestPtr::read` + /// does not access memory with an outstanding mutable borrow, and + /// `GuestPtr::write` does not access memory with an outstanding + /// shared or mutable borrow. + pub fn new() -> Self { + BorrowChecker { + bc: Mutex::new(InnerBorrowChecker::new()), + } + } + /// Indicates whether any outstanding shared or mutable borrows are known + /// to the `BorrowChecker`. This function must be `false` in order for it + /// to be safe to recursively call into a WebAssembly module, or to + /// manipulate the WebAssembly memory by any other means. + pub fn has_outstanding_borrows(&self) -> bool { + self.bc.lock().unwrap().has_outstanding_borrows() + } + pub fn shared_borrow(&self, r: Region) -> Result { + self.bc.lock().unwrap().shared_borrow(r) + } + pub fn mut_borrow(&self, r: Region) -> Result { + self.bc.lock().unwrap().mut_borrow(r) + } + pub fn shared_unborrow(&self, h: BorrowHandle) { + self.bc.lock().unwrap().shared_unborrow(h) + } + pub fn mut_unborrow(&self, h: BorrowHandle) { + self.bc.lock().unwrap().mut_unborrow(h) + } + pub fn is_shared_borrowed(&self, r: Region) -> bool { + self.bc.lock().unwrap().is_shared_borrowed(r) + } + pub fn is_mut_borrowed(&self, r: Region) -> bool { + self.bc.lock().unwrap().is_mut_borrowed(r) + } +} + +#[derive(Debug)] +/// This is a pretty naive way to account for borrows. This datastructure +/// could be made a lot more efficient with some effort. +struct InnerBorrowChecker { + /// Maps from handle to region borrowed. A HashMap is probably not ideal + /// for this but it works. It would be more efficient if we could + /// check `is_borrowed` without an O(n) iteration, by organizing borrows + /// by an ordering of Region. + shared_borrows: HashMap, + mut_borrows: HashMap, + /// Handle to give out for the next borrow. This is the bare minimum of + /// bookkeeping of free handles, and in a pathological case we could run + /// out, hence [`GuestError::BorrowCheckerOutOfHandles`] + next_handle: BorrowHandle, +} + +impl InnerBorrowChecker { + fn new() -> Self { + InnerBorrowChecker { + shared_borrows: HashMap::new(), + mut_borrows: HashMap::new(), + next_handle: BorrowHandle(0), + } + } + + fn has_outstanding_borrows(&self) -> bool { + !(self.shared_borrows.is_empty() && self.mut_borrows.is_empty()) + } + + fn is_shared_borrowed(&self, r: Region) -> bool { + self.shared_borrows.values().any(|b| b.overlaps(r)) + } + fn is_mut_borrowed(&self, r: Region) -> bool { + self.mut_borrows.values().any(|b| b.overlaps(r)) + } + + fn new_handle(&mut self) -> Result { + // Reset handles to 0 if all handles have been returned. + if self.shared_borrows.is_empty() && self.mut_borrows.is_empty() { + self.next_handle = BorrowHandle(0); + } + let h = self.next_handle; + // Get the next handle. Since we don't recycle handles until all of + // them have been returned, there is a pathological case where a user + // may make a Very Large (usize::MAX) number of valid borrows and + // unborrows while always keeping at least one borrow outstanding, and + // we will run out of borrow handles. + self.next_handle = BorrowHandle( + h.0.checked_add(1) + .ok_or_else(|| GuestError::BorrowCheckerOutOfHandles)?, + ); + Ok(h) + } + + fn shared_borrow(&mut self, r: Region) -> Result { + if self.is_mut_borrowed(r) { + return Err(GuestError::PtrBorrowed(r)); + } + let h = self.new_handle()?; + self.shared_borrows.insert(h, r); + Ok(h) + } + + fn mut_borrow(&mut self, r: Region) -> Result { + if self.is_shared_borrowed(r) || self.is_mut_borrowed(r) { + return Err(GuestError::PtrBorrowed(r)); + } + let h = self.new_handle()?; + self.mut_borrows.insert(h, r); + Ok(h) + } + + fn shared_unborrow(&mut self, h: BorrowHandle) { + let removed = self.shared_borrows.remove(&h); + debug_assert!(removed.is_some(), "double-freed shared borrow"); + } + + fn mut_unborrow(&mut self, h: BorrowHandle) { + let removed = self.mut_borrows.remove(&h); + debug_assert!(removed.is_some(), "double-freed mut borrow"); + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn nonoverlapping() { + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(10, 10); + assert!(!r1.overlaps(r2)); + bs.mut_borrow(r1).expect("can borrow r1"); + bs.mut_borrow(r2).expect("can borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(10, 10); + let r2 = Region::new(0, 10); + assert!(!r1.overlaps(r2)); + bs.mut_borrow(r1).expect("can borrow r1"); + bs.mut_borrow(r2).expect("can borrow r2"); + } + + #[test] + fn overlapping() { + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(9, 10); + assert!(r1.overlaps(r2)); + bs.shared_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant mut borrow r2"); + bs.shared_borrow(r2).expect("can shared borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(2, 5); + assert!(r1.overlaps(r2)); + bs.shared_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); + bs.shared_borrow(r2).expect("can shared borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(9, 10); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); + bs.shared_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); + bs.shared_borrow(r2).expect("can shared borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(2, 5); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); + bs.shared_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); + bs.shared_borrow(r2).expect("can shared borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(2, 5); + let r2 = Region::new(10, 5); + let r3 = Region::new(15, 5); + let r4 = Region::new(0, 10); + assert!(r1.overlaps(r4)); + bs.shared_borrow(r1).expect("can borrow r1"); + bs.shared_borrow(r2).expect("can borrow r2"); + bs.shared_borrow(r3).expect("can borrow r3"); + assert!(bs.mut_borrow(r4).is_err(), "cant mut borrow r4"); + bs.shared_borrow(r4).expect("can shared borrow r4"); + } + + #[test] + fn unborrowing() { + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(10, 10); + assert!(!r1.overlaps(r2)); + assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); + let h1 = bs.mut_borrow(r1).expect("can borrow r1"); + assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); + let h2 = bs.mut_borrow(r2).expect("can borrow r2"); + + assert!(bs.mut_borrow(r2).is_err(), "can't borrow r2 twice"); + bs.mut_unborrow(h2); + assert_eq!( + bs.has_outstanding_borrows(), + true, + "h1 is still outstanding" + ); + bs.mut_unborrow(h1); + assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); + + let _h3 = bs + .mut_borrow(r2) + .expect("can borrow r2 again now that its been unborrowed"); + + // Lets try again with shared: + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(10, 10); + assert!(!r1.overlaps(r2)); + assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); + let h1 = bs.shared_borrow(r1).expect("can borrow r1"); + assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); + let h2 = bs.shared_borrow(r2).expect("can borrow r2"); + let h3 = bs.shared_borrow(r2).expect("can shared borrow r2 twice"); + + bs.shared_unborrow(h2); + assert_eq!( + bs.has_outstanding_borrows(), + true, + "h1, h3 still outstanding" + ); + bs.shared_unborrow(h1); + bs.shared_unborrow(h3); + assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); + } +} diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index a6e4271325e5..df2c49a4fc21 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -5,11 +5,15 @@ use std::sync::Arc; pub use wiggle_macro::{async_trait, from_witx}; +#[cfg(feature = "wasmtime")] +pub use wiggle_macro::wasmtime_integration; + pub use bitflags; #[cfg(feature = "wiggle_metadata")] pub use witx; +pub mod borrow; mod error; mod guest_type; mod region; @@ -24,6 +28,9 @@ pub mod async_trait_crate { pub use async_trait::*; } +#[cfg(feature = "wasmtime")] +pub mod wasmtime; + /// A trait which abstracts how to get at the region of host memory taht /// contains guest memory. /// diff --git a/crates/wiggle/src/wasmtime.rs b/crates/wiggle/src/wasmtime.rs new file mode 100644 index 000000000000..dd9de48abdeb --- /dev/null +++ b/crates/wiggle/src/wasmtime.rs @@ -0,0 +1,54 @@ +use crate::borrow::BorrowChecker; +use crate::{BorrowHandle, GuestError, GuestMemory, Region}; + +/// Lightweight `wasmtime::Memory` wrapper so we can implement the +/// `wiggle::GuestMemory` trait on it. +pub struct WasmtimeGuestMemory<'a> { + mem: &'a mut [u8], + bc: BorrowChecker, +} + +impl<'a> WasmtimeGuestMemory<'a> { + pub fn new(mem: &'a mut [u8]) -> Self { + Self { + mem, + // Wiggle does not expose any methods for functions to re-enter + // the WebAssembly instance, or expose the memory via non-wiggle + // mechanisms. However, the user-defined code may end up + // re-entering the instance, in which case this is an incorrect + // implementation - we require exactly one BorrowChecker exist per + // instance. + // This BorrowChecker construction is a holdover until it is + // integrated fully with wasmtime: + // https://github.com/bytecodealliance/wasmtime/issues/1917 + bc: BorrowChecker::new(), + } + } +} + +unsafe impl GuestMemory for WasmtimeGuestMemory<'_> { + fn base(&self) -> (*mut u8, u32) { + (self.mem.as_ptr() as *mut u8, self.mem.len() as u32) + } + fn has_outstanding_borrows(&self) -> bool { + self.bc.has_outstanding_borrows() + } + fn is_shared_borrowed(&self, r: Region) -> bool { + self.bc.is_shared_borrowed(r) + } + fn is_mut_borrowed(&self, r: Region) -> bool { + self.bc.is_mut_borrowed(r) + } + fn shared_borrow(&self, r: Region) -> Result { + self.bc.shared_borrow(r) + } + fn mut_borrow(&self, r: Region) -> Result { + self.bc.mut_borrow(r) + } + fn shared_unborrow(&self, h: BorrowHandle) { + self.bc.shared_unborrow(h) + } + fn mut_unborrow(&self, h: BorrowHandle) { + self.bc.mut_unborrow(h) + } +} diff --git a/crates/wiggle/tests/wasmtime_async.rs b/crates/wiggle/tests/wasmtime_async.rs new file mode 100644 index 000000000000..1b1f23d197fd --- /dev/null +++ b/crates/wiggle/tests/wasmtime_async.rs @@ -0,0 +1,168 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; +use wasmtime::{Config, Engine, Linker, Module, Store}; + +wiggle::from_witx!({ + witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], + async: { + atoms::{double_int_return_float} + } +}); + +wiggle::wasmtime_integration!({ + target: crate, + witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], + async: { + atoms::double_int_return_float + } +}); + +pub struct Ctx; +impl wiggle::GuestErrorType for types::Errno { + fn success() -> Self { + types::Errno::Ok + } +} + +#[wiggle::async_trait] +impl atoms::Atoms for Ctx { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("INT FLOAT ARGS: {} {}", an_int, an_float); + Ok(()) + } + async fn double_int_return_float( + &mut self, + an_int: u32, + ) -> Result { + Ok((an_int as f32) * 2.0) + } +} + +#[test] +fn test_sync_host_func() { + let mut store = async_store(); + let mut linker = Linker::new(store.engine()); + add_to_linker(&mut linker).unwrap(); + let shim_mod = shim_module(linker.engine()); + let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); + + let results = run(shim_inst + .get_func(&mut store, "int_float_args_shim") + .unwrap() + .call_async(&mut store, &[0i32.into(), 123.45f32.into()])) + .unwrap(); + + assert_eq!(results.len(), 1, "one return value"); + assert_eq!( + results[0].unwrap_i32(), + types::Errno::Ok as i32, + "int_float_args errno" + ); +} + +#[test] +fn test_async_host_func() { + let mut store = async_store(); + let mut linker = Linker::new(store.engine()); + add_to_linker(&mut linker).unwrap(); + + let shim_mod = shim_module(linker.engine()); + let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); + + let input: i32 = 123; + let result_location: i32 = 0; + + let results = run(shim_inst + .get_func(&mut store, "double_int_return_float_shim") + .unwrap() + .call_async(&mut store, &[input.into(), result_location.into()])) + .unwrap(); + + assert_eq!(results.len(), 1, "one return value"); + assert_eq!( + results[0].unwrap_i32(), + types::Errno::Ok as i32, + "double_int_return_float errno" + ); + + // The actual result is in memory: + let mem = shim_inst.get_memory(&mut store, "memory").unwrap(); + let mut result_bytes: [u8; 4] = [0, 0, 0, 0]; + mem.read(&store, result_location as usize, &mut result_bytes) + .unwrap(); + let result = f32::from_le_bytes(result_bytes); + assert_eq!((input * 2) as f32, result); +} + +fn run(future: F) -> F::Output { + let mut f = Pin::from(Box::new(future)); + let waker = dummy_waker(); + let mut cx = Context::from_waker(&waker); + loop { + match f.as_mut().poll(&mut cx) { + Poll::Ready(val) => break val, + Poll::Pending => {} + } + } +} + +fn dummy_waker() -> Waker { + return unsafe { Waker::from_raw(clone(5 as *const _)) }; + + unsafe fn clone(ptr: *const ()) -> RawWaker { + assert_eq!(ptr as usize, 5); + const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); + RawWaker::new(ptr, &VTABLE) + } + + unsafe fn wake(ptr: *const ()) { + assert_eq!(ptr as usize, 5); + } + + unsafe fn wake_by_ref(ptr: *const ()) { + assert_eq!(ptr as usize, 5); + } + + unsafe fn drop(ptr: *const ()) { + assert_eq!(ptr as usize, 5); + } +} + +fn async_store() -> Store { + Store::new( + &Engine::new(Config::new().async_support(true)).unwrap(), + Ctx, + ) +} + +// Wiggle expects the caller to have an exported memory. Wasmtime can only +// provide this if the caller is a WebAssembly module, so we need to write +// a shim module: +fn shim_module(engine: &Engine) -> Module { + Module::new( + engine, + r#" + (module + (memory 1) + (export "memory" (memory 0)) + (import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32))) + (import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32))) + + (func $int_float_args_shim (param i32 f32) (result i32) + local.get 0 + local.get 1 + call $int_float_args + ) + (func $double_int_return_float_shim (param i32 i32) (result i32) + local.get 0 + local.get 1 + call $double_int_return_float + ) + (export "int_float_args_shim" (func $int_float_args_shim)) + (export "double_int_return_float_shim" (func $double_int_return_float_shim)) + ) + "#, + ) + .unwrap() +} diff --git a/crates/wiggle/tests/wasmtime_sync.rs b/crates/wiggle/tests/wasmtime_sync.rs new file mode 100644 index 000000000000..0fbd9d9350c6 --- /dev/null +++ b/crates/wiggle/tests/wasmtime_sync.rs @@ -0,0 +1,130 @@ +use wasmtime::{Engine, Linker, Module, Store}; + +wiggle::from_witx!({ + witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], + async: { + atoms::{double_int_return_float} + } +}); + +wiggle::wasmtime_integration!({ + target: crate, + witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], + block_on: { + atoms::double_int_return_float + } +}); + +pub struct Ctx; +impl wiggle::GuestErrorType for types::Errno { + fn success() -> Self { + types::Errno::Ok + } +} + +#[wiggle::async_trait] +impl atoms::Atoms for Ctx { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("INT FLOAT ARGS: {} {}", an_int, an_float); + Ok(()) + } + async fn double_int_return_float( + &mut self, + an_int: u32, + ) -> Result { + Ok((an_int as f32) * 2.0) + } +} + +#[test] +fn test_sync_host_func() { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker).unwrap(); + let mut store = store(&engine); + let shim_mod = shim_module(&engine); + let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); + + let results = shim_inst + .get_func(&mut store, "int_float_args_shim") + .unwrap() + .call(&mut store, &[0i32.into(), 123.45f32.into()]) + .unwrap(); + + assert_eq!(results.len(), 1, "one return value"); + assert_eq!( + results[0].unwrap_i32(), + types::Errno::Ok as i32, + "int_float_args errno" + ); +} + +#[test] +fn test_async_host_func() { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker).unwrap(); + let mut store = store(&engine); + + let shim_mod = shim_module(&engine); + let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); + + let input: i32 = 123; + let result_location: i32 = 0; + + let results = shim_inst + .get_func(&mut store, "double_int_return_float_shim") + .unwrap() + .call(&mut store, &[input.into(), result_location.into()]) + .unwrap(); + + assert_eq!(results.len(), 1, "one return value"); + assert_eq!( + results[0].unwrap_i32(), + types::Errno::Ok as i32, + "double_int_return_float errno" + ); + + // The actual result is in memory: + let mem = shim_inst.get_memory(&mut store, "memory").unwrap(); + let mut result_bytes: [u8; 4] = [0, 0, 0, 0]; + mem.read(&store, result_location as usize, &mut result_bytes) + .unwrap(); + let result = f32::from_le_bytes(result_bytes); + assert_eq!((input * 2) as f32, result); +} + +fn store(engine: &Engine) -> Store { + Store::new(engine, Ctx) +} + +// Wiggle expects the caller to have an exported memory. Wasmtime can only +// provide this if the caller is a WebAssembly module, so we need to write +// a shim module: +fn shim_module(engine: &Engine) -> Module { + Module::new( + engine, + r#" + (module + (memory 1) + (export "memory" (memory 0)) + (import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32))) + (import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32))) + + (func $int_float_args_shim (param i32 f32) (result i32) + local.get 0 + local.get 1 + call $int_float_args + ) + (func $double_int_return_float_shim (param i32 i32) (result i32) + local.get 0 + local.get 1 + call $double_int_return_float + ) + (export "int_float_args_shim" (func $int_float_args_shim)) + (export "double_int_return_float_shim" (func $double_int_return_float_shim)) + ) + "#, + ) + .unwrap() +} From f05aef3f509833edee85fbff24b7cd4fc7b2ae5b Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 19 May 2021 17:29:41 -0700 Subject: [PATCH 53/90] makesure non-default wiggle test features are enabled and delete weird cargo build of wasmtime-wasm... --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c0e84ea1c368..f11caf17bf70 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,7 +54,6 @@ jobs: curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.4.4/mdbook-v0.4.4-x86_64-unknown-linux-gnu.tar.gz | tar xzf - echo `pwd` >> $GITHUB_PATH - run: (cd docs && mdbook build) - - run: cargo build -p wasmtime-wasi --features wasmtime/wat - run: (cd docs && mdbook test -L ../target/debug/deps) - uses: actions/upload-artifact@v1 with: @@ -480,7 +479,7 @@ jobs: # would increase build times here. - run: | $CENTOS cargo test \ - --features test-programs/test_programs \ + --features "test-programs/test_programs wiggle/wasmtime_async wasmtime/wat" \ --release \ --workspace \ --exclude '*lightbeam*' \ From 39c97ac1eae7b18e7374426b3ea6ea456c964f25 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 19 May 2021 18:43:08 -0700 Subject: [PATCH 54/90] various fixes to wasmtime integration in wiggle crate --- crates/wiggle/Cargo.toml | 6 + .../wiggle/generate/src/codegen_settings.rs | 2 +- crates/wiggle/generate/src/funcs.rs | 77 ++++++---- crates/wiggle/generate/src/lib.rs | 7 + crates/wiggle/generate/src/wasmtime.rs | 57 ++++++-- crates/wiggle/macro/src/lib.rs | 14 +- crates/wiggle/tests/wasmtime_async.rs | 12 +- crates/wiggle/tests/wasmtime_integration.rs | 134 ++++++++++++++++++ crates/wiggle/tests/wasmtime_sync.rs | 12 +- 9 files changed, 256 insertions(+), 65 deletions(-) create mode 100644 crates/wiggle/tests/wasmtime_integration.rs diff --git a/crates/wiggle/Cargo.toml b/crates/wiggle/Cargo.toml index c476eed1beca..5b6c4a2302d4 100644 --- a/crates/wiggle/Cargo.toml +++ b/crates/wiggle/Cargo.toml @@ -37,6 +37,12 @@ name = "wasmtime_sync" path = "tests/wasmtime_sync.rs" required-features = ["wasmtime_integration", "wasmtime/wat"] +[[test]] +name = "wasmtime_integration" +path = "tests/wasmtime_integration.rs" +required-features = ["wasmtime_integration", "wasmtime/wat"] + + [features] # The wiggle proc-macro emits some code (inside `pub mod metadata`) guarded # by the `wiggle_metadata` feature flag. We use this feature flag so that diff --git a/crates/wiggle/generate/src/codegen_settings.rs b/crates/wiggle/generate/src/codegen_settings.rs index 4351e95a9796..144d2be282b6 100644 --- a/crates/wiggle/generate/src/codegen_settings.rs +++ b/crates/wiggle/generate/src/codegen_settings.rs @@ -10,7 +10,7 @@ pub use crate::config::Asyncness; pub struct CodegenSettings { pub errors: ErrorTransform, - async_: AsyncConf, + pub async_: AsyncConf, pub wasmtime: bool, } impl CodegenSettings { diff --git a/crates/wiggle/generate/src/funcs.rs b/crates/wiggle/generate/src/funcs.rs index 1cf28caca942..74c2fee4d98e 100644 --- a/crates/wiggle/generate/src/funcs.rs +++ b/crates/wiggle/generate/src/funcs.rs @@ -14,6 +14,26 @@ pub fn define_func( func: &witx::InterfaceFunc, settings: &CodegenSettings, ) -> TokenStream { + let (ts, _bounds) = _define_func(names, module, func, settings); + ts +} + +pub fn func_bounds( + names: &Names, + module: &witx::Module, + func: &witx::InterfaceFunc, + settings: &CodegenSettings, +) -> Vec { + let (_ts, bounds) = _define_func(names, module, func, settings); + bounds +} + +fn _define_func( + names: &Names, + module: &witx::Module, + func: &witx::InterfaceFunc, + settings: &CodegenSettings, +) -> (TokenStream, Vec) { let rt = names.runtime_mod(); let ident = names.func(&func.name); @@ -36,7 +56,7 @@ pub fn define_func( }; let mut body = TokenStream::new(); - let mut required_impls = vec![names.trait_name(&module.name)]; + let mut bounds = vec![names.trait_name(&module.name)]; func.call_interface( &module.name, &mut Rust { @@ -49,7 +69,7 @@ pub fn define_func( module, funcname: func.name.as_str(), settings, - required_impls: &mut required_impls, + bounds: &mut bounds, }, ); @@ -60,26 +80,29 @@ pub fn define_func( }; let mod_name = &module.name.as_str(); let func_name = &func.name.as_str(); - quote! { - #[allow(unreachable_code)] // deals with warnings in noreturn functions - pub #asyncness fn #ident( - ctx: &mut (impl #(#required_impls)+*), - memory: &dyn #rt::GuestMemory, - #(#abi_params),* - ) -> Result<#abi_ret, #rt::Trap> { - use std::convert::TryFrom as _; - - let _span = #rt::tracing::span!( - #rt::tracing::Level::TRACE, - "wiggle abi", - module = #mod_name, - function = #func_name - ); - let _enter = _span.enter(); - - #body - } - } + ( + quote! { + #[allow(unreachable_code)] // deals with warnings in noreturn functions + pub #asyncness fn #ident( + ctx: &mut (impl #(#bounds)+*), + memory: &dyn #rt::GuestMemory, + #(#abi_params),* + ) -> Result<#abi_ret, #rt::Trap> { + use std::convert::TryFrom as _; + + let _span = #rt::tracing::span!( + #rt::tracing::Level::TRACE, + "wiggle abi", + module = #mod_name, + function = #func_name + ); + let _enter = _span.enter(); + + #body + } + }, + bounds, + ) } struct Rust<'a> { @@ -92,13 +115,13 @@ struct Rust<'a> { module: &'a witx::Module, funcname: &'a str, settings: &'a CodegenSettings, - required_impls: &'a mut Vec, + bounds: &'a mut Vec, } impl Rust<'_> { - fn required_impl(&mut self, i: Ident) { - if !self.required_impls.contains(&i) { - self.required_impls.push(i); + fn bound(&mut self, i: Ident) { + if !self.bounds.contains(&i) { + self.bounds.push(i); } } } @@ -254,7 +277,7 @@ impl witx::Bindgen for Rust<'_> { let val = match self.settings.errors.for_name(ty) { Some(custom) => { let method = self.names.user_error_conversion_method(&custom); - self.required_impl(quote::format_ident!("UserErrorConversion")); + self.bound(quote::format_ident!("UserErrorConversion")); quote!(UserErrorConversion::#method(ctx, #val)?) } None => val, diff --git a/crates/wiggle/generate/src/lib.rs b/crates/wiggle/generate/src/lib.rs index 4a21134ec0f4..3b77a3a0d6fa 100644 --- a/crates/wiggle/generate/src/lib.rs +++ b/crates/wiggle/generate/src/lib.rs @@ -56,12 +56,19 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings) .funcs() .map(|f| define_func(&names, &module, &f, &settings)); let modtrait = define_module_trait(&names, &module, &settings); + let wasmtime = if settings.wasmtime { + crate::wasmtime::link_module(&module, &names, None, &settings) + } else { + quote! {} + }; quote!( pub mod #modname { use super::types::*; #(#fs)* #modtrait + + #wasmtime } ) }); diff --git a/crates/wiggle/generate/src/wasmtime.rs b/crates/wiggle/generate/src/wasmtime.rs index 78bb23e076a7..d66856132fd9 100644 --- a/crates/wiggle/generate/src/wasmtime.rs +++ b/crates/wiggle/generate/src/wasmtime.rs @@ -1,34 +1,57 @@ -use crate::config::{AsyncConf, Asyncness}; -use crate::Names; +use crate::config::Asyncness; +use crate::funcs::func_bounds; +use crate::{CodegenSettings, Names}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; +use std::collections::HashSet; pub fn link_module( module: &witx::Module, names: &Names, - target_path: &syn::Path, - async_conf: &AsyncConf, + target_path: Option<&syn::Path>, + settings: &CodegenSettings, ) -> TokenStream { let module_ident = names.module(&module.name); - let trait_ident = names.trait_name(&module.name); - let send_bound = if async_conf.contains_async(module) { + let send_bound = if settings.async_.contains_async(module) { quote! { + Send } } else { quote! {} }; - let bodies = module.funcs().map(|f| { - let asyncness = async_conf.get(module.name.as_str(), f.name.as_str()); - generate_func(&module, &f, names, &target_path, asyncness) - }); + let mut bodies = Vec::new(); + let mut bounds = HashSet::new(); + for f in module.funcs() { + let asyncness = settings.async_.get(module.name.as_str(), f.name.as_str()); + bodies.push(generate_func(&module, &f, names, target_path, asyncness)); + let bound = func_bounds(names, module, &f, settings); + for b in bound { + bounds.insert(b); + } + } + + let ctx_bound = if let Some(target_path) = target_path { + let bounds = bounds + .into_iter() + .map(|b| quote!(#target_path::#module_ident::#b)); + quote!( #(#bounds)+* #send_bound ) + } else { + let bounds = bounds.into_iter(); + quote!( #(#bounds)+* #send_bound ) + }; + + let func_name = if target_path.is_none() { + format_ident!("add_to_linker") + } else { + format_ident!("add_{}_to_linker", module_ident) + }; quote! { /// Adds all instance items to the specified `Linker`. - pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> + pub fn #func_name(linker: &mut wasmtime::Linker) -> anyhow::Result<()> where T: std::borrow::BorrowMut #send_bound, - C: #target_path::#module_ident::#trait_ident #send_bound, + C: #ctx_bound { #(#bodies)* Ok(()) @@ -40,7 +63,7 @@ fn generate_func( module: &witx::Module, func: &witx::InterfaceFunc, names: &Names, - target_path: &syn::Path, + target_path: Option<&syn::Path>, asyncness: Asyncness, ) -> TokenStream { let rt = names.runtime_mod(); @@ -78,6 +101,12 @@ fn generate_func( quote!(.await) }; + let abi_func = if let Some(target_path) = target_path { + quote!( #target_path::#module_ident::#field_ident ) + } else { + quote!( #field_ident ) + }; + let runtime = names.runtime_mod(); let body = quote! { @@ -106,7 +135,7 @@ fn generate_func( let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); (caller.data_mut().borrow_mut(), #runtime::wasmtime::WasmtimeGuestMemory::new(mem)) }; - match #target_path::#module_ident::#field_ident(ctx, &mem #(, #arg_names)*) #await_ { + match #abi_func(ctx, &mem #(, #arg_names)*) #await_ { Ok(r) => Ok(<#ret_ty>::from(r)), Err(#runtime::Trap::String(err)) => Err(wasmtime::Trap::new(err)), Err(#runtime::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), diff --git a/crates/wiggle/macro/src/lib.rs b/crates/wiggle/macro/src/lib.rs index 4c40d975faf8..57ba9c03e754 100644 --- a/crates/wiggle/macro/src/lib.rs +++ b/crates/wiggle/macro/src/lib.rs @@ -144,7 +144,7 @@ pub fn from_witx(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = wiggle_generate::Names::new(quote!(wiggle)); - let error_transform = wiggle_generate::CodegenSettings::new( + let settings = wiggle_generate::CodegenSettings::new( &config.errors, &config.async_, &doc, @@ -152,7 +152,7 @@ pub fn from_witx(args: TokenStream) -> TokenStream { ) .expect("validating codegen settings"); - let code = wiggle_generate::generate(&doc, &names, &error_transform); + let code = wiggle_generate::generate(&doc, &names, &settings); let metadata = if cfg!(feature = "wiggle_metadata") { wiggle_generate::generate_metadata(&doc, &names) } else { @@ -186,8 +186,16 @@ pub fn wasmtime_integration(args: TokenStream) -> TokenStream { let doc = config.c.load_document(); let names = wiggle_generate::Names::new(quote!(wiggle)); + let settings = wiggle_generate::CodegenSettings::new( + &config.c.errors, + &config.c.async_, + &doc, + cfg!(feature = "wasmtime"), + ) + .expect("validating codegen settings"); + let modules = doc.modules().map(|module| { - wiggle_generate::wasmtime::link_module(&module, &names, &config.target, &config.c.async_) + wiggle_generate::wasmtime::link_module(&module, &names, Some(&config.target), &settings) }); quote!( #(#modules)* ).into() } diff --git a/crates/wiggle/tests/wasmtime_async.rs b/crates/wiggle/tests/wasmtime_async.rs index 1b1f23d197fd..41ccd12f15b0 100644 --- a/crates/wiggle/tests/wasmtime_async.rs +++ b/crates/wiggle/tests/wasmtime_async.rs @@ -10,14 +10,6 @@ wiggle::from_witx!({ } }); -wiggle::wasmtime_integration!({ - target: crate, - witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - async: { - atoms::double_int_return_float - } -}); - pub struct Ctx; impl wiggle::GuestErrorType for types::Errno { fn success() -> Self { @@ -43,7 +35,7 @@ impl atoms::Atoms for Ctx { fn test_sync_host_func() { let mut store = async_store(); let mut linker = Linker::new(store.engine()); - add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker).unwrap(); let shim_mod = shim_module(linker.engine()); let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); @@ -65,7 +57,7 @@ fn test_sync_host_func() { fn test_async_host_func() { let mut store = async_store(); let mut linker = Linker::new(store.engine()); - add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker).unwrap(); let shim_mod = shim_module(linker.engine()); let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); diff --git a/crates/wiggle/tests/wasmtime_integration.rs b/crates/wiggle/tests/wasmtime_integration.rs new file mode 100644 index 000000000000..f222260647d5 --- /dev/null +++ b/crates/wiggle/tests/wasmtime_integration.rs @@ -0,0 +1,134 @@ +use wasmtime::{Engine, Linker, Module, Store}; + +// from_witx invocation says the func is async. This context doesn't support async! +wiggle::from_witx!({ + witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], + async: { + atoms::{double_int_return_float} + } +}); + +pub mod integration { + // The integration invocation says the func is blocking, so it will still work. + wiggle::wasmtime_integration!({ + target: crate, + witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], + block_on: { + atoms::{double_int_return_float} + } + }); +} + +pub struct Ctx; +impl wiggle::GuestErrorType for types::Errno { + fn success() -> Self { + types::Errno::Ok + } +} + +#[wiggle::async_trait] +impl atoms::Atoms for Ctx { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("INT FLOAT ARGS: {} {}", an_int, an_float); + Ok(()) + } + async fn double_int_return_float( + &mut self, + an_int: u32, + ) -> Result { + Ok((an_int as f32) * 2.0) + } +} + +#[test] +fn test_sync_host_func() { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + integration::add_atoms_to_linker(&mut linker).unwrap(); + let mut store = store(&engine); + let shim_mod = shim_module(&engine); + let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); + + let results = shim_inst + .get_func(&mut store, "int_float_args_shim") + .unwrap() + .call(&mut store, &[0i32.into(), 123.45f32.into()]) + .unwrap(); + + assert_eq!(results.len(), 1, "one return value"); + assert_eq!( + results[0].unwrap_i32(), + types::Errno::Ok as i32, + "int_float_args errno" + ); +} + +#[test] +fn test_async_host_func() { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + integration::add_atoms_to_linker(&mut linker).unwrap(); + let mut store = store(&engine); + + let shim_mod = shim_module(&engine); + let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); + + let input: i32 = 123; + let result_location: i32 = 0; + + let results = shim_inst + .get_func(&mut store, "double_int_return_float_shim") + .unwrap() + .call(&mut store, &[input.into(), result_location.into()]) + .unwrap(); + + assert_eq!(results.len(), 1, "one return value"); + assert_eq!( + results[0].unwrap_i32(), + types::Errno::Ok as i32, + "double_int_return_float errno" + ); + + // The actual result is in memory: + let mem = shim_inst.get_memory(&mut store, "memory").unwrap(); + let mut result_bytes: [u8; 4] = [0, 0, 0, 0]; + mem.read(&store, result_location as usize, &mut result_bytes) + .unwrap(); + let result = f32::from_le_bytes(result_bytes); + assert_eq!((input * 2) as f32, result); +} + +fn store(engine: &Engine) -> Store { + Store::new(engine, Ctx) +} + +// Wiggle expects the caller to have an exported memory. Wasmtime can only +// provide this if the caller is a WebAssembly module, so we need to write +// a shim module: +fn shim_module(engine: &Engine) -> Module { + Module::new( + engine, + r#" + (module + (memory 1) + (export "memory" (memory 0)) + (import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32))) + (import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32))) + + (func $int_float_args_shim (param i32 f32) (result i32) + local.get 0 + local.get 1 + call $int_float_args + ) + (func $double_int_return_float_shim (param i32 i32) (result i32) + local.get 0 + local.get 1 + call $double_int_return_float + ) + (export "int_float_args_shim" (func $int_float_args_shim)) + (export "double_int_return_float_shim" (func $double_int_return_float_shim)) + ) + "#, + ) + .unwrap() +} diff --git a/crates/wiggle/tests/wasmtime_sync.rs b/crates/wiggle/tests/wasmtime_sync.rs index 0fbd9d9350c6..7c8b4c09141a 100644 --- a/crates/wiggle/tests/wasmtime_sync.rs +++ b/crates/wiggle/tests/wasmtime_sync.rs @@ -1,14 +1,6 @@ use wasmtime::{Engine, Linker, Module, Store}; wiggle::from_witx!({ - witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - async: { - atoms::{double_int_return_float} - } -}); - -wiggle::wasmtime_integration!({ - target: crate, witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], block_on: { atoms::double_int_return_float @@ -40,7 +32,7 @@ impl atoms::Atoms for Ctx { fn test_sync_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); @@ -63,7 +55,7 @@ fn test_sync_host_func() { fn test_async_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); From 6e11ad388dd9ad3089ecbabf7da399f70291edd4 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 19 May 2021 19:31:33 -0700 Subject: [PATCH 55/90] replace wasmtime-wiggle crate with normal wiggle. many fixes. --- Cargo.lock | 35 --- Cargo.toml | 1 - crates/wasi-crypto/Cargo.toml | 1 - crates/wasi-crypto/src/lib.rs | 37 +-- crates/wasi-nn/Cargo.toml | 1 - crates/wasi-nn/src/lib.rs | 19 +- crates/wasi/Cargo.toml | 5 +- crates/wasi/src/lib.rs | 42 +--- crates/wiggle/borrow/Cargo.toml | 17 -- crates/wiggle/borrow/src/lib.rs | 259 -------------------- crates/wiggle/generate/src/config.rs | 15 +- crates/wiggle/generate/src/lib.rs | 1 + crates/wiggle/generate/src/wasmtime.rs | 22 +- crates/wiggle/macro/src/lib.rs | 10 +- crates/wiggle/src/lib.rs | 5 + crates/wiggle/test-helpers/Cargo.toml | 1 - crates/wiggle/test-helpers/src/lib.rs | 4 +- crates/wiggle/wasmtime/Cargo.toml | 53 ---- crates/wiggle/wasmtime/macro/Cargo.toml | 29 --- crates/wiggle/wasmtime/macro/src/config.rs | 193 --------------- crates/wiggle/wasmtime/macro/src/lib.rs | 184 -------------- crates/wiggle/wasmtime/src/lib.rs | 56 ----- crates/wiggle/wasmtime/tests/atoms.witx | 25 -- crates/wiggle/wasmtime/tests/atoms_async.rs | 168 ------------- crates/wiggle/wasmtime/tests/atoms_sync.rs | 130 ---------- scripts/publish.rs | 5 +- src/commands/run.rs | 2 +- 27 files changed, 51 insertions(+), 1269 deletions(-) delete mode 100644 crates/wiggle/borrow/Cargo.toml delete mode 100644 crates/wiggle/borrow/src/lib.rs delete mode 100644 crates/wiggle/wasmtime/Cargo.toml delete mode 100644 crates/wiggle/wasmtime/macro/Cargo.toml delete mode 100644 crates/wiggle/wasmtime/macro/src/config.rs delete mode 100644 crates/wiggle/wasmtime/macro/src/lib.rs delete mode 100644 crates/wiggle/wasmtime/src/lib.rs delete mode 100644 crates/wiggle/wasmtime/tests/atoms.witx delete mode 100644 crates/wiggle/wasmtime/tests/atoms_async.rs delete mode 100644 crates/wiggle/wasmtime/tests/atoms_sync.rs diff --git a/Cargo.lock b/Cargo.lock index d9c25f10e809..3e54d9a8ddfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3823,7 +3823,6 @@ dependencies = [ "wasi-common", "wasi-tokio", "wasmtime", - "wasmtime-wiggle", "wiggle", ] @@ -3834,7 +3833,6 @@ dependencies = [ "anyhow", "wasi-crypto", "wasmtime", - "wasmtime-wiggle", "wiggle", ] @@ -3850,7 +3848,6 @@ dependencies = [ "wasmtime", "wasmtime-runtime", "wasmtime-wasi", - "wasmtime-wiggle", "wiggle", ] @@ -3863,30 +3860,6 @@ dependencies = [ "wast 35.0.2", ] -[[package]] -name = "wasmtime-wiggle" -version = "0.26.0" -dependencies = [ - "anyhow", - "proptest", - "wasmtime", - "wasmtime-wiggle-macro", - "wiggle", - "wiggle-borrow", - "witx", -] - -[[package]] -name = "wasmtime-wiggle-macro" -version = "0.26.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wiggle-generate", - "witx", -] - [[package]] name = "wast" version = "33.0.0" @@ -3940,13 +3913,6 @@ dependencies = [ "witx", ] -[[package]] -name = "wiggle-borrow" -version = "0.26.0" -dependencies = [ - "wiggle", -] - [[package]] name = "wiggle-generate" version = "0.26.0" @@ -3982,7 +3948,6 @@ dependencies = [ "tracing", "tracing-subscriber", "wiggle", - "wiggle-borrow", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 210b1fc7fdfc..a873833f44a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,6 @@ members = [ "crates/wiggle", "crates/wiggle/generate", "crates/wiggle/macro", - "crates/wiggle/wasmtime", "crates/wasi-common", "crates/wasi-common/cap-std-sync", "crates/wasi-common/tokio", diff --git a/crates/wasi-crypto/Cargo.toml b/crates/wasi-crypto/Cargo.toml index 71354ce575ae..957bd02d2176 100644 --- a/crates/wasi-crypto/Cargo.toml +++ b/crates/wasi-crypto/Cargo.toml @@ -15,7 +15,6 @@ edition = "2018" anyhow = "1.0" wasi-crypto = { path = "spec/implementations/hostcalls/rust", version = "0.1.4" } wasmtime = { path = "../wasmtime", version = "0.26.0", default-features = false } -wasmtime-wiggle = { path = "../wiggle/wasmtime", version = "0.26.0" } wiggle = { path = "../wiggle", version = "0.26.0" } [badges] diff --git a/crates/wasi-crypto/src/lib.rs b/crates/wasi-crypto/src/lib.rs index 5a9455c2e99f..defce00142d6 100644 --- a/crates/wasi-crypto/src/lib.rs +++ b/crates/wasi-crypto/src/lib.rs @@ -8,37 +8,10 @@ pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> where T: BorrowMut, { - add_wasi_crypto_common_to_linker(linker)?; - add_wasi_crypto_asymmetric_common_to_linker(linker)?; - add_wasi_crypto_signatures_to_linker(linker)?; - add_wasi_crypto_symmetric_to_linker(linker)?; + use wiggle_interfaces::wasi_modules as w; + w::wasi_ephemeral_crypto_common::add_to_linker(linker)?; + w::wasi_ephemeral_crypto_asymetric_common::add_to_linker(linker)?; + w::wasi_ephemeral_crypto_signatures::add_to_linker(linker)?; + w::wasi_ephemeral_crypto_symmetric::add_to_linker(linker)?; Ok(()) } - -wasmtime_wiggle::wasmtime_integration!({ - target: wiggle_interfaces::wasi_modules, - witx: ["$CARGO_MANIFEST_DIR/spec/witx/wasi_ephemeral_crypto.witx"], - ctx: WasiCryptoCtx, - modules: { - wasi_ephemeral_crypto_common => - { - name: wasi_crypto_common, - docs: "wasi-crypto - Common module." - }, - wasi_ephemeral_crypto_asymmetric_common => - { - name: wasi_crypto_asymmetric_common, - docs: "wasi-crypto - Common module for asymmetric operations." - }, - wasi_ephemeral_crypto_signatures => - { - name: wasi_crypto_signatures, - docs: "wasi-crypto - Signature module." - }, - wasi_ephemeral_crypto_symmetric => - { - name: wasi_crypto_symmetric, - docs: "wasi-crypto - Symmetric cryptography module." - } - } -}); diff --git a/crates/wasi-nn/Cargo.toml b/crates/wasi-nn/Cargo.toml index a5de00ed3a2c..21b02ec0dcf6 100644 --- a/crates/wasi-nn/Cargo.toml +++ b/crates/wasi-nn/Cargo.toml @@ -17,7 +17,6 @@ anyhow = "1.0" log = { version = "0.4", default-features = false } wasmtime = { path = "../wasmtime", version = "0.26.0", default-features = false } wasmtime-runtime = { path = "../runtime", version = "0.26.0" } -wasmtime-wiggle = { path = "../wiggle/wasmtime", version = "0.26.0" } wasmtime-wasi = { path = "../wasi", version = "0.26.0" } wiggle = { path = "../wiggle", version = "0.26.0" } diff --git a/crates/wasi-nn/src/lib.rs b/crates/wasi-nn/src/lib.rs index 783188652023..d66b05f5e667 100644 --- a/crates/wasi-nn/src/lib.rs +++ b/crates/wasi-nn/src/lib.rs @@ -3,21 +3,4 @@ mod r#impl; mod witx; pub use ctx::WasiNnCtx; - -// Defines a `struct WasiNn` with member fields and appropriate APIs for dealing with all the -// various WASI exports. -wasmtime_wiggle::wasmtime_integration!({ - // The wiggle code to integrate with lives here: - target: witx, - // This must be the same witx document as used above: - witx: ["$WASI_ROOT/phases/ephemeral/witx/wasi_ephemeral_nn.witx"], - // This must be the same ctx type as used for the target: - ctx: WasiNnCtx, - // This macro will emit a struct to represent the instance, with this name and docs: - modules: { - wasi_ephemeral_nn => { - name: wasi_nn, - docs: "An instantiated instance of the wasi-nn exports.", - } - }, -}); +pub use witx::wasi_ephemeral_nn::add_to_linker; diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 3fa9dad245e2..b079dd34190f 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -16,12 +16,11 @@ build = "build.rs" wasi-common = { path = "../wasi-common", version = "0.26.0" } wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync", version = "0.26.0", optional = true } wasi-tokio = { path = "../wasi-common/tokio", version = "0.26.0", optional = true } -wiggle = { path = "../wiggle", default-features = false, version = "0.26.0" } -wasmtime-wiggle = { path = "../wiggle/wasmtime", default-features = false, version = "0.26.0" } +wiggle = { path = "../wiggle", default-features = false, version = "0.26.0", features = ["wasmtime_integration"] } wasmtime = { path = "../wasmtime", default-features = false, version = "0.26.0" } anyhow = "1.0" [features] default = ["sync"] sync = ["wasi-cap-std-sync"] -tokio = ["wasi-tokio", "wasmtime/async", "wasmtime-wiggle/async"] +tokio = ["wasi-tokio", "wasmtime/async", "wiggle/wasmtime_async"] diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 5c6886eec958..6daf3dab8370 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -54,58 +54,24 @@ where pub mod snapshots { pub mod preview_1 { - use wasi_common::WasiCtx; - // Defines a `struct Wasi` with member fields and appropriate APIs for dealing - // with all the various WASI exports. - wasmtime_wiggle::wasmtime_integration!({ + wiggle::wasmtime_integration!({ // The wiggle code to integrate with lives here: target: wasi_common::snapshots::preview_1, // This must be the same witx document as used above. This should be ensured by // the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`. witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"], - // This must be the same ctx type as used for the target: - ctx: WasiCtx, - // This macro will emit a struct to represent the instance, - // with this name and docs: - modules: { wasi_snapshot_preview1 => - { name: wasi_snapshot_preview1, - docs: "An instantiated instance of the wasi exports. - -This represents a wasi module which can be used to instantiate other wasm -modules. This structure exports all that various fields of the wasi instance -as fields which can be used to implement your own instantiation logic, if -necessary. Additionally [`Wasi::get_export`] can be used to do name-based -resolution.", - }, - }, + errors: { errno => Error }, $async_mode: * }); } pub mod preview_0 { - use wasi_common::WasiCtx; - // Defines a `struct Wasi` with member fields and appropriate APIs for dealing - // with all the various WASI exports. - wasmtime_wiggle::wasmtime_integration!({ + wiggle::wasmtime_integration!({ // The wiggle code to integrate with lives here: target: wasi_common::snapshots::preview_0, // This must be the same witx document as used above. This should be ensured by // the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`. witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"], - // This must be the same ctx type as used for the target: - ctx: WasiCtx, - // This macro will emit a struct to represent the instance, - // with this name and docs: - modules: { wasi_unstable => - { name: wasi_unstable, - docs: "An instantiated instance of the wasi exports. - -This represents a wasi module which can be used to instantiate other wasm -modules. This structure exports all that various fields of the wasi instance -as fields which can be used to implement your own instantiation logic, if -necessary. Additionally [`Wasi::get_export`] can be used to do name-based -resolution.", - }, - }, + errors: { errno => Error }, $async_mode: * }); } diff --git a/crates/wiggle/borrow/Cargo.toml b/crates/wiggle/borrow/Cargo.toml deleted file mode 100644 index 46ebe250091e..000000000000 --- a/crates/wiggle/borrow/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "wiggle-borrow" -version = "0.26.0" -authors = ["Pat Hickey ", "Jakub Konka ", "Alex Crichton "] -edition = "2018" -license = "Apache-2.0 WITH LLVM-exception" -description = "A run-time borrow checker for use with Wiggle" -categories = ["wasm"] -keywords = ["webassembly", "wasm"] -repository = "https://github.com/bytecodealliance/wasmtime" -include = ["src/**/*", "LICENSE"] - -[dependencies] -wiggle = { path = "..", version = "0.26.0" } - -[badges] -maintenance = { status = "actively-developed" } diff --git a/crates/wiggle/borrow/src/lib.rs b/crates/wiggle/borrow/src/lib.rs deleted file mode 100644 index 629733a02cf9..000000000000 --- a/crates/wiggle/borrow/src/lib.rs +++ /dev/null @@ -1,259 +0,0 @@ -use std::collections::HashMap; -use std::sync::Mutex; -use wiggle::{BorrowHandle, GuestError, Region}; - -pub struct BorrowChecker { - /// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking - /// overlap, the method calls on this member will be confusing. - /// - /// Also, unfortunately, for now this uses a `Mutex`. The reason for that is - /// that this is shared as `&BorrowChecker` in a bunch of `GuestPtr` values. - /// Through this sharing we still want each `GuestPtr` to be `Send` and the - /// "naive" way to make `&T` `Send` with interior mutability is to use a - /// `Mutex`. Fixing this will likely require rethinking `GuestPtr` one way - /// or another. That needs to happen for other reasons as well (for example - /// to allow for wasm calls to happen while `GuestPtr` values are active), - /// so it's hoped that in a later rethinking of `GuestPtr` we can revisit - /// this and remove this `Mutex`. - bc: Mutex, -} - -impl BorrowChecker { - /// A `BorrowChecker` manages run-time validation of borrows from a - /// `GuestMemory`. It keeps track of regions of guest memory which are - /// possible to alias with Rust references (via the `GuestSlice` and - /// `GuestStr` structs, which implement `std::ops::Deref` and - /// `std::ops::DerefMut`. It also enforces that `GuestPtr::read` - /// does not access memory with an outstanding mutable borrow, and - /// `GuestPtr::write` does not access memory with an outstanding - /// shared or mutable borrow. - pub fn new() -> Self { - BorrowChecker { - bc: Mutex::new(InnerBorrowChecker::new()), - } - } - /// Indicates whether any outstanding shared or mutable borrows are known - /// to the `BorrowChecker`. This function must be `false` in order for it - /// to be safe to recursively call into a WebAssembly module, or to - /// manipulate the WebAssembly memory by any other means. - pub fn has_outstanding_borrows(&self) -> bool { - self.bc.lock().unwrap().has_outstanding_borrows() - } - pub fn shared_borrow(&self, r: Region) -> Result { - self.bc.lock().unwrap().shared_borrow(r) - } - pub fn mut_borrow(&self, r: Region) -> Result { - self.bc.lock().unwrap().mut_borrow(r) - } - pub fn shared_unborrow(&self, h: BorrowHandle) { - self.bc.lock().unwrap().shared_unborrow(h) - } - pub fn mut_unborrow(&self, h: BorrowHandle) { - self.bc.lock().unwrap().mut_unborrow(h) - } - pub fn is_shared_borrowed(&self, r: Region) -> bool { - self.bc.lock().unwrap().is_shared_borrowed(r) - } - pub fn is_mut_borrowed(&self, r: Region) -> bool { - self.bc.lock().unwrap().is_mut_borrowed(r) - } -} - -#[derive(Debug)] -/// This is a pretty naive way to account for borrows. This datastructure -/// could be made a lot more efficient with some effort. -struct InnerBorrowChecker { - /// Maps from handle to region borrowed. A HashMap is probably not ideal - /// for this but it works. It would be more efficient if we could - /// check `is_borrowed` without an O(n) iteration, by organizing borrows - /// by an ordering of Region. - shared_borrows: HashMap, - mut_borrows: HashMap, - /// Handle to give out for the next borrow. This is the bare minimum of - /// bookkeeping of free handles, and in a pathological case we could run - /// out, hence [`GuestError::BorrowCheckerOutOfHandles`] - next_handle: BorrowHandle, -} - -impl InnerBorrowChecker { - fn new() -> Self { - InnerBorrowChecker { - shared_borrows: HashMap::new(), - mut_borrows: HashMap::new(), - next_handle: BorrowHandle(0), - } - } - - fn has_outstanding_borrows(&self) -> bool { - !(self.shared_borrows.is_empty() && self.mut_borrows.is_empty()) - } - - fn is_shared_borrowed(&self, r: Region) -> bool { - self.shared_borrows.values().any(|b| b.overlaps(r)) - } - fn is_mut_borrowed(&self, r: Region) -> bool { - self.mut_borrows.values().any(|b| b.overlaps(r)) - } - - fn new_handle(&mut self) -> Result { - // Reset handles to 0 if all handles have been returned. - if self.shared_borrows.is_empty() && self.mut_borrows.is_empty() { - self.next_handle = BorrowHandle(0); - } - let h = self.next_handle; - // Get the next handle. Since we don't recycle handles until all of - // them have been returned, there is a pathological case where a user - // may make a Very Large (usize::MAX) number of valid borrows and - // unborrows while always keeping at least one borrow outstanding, and - // we will run out of borrow handles. - self.next_handle = BorrowHandle( - h.0.checked_add(1) - .ok_or_else(|| GuestError::BorrowCheckerOutOfHandles)?, - ); - Ok(h) - } - - fn shared_borrow(&mut self, r: Region) -> Result { - if self.is_mut_borrowed(r) { - return Err(GuestError::PtrBorrowed(r)); - } - let h = self.new_handle()?; - self.shared_borrows.insert(h, r); - Ok(h) - } - - fn mut_borrow(&mut self, r: Region) -> Result { - if self.is_shared_borrowed(r) || self.is_mut_borrowed(r) { - return Err(GuestError::PtrBorrowed(r)); - } - let h = self.new_handle()?; - self.mut_borrows.insert(h, r); - Ok(h) - } - - fn shared_unborrow(&mut self, h: BorrowHandle) { - let removed = self.shared_borrows.remove(&h); - debug_assert!(removed.is_some(), "double-freed shared borrow"); - } - - fn mut_unborrow(&mut self, h: BorrowHandle) { - let removed = self.mut_borrows.remove(&h); - debug_assert!(removed.is_some(), "double-freed mut borrow"); - } -} - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn nonoverlapping() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - bs.mut_borrow(r1).expect("can borrow r1"); - bs.mut_borrow(r2).expect("can borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(10, 10); - let r2 = Region::new(0, 10); - assert!(!r1.overlaps(r2)); - bs.mut_borrow(r1).expect("can borrow r1"); - bs.mut_borrow(r2).expect("can borrow r2"); - } - - #[test] - fn overlapping() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(9, 10); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant mut borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(2, 5); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(9, 10); - let r2 = Region::new(0, 10); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(2, 5); - let r2 = Region::new(0, 10); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(2, 5); - let r2 = Region::new(10, 5); - let r3 = Region::new(15, 5); - let r4 = Region::new(0, 10); - assert!(r1.overlaps(r4)); - bs.shared_borrow(r1).expect("can borrow r1"); - bs.shared_borrow(r2).expect("can borrow r2"); - bs.shared_borrow(r3).expect("can borrow r3"); - assert!(bs.mut_borrow(r4).is_err(), "cant mut borrow r4"); - bs.shared_borrow(r4).expect("can shared borrow r4"); - } - - #[test] - fn unborrowing() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); - let h1 = bs.mut_borrow(r1).expect("can borrow r1"); - assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); - let h2 = bs.mut_borrow(r2).expect("can borrow r2"); - - assert!(bs.mut_borrow(r2).is_err(), "can't borrow r2 twice"); - bs.mut_unborrow(h2); - assert_eq!( - bs.has_outstanding_borrows(), - true, - "h1 is still outstanding" - ); - bs.mut_unborrow(h1); - assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); - - let _h3 = bs - .mut_borrow(r2) - .expect("can borrow r2 again now that its been unborrowed"); - - // Lets try again with shared: - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); - let h1 = bs.shared_borrow(r1).expect("can borrow r1"); - assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); - let h2 = bs.shared_borrow(r2).expect("can borrow r2"); - let h3 = bs.shared_borrow(r2).expect("can shared borrow r2 twice"); - - bs.shared_unborrow(h2); - assert_eq!( - bs.has_outstanding_borrows(), - true, - "h1, h3 still outstanding" - ); - bs.shared_unborrow(h1); - bs.shared_unborrow(h3); - assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); - } -} diff --git a/crates/wiggle/generate/src/config.rs b/crates/wiggle/generate/src/config.rs index 73db2fc5f31d..8739d6a121a1 100644 --- a/crates/wiggle/generate/src/config.rs +++ b/crates/wiggle/generate/src/config.rs @@ -311,12 +311,21 @@ pub enum Asyncness { impl Asyncness { pub fn is_async(&self) -> bool { match self { - Self::Blocking | Self::Async => true, - Self::Sync => false, + Self::Async => true, + _ => false, + } + } + pub fn is_blocking(&self) -> bool { + match self { + Self::Blocking => true, + _ => false, } } pub fn is_sync(&self) -> bool { - !self.is_async() + match self { + Self::Sync => true, + _ => false, + } } } diff --git a/crates/wiggle/generate/src/lib.rs b/crates/wiggle/generate/src/lib.rs index 3b77a3a0d6fa..3b58e12bcab1 100644 --- a/crates/wiggle/generate/src/lib.rs +++ b/crates/wiggle/generate/src/lib.rs @@ -64,6 +64,7 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings) quote!( pub mod #modname { use super::types::*; + pub use super::types::UserErrorConversion; #(#fs)* #modtrait diff --git a/crates/wiggle/generate/src/wasmtime.rs b/crates/wiggle/generate/src/wasmtime.rs index d66856132fd9..cdce7a97f565 100644 --- a/crates/wiggle/generate/src/wasmtime.rs +++ b/crates/wiggle/generate/src/wasmtime.rs @@ -46,9 +46,11 @@ pub fn link_module( format_ident!("add_{}_to_linker", module_ident) }; + let rt = names.runtime_mod(); + quote! { /// Adds all instance items to the specified `Linker`. - pub fn #func_name(linker: &mut wasmtime::Linker) -> anyhow::Result<()> + pub fn #func_name(linker: &mut #rt::wasmtime_crate::Linker) -> anyhow::Result<()> where T: std::borrow::BorrowMut #send_bound, C: #ctx_bound @@ -107,13 +109,11 @@ fn generate_func( quote!( #field_ident ) }; - let runtime = names.runtime_mod(); - let body = quote! { let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, + Some(#rt::wasmtime_crate::Extern::Memory(m)) => m, _ => { - return Err(wasmtime::Trap::new("missing required memory export")); + return Err(#rt::wasmtime_crate::Trap::new("missing required memory export")); } }; // Note the unsafety here. Our goal is to simultaneously borrow the @@ -133,12 +133,12 @@ fn generate_func( // working from the previous iteration for now. let (ctx, mem) = unsafe { let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); - (caller.data_mut().borrow_mut(), #runtime::wasmtime::WasmtimeGuestMemory::new(mem)) + (caller.data_mut().borrow_mut(), #rt::wasmtime::WasmtimeGuestMemory::new(mem)) }; match #abi_func(ctx, &mem #(, #arg_names)*) #await_ { Ok(r) => Ok(<#ret_ty>::from(r)), - Err(#runtime::Trap::String(err)) => Err(wasmtime::Trap::new(err)), - Err(#runtime::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), + Err(#rt::Trap::String(err)) => Err(#rt::wasmtime_crate::Trap::new(err)), + Err(#rt::Trap::I32Exit(err)) => Err(#rt::wasmtime_crate::Trap::i32_exit(err)), } }; @@ -149,7 +149,7 @@ fn generate_func( linker.#wrapper( #module_str, #field_str, - move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| { + move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| { Box::new(async move { #body }) }, )?; @@ -161,7 +161,7 @@ fn generate_func( linker.func_wrap( #module_str, #field_str, - move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { + move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, #rt::wasmtime_crate::Trap> { let result = async { #body }; #rt::run_in_dummy_executor(result) }, @@ -174,7 +174,7 @@ fn generate_func( linker.func_wrap( #module_str, #field_str, - move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { + move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, #rt::wasmtime_crate::Trap> { #body }, )?; diff --git a/crates/wiggle/macro/src/lib.rs b/crates/wiggle/macro/src/lib.rs index 57ba9c03e754..582a4bdcb680 100644 --- a/crates/wiggle/macro/src/lib.rs +++ b/crates/wiggle/macro/src/lib.rs @@ -17,15 +17,19 @@ use syn::parse_macro_input; /// * For each `@interface func` defined in a witx module, an abi-level /// function is generated which takes ABI-level arguments, along with /// a ref that impls the module trait, and a `GuestMemory` implementation. -/// Users typically won't use these abi-level functions: The `wasmtime-wiggle` -/// and `lucet-wiggle` crates adapt these to work with a particular WebAssembly -/// engine. +/// Users typically won't use these abi-level functions: Either the +/// `wasmtime_integration` macro or the `lucet-wiggle` crates adapt these +/// to work with a particular WebAssembly engine. /// /// * A public "module trait" is defined (called the module name, in /// SnakeCase) which has a `&self` method for each function in the /// module. These methods takes idiomatic Rust types for each argument /// and return `Result<($return_types),$error_type>` /// +/// * When the `wiggle` crate is built with the `wasmtime_integration` +/// feature, each module contains an `add_to_linker` function to add it to +/// a `wasmtime::Linker`. +/// /// Arguments are provided using Rust struct value syntax. /// /// * `witx` takes a list of string literal paths. Paths are relative to the diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index df2c49a4fc21..7ff0ba18639c 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -31,6 +31,11 @@ pub mod async_trait_crate { #[cfg(feature = "wasmtime")] pub mod wasmtime; +#[cfg(feature = "wasmtime")] +pub mod wasmtime_crate { + pub use wasmtime::*; +} + /// A trait which abstracts how to get at the region of host memory taht /// contains guest memory. /// diff --git a/crates/wiggle/test-helpers/Cargo.toml b/crates/wiggle/test-helpers/Cargo.toml index 63f68d3b3d82..7944bbe24ef2 100644 --- a/crates/wiggle/test-helpers/Cargo.toml +++ b/crates/wiggle/test-helpers/Cargo.toml @@ -14,7 +14,6 @@ publish = false [dependencies] proptest = "1.0.0" wiggle = { path = "..", features = ["tracing_log"] } -wiggle-borrow = { path = "../borrow" } [dev-dependencies] thiserror = "1.0" diff --git a/crates/wiggle/test-helpers/src/lib.rs b/crates/wiggle/test-helpers/src/lib.rs index ff2b2eda0d42..361372546d90 100644 --- a/crates/wiggle/test-helpers/src/lib.rs +++ b/crates/wiggle/test-helpers/src/lib.rs @@ -1,9 +1,7 @@ use proptest::prelude::*; use std::cell::UnsafeCell; use std::marker; -use wiggle::{BorrowHandle, GuestMemory, Region}; - -use wiggle_borrow::BorrowChecker; +use wiggle::{borrow::BorrowChecker, BorrowHandle, GuestMemory, Region}; #[derive(Debug, Clone)] pub struct MemAreas(Vec); diff --git a/crates/wiggle/wasmtime/Cargo.toml b/crates/wiggle/wasmtime/Cargo.toml deleted file mode 100644 index f51ea119bbc5..000000000000 --- a/crates/wiggle/wasmtime/Cargo.toml +++ /dev/null @@ -1,53 +0,0 @@ -[package] -name = "wasmtime-wiggle" -version = "0.26.0" -authors = ["Pat Hickey ", "Jakub Konka ", "Alex Crichton "] -edition = "2018" -license = "Apache-2.0 WITH LLVM-exception" -description = "Integrate Wiggle code generator with Wasmtime" -categories = ["wasm"] -keywords = ["webassembly", "wasm"] -repository = "https://github.com/bytecodealliance/wasmtime" -include = ["src/**/*", "LICENSE"] - -[dependencies] -wasmtime = { path = "../../wasmtime", version = "0.26.0", default-features = false } -wasmtime-wiggle-macro = { path = "./macro", version = "0.26.0" } -witx = { version = "0.9.0", path = "../../wasi-common/WASI/tools/witx", optional = true } -wiggle = { path = "..", version = "0.26.0" } -wiggle-borrow = { path = "../borrow", version = "0.26.0" } - -[dev-dependencies] -anyhow = "1" -proptest = "1.0.0" - -[[test]] -name = "atoms_async" -path = "tests/atoms_async.rs" -required-features = ["async", "wasmtime/wat"] - -[[test]] -name = "atoms_sync" -path = "tests/atoms_sync.rs" -required-features = ["wasmtime/wat"] - -[badges] -maintenance = { status = "actively-developed" } - -[features] -# Async support for wasmtime -async = [ 'wasmtime/async', 'wasmtime-wiggle-macro/async' ] - -# The wiggle proc-macro emits some code (inside `pub mod metadata`) guarded -# by the `wiggle_metadata` feature flag. We use this feature flag so that -# users of wiggle are not forced to take a direct dependency on the `witx` -# crate unless they want it. -wiggle_metadata = ['witx', "wiggle/wiggle_metadata"] - -# The `tracing` crate can use the `log` ecosystem of backends with this -# non-default feature. We don't need to provide this by default, but its -# useful for users that don't want to use `tracing-subscriber` to get -# the logs out of wiggle-generated libraries. -tracing_log = [ "wiggle/tracing_log" ] - -default = ["wiggle_metadata", "async"] diff --git a/crates/wiggle/wasmtime/macro/Cargo.toml b/crates/wiggle/wasmtime/macro/Cargo.toml deleted file mode 100644 index c1c9494d384e..000000000000 --- a/crates/wiggle/wasmtime/macro/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "wasmtime-wiggle-macro" -version = "0.26.0" -authors = ["Pat Hickey ", "Jakub Konka ", "Alex Crichton "] -edition = "2018" -license = "Apache-2.0 WITH LLVM-exception" -description = "Macro for integrating Wiggle code generator with Wasmtime" -categories = ["wasm"] -keywords = ["webassembly", "wasm"] -repository = "https://github.com/bytecodealliance/wasmtime" -include = ["src/**/*", "LICENSE"] - -[lib] -proc-macro = true -test = false - -[dependencies] -witx = { version = "0.9.0", path = "../../../wasi-common/WASI/tools/witx" } -wiggle-generate = { path = "../../generate", version = "0.26.0" } -quote = "1.0" -syn = { version = "1.0", features = ["full", "extra-traits"] } -proc-macro2 = "1.0" - -[badges] -maintenance = { status = "actively-developed" } - -[features] -async = [] -default = [] diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs deleted file mode 100644 index 64827f4a042f..000000000000 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ /dev/null @@ -1,193 +0,0 @@ -use wiggle_generate::config::AsyncFunctions; -use { - proc_macro2::Span, - syn::{ - braced, - parse::{Parse, ParseStream}, - punctuated::Punctuated, - Error, Path, Result, Token, - }, - wiggle_generate::config::WitxConf, -}; -#[derive(Debug, Clone)] -pub struct Config { - pub target: TargetConf, - pub witx: WitxConf, - pub async_: AsyncConf, -} - -#[derive(Debug, Clone)] -pub enum ConfigField { - Target(TargetConf), - Witx(WitxConf), - Async(AsyncConf), -} - -mod kw { - syn::custom_keyword!(target); - syn::custom_keyword!(witx); - syn::custom_keyword!(witx_literal); - syn::custom_keyword!(block_on); -} - -impl Parse for ConfigField { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(kw::target) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Target(input.parse()?)) - } else if lookahead.peek(kw::witx) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Witx(WitxConf::Paths(input.parse()?))) - } else if lookahead.peek(kw::witx_literal) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?))) - } else if lookahead.peek(Token![async]) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Async(AsyncConf { - blocking: false, - functions: input.parse()?, - })) - } else if lookahead.peek(kw::block_on) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Async(AsyncConf { - blocking: true, - functions: input.parse()?, - })) - } else { - Err(lookahead.error()) - } - } -} - -impl Config { - pub fn build(fields: impl Iterator, err_loc: Span) -> Result { - let mut target = None; - let mut witx = None; - let mut async_ = None; - for f in fields { - match f { - ConfigField::Target(c) => { - if target.is_some() { - return Err(Error::new(err_loc, "duplicate `target` field")); - } - target = Some(c); - } - ConfigField::Witx(c) => { - if witx.is_some() { - return Err(Error::new(err_loc, "duplicate `witx` field")); - } - witx = Some(c); - } - ConfigField::Async(c) => { - if async_.is_some() { - return Err(Error::new(err_loc, "duplicate `async` field")); - } - async_ = Some(c); - } - } - } - Ok(Config { - target: target.ok_or_else(|| Error::new(err_loc, "`target` field required"))?, - witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, - async_: async_.unwrap_or_default(), - }) - } - - /// Load the `witx` document for the configuration. - /// - /// # Panics - /// - /// This method will panic if the paths given in the `witx` field were not valid documents. - pub fn load_document(&self) -> witx::Document { - self.witx.load_document() - } -} - -impl Parse for Config { - fn parse(input: ParseStream) -> Result { - let contents; - let _lbrace = braced!(contents in input); - let fields: Punctuated = - contents.parse_terminated(ConfigField::parse)?; - Ok(Config::build(fields.into_iter(), input.span())?) - } -} - -#[derive(Debug, Clone)] -pub struct TargetConf { - pub path: Path, -} - -impl Parse for TargetConf { - fn parse(input: ParseStream) -> Result { - Ok(TargetConf { - path: input.parse()?, - }) - } -} - -#[derive(Clone, Default, Debug)] -/// Modules and funcs that have async signatures -pub struct AsyncConf { - blocking: bool, - functions: AsyncFunctions, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Asyncness { - /// Wiggle function is synchronous, wasmtime Func is synchronous - Sync, - /// Wiggle function is asynchronous, but wasmtime Func is synchronous - Blocking, - /// Wiggle function and wasmtime Func are asynchronous. - Async, -} - -impl Asyncness { - pub fn is_sync(&self) -> bool { - match self { - Asyncness::Sync => true, - _ => false, - } - } -} - -impl AsyncConf { - pub fn is_async(&self, module: &str, function: &str) -> Asyncness { - let a = if self.blocking { - Asyncness::Blocking - } else { - Asyncness::Async - }; - match &self.functions { - AsyncFunctions::Some(fs) => { - if fs - .get(module) - .and_then(|fs| fs.iter().find(|f| *f == function)) - .is_some() - { - a - } else { - Asyncness::Sync - } - } - AsyncFunctions::All => a, - } - } - - pub fn contains_async(&self, module: &witx::Module) -> bool { - for f in module.funcs() { - match self.is_async(module.name.as_str(), f.name.as_str()) { - Asyncness::Async => return true, - _ => {} - } - } - false - } -} diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs deleted file mode 100644 index 5605ba50214b..000000000000 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ /dev/null @@ -1,184 +0,0 @@ -use proc_macro::TokenStream; -use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; -use quote::{format_ident, quote}; -use syn::parse_macro_input; -use wiggle_generate::Names; - -mod config; - -use config::{AsyncConf, Asyncness}; - -/// Define the structs required to integrate a Wiggle implementation with Wasmtime. -/// -/// ## Arguments -/// -/// Arguments are provided using struct syntax e.g. `{ arg_name: value }`. -/// -/// * `target`: The path of the module where the Wiggle implementation is defined. -/// * `witx` or `witx_literal`: the .witx document where the interface is defined. -/// `witx` takes a list of filesystem paths, e.g. `["/path/to/file1.witx", -/// "./path/to_file2.witx"]`. Relative paths are relative to the root of the crate -/// where the macro is invoked. `witx_literal` takes a string of the witx document, e.g. -/// `"(typename $foo u8)"`. -#[proc_macro] -pub fn wasmtime_integration(args: TokenStream) -> TokenStream { - let config = parse_macro_input!(args as config::Config); - let doc = config.load_document(); - let names = Names::new(quote!(wasmtime_wiggle)); - - let modules = doc - .modules() - .map(|module| generate_module(&module, &names, &config.target.path, &config.async_)); - quote!( #(#modules)* ).into() -} - -fn generate_module( - module: &witx::Module, - names: &Names, - target_path: &syn::Path, - async_conf: &AsyncConf, -) -> TokenStream2 { - let module_ident = names.module(&module.name); - let trait_ident = names.trait_name(&module.name); - - let send_bound = if async_conf.contains_async(module) { - quote! { + Send } - } else { - quote! {} - }; - - let bodies = module.funcs().map(|f| { - let asyncness = async_conf.is_async(module.name.as_str(), f.name.as_str()); - generate_func(&module, &f, names, &target_path, asyncness) - }); - - quote! { - /// Adds all instance items to the specified `Linker`. - pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> - where - T: std::borrow::BorrowMut #send_bound, - C: #target_path::#module_ident::#trait_ident #send_bound, - { - #(#bodies)* - Ok(()) - } - } -} - -fn generate_func( - module: &witx::Module, - func: &witx::InterfaceFunc, - names: &Names, - target_path: &syn::Path, - asyncness: Asyncness, -) -> TokenStream2 { - let rt = names.runtime_mod(); - - let module_str = module.name.as_str(); - let module_ident = names.module(&module.name); - - let field_str = func.name.as_str(); - let field_ident = names.func(&func.name); - - let (params, results) = func.wasm_signature(); - - let arg_names = (0..params.len()) - .map(|i| Ident::new(&format!("arg{}", i), Span::call_site())) - .collect::>(); - let arg_decls = params - .iter() - .enumerate() - .map(|(i, ty)| { - let name = &arg_names[i]; - let wasm = names.wasm_type(*ty); - quote! { #name: #wasm } - }) - .collect::>(); - - let ret_ty = match results.len() { - 0 => quote!(()), - 1 => names.wasm_type(results[0]), - _ => unimplemented!(), - }; - - let await_ = if asyncness.is_sync() { - quote!() - } else { - quote!(.await) - }; - - let runtime = names.runtime_mod(); - - let body = quote! { - let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, - _ => { - return Err(wasmtime::Trap::new("missing required memory export")); - } - }; - // Note the unsafety here. Our goal is to simultaneously borrow the - // memory and custom data from `caller`, and the store it's connected - // to. Rust will not let us do that, however, because we must call two - // separate methods (both of which borrow the whole `caller`) and one of - // our borrows is mutable (the custom data). - // - // This operation, however, is safe because these borrows do not overlap - // and in the process of borrowing them mutability doesn't actually - // touch anything. This is akin to mutably borrowing two indices in an - // array, which is safe so long as the indices are separate. - // - // TODO: depending on how common this is for other users to run into we - // may wish to consider adding a dedicated method for this. For now the - // future of `GuestPtr` may be a bit hazy, so let's just get this - // working from the previous iteration for now. - let (ctx, mem) = unsafe { - let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); - (caller.data_mut().borrow_mut(), #runtime::WasmtimeGuestMemory::new(mem)) - }; - match #target_path::#module_ident::#field_ident(ctx, &mem #(, #arg_names)*) #await_ { - Ok(r) => Ok(<#ret_ty>::from(r)), - Err(wasmtime_wiggle::Trap::String(err)) => Err(wasmtime::Trap::new(err)), - Err(wasmtime_wiggle::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), - } - }; - - match asyncness { - Asyncness::Async => { - let wrapper = format_ident!("func_wrap{}_async", params.len()); - quote! { - linker.#wrapper( - #module_str, - #field_str, - move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| { - Box::new(async move { #body }) - }, - )?; - } - } - - Asyncness::Blocking => { - quote! { - linker.func_wrap( - #module_str, - #field_str, - move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { - let result = async { #body }; - #rt::run_in_dummy_executor(result) - }, - )?; - } - } - - Asyncness::Sync => { - quote! { - linker.func_wrap( - #module_str, - #field_str, - move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { - #body - }, - )?; - } - } - } -} diff --git a/crates/wiggle/wasmtime/src/lib.rs b/crates/wiggle/wasmtime/src/lib.rs deleted file mode 100644 index 23dff142c73d..000000000000 --- a/crates/wiggle/wasmtime/src/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -pub use wasmtime_wiggle_macro::*; -pub use wiggle::*; - -use wiggle_borrow::BorrowChecker; - -/// Lightweight `wasmtime::Memory` wrapper so we can implement the -/// `wiggle::GuestMemory` trait on it. -pub struct WasmtimeGuestMemory<'a> { - mem: &'a mut [u8], - bc: BorrowChecker, -} - -impl<'a> WasmtimeGuestMemory<'a> { - pub fn new(mem: &'a mut [u8]) -> Self { - Self { - mem, - // Wiggle does not expose any methods for functions to re-enter - // the WebAssembly instance, or expose the memory via non-wiggle - // mechanisms. However, the user-defined code may end up - // re-entering the instance, in which case this is an incorrect - // implementation - we require exactly one BorrowChecker exist per - // instance. - // This BorrowChecker construction is a holdover until it is - // integrated fully with wasmtime: - // https://github.com/bytecodealliance/wasmtime/issues/1917 - bc: BorrowChecker::new(), - } - } -} - -unsafe impl GuestMemory for WasmtimeGuestMemory<'_> { - fn base(&self) -> (*mut u8, u32) { - (self.mem.as_ptr() as *mut u8, self.mem.len() as u32) - } - fn has_outstanding_borrows(&self) -> bool { - self.bc.has_outstanding_borrows() - } - fn is_shared_borrowed(&self, r: Region) -> bool { - self.bc.is_shared_borrowed(r) - } - fn is_mut_borrowed(&self, r: Region) -> bool { - self.bc.is_mut_borrowed(r) - } - fn shared_borrow(&self, r: Region) -> Result { - self.bc.shared_borrow(r) - } - fn mut_borrow(&self, r: Region) -> Result { - self.bc.mut_borrow(r) - } - fn shared_unborrow(&self, h: BorrowHandle) { - self.bc.shared_unborrow(h) - } - fn mut_unborrow(&self, h: BorrowHandle) { - self.bc.mut_unborrow(h) - } -} diff --git a/crates/wiggle/wasmtime/tests/atoms.witx b/crates/wiggle/wasmtime/tests/atoms.witx deleted file mode 100644 index af5955dbf0dc..000000000000 --- a/crates/wiggle/wasmtime/tests/atoms.witx +++ /dev/null @@ -1,25 +0,0 @@ - -(typename $errno - (enum (@witx tag u32) - ;;; Success - $ok - ;;; Invalid argument - $invalid_arg - ;;; I really don't want to - $dont_want_to - ;;; I am physically unable to - $physically_unable - ;;; Well, that's a picket line alright! - $picket_line)) - -(typename $alias_to_float f32) - -(module $atoms - (@interface func (export "int_float_args") - (param $an_int u32) - (param $an_float f32) - (result $error (expected (error $errno)))) - (@interface func (export "double_int_return_float") - (param $an_int u32) - (result $error (expected $alias_to_float (error $errno)))) -) diff --git a/crates/wiggle/wasmtime/tests/atoms_async.rs b/crates/wiggle/wasmtime/tests/atoms_async.rs deleted file mode 100644 index 65a5ae98d711..000000000000 --- a/crates/wiggle/wasmtime/tests/atoms_async.rs +++ /dev/null @@ -1,168 +0,0 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; -use wasmtime::{Config, Engine, Linker, Module, Store}; - -wasmtime_wiggle::from_witx!({ - witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - async: { - atoms::{double_int_return_float} - } -}); - -wasmtime_wiggle::wasmtime_integration!({ - target: crate, - witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - async: { - atoms::double_int_return_float - } -}); - -pub struct Ctx; -impl wiggle::GuestErrorType for types::Errno { - fn success() -> Self { - types::Errno::Ok - } -} - -#[wasmtime_wiggle::async_trait] -impl atoms::Atoms for Ctx { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { - println!("INT FLOAT ARGS: {} {}", an_int, an_float); - Ok(()) - } - async fn double_int_return_float( - &mut self, - an_int: u32, - ) -> Result { - Ok((an_int as f32) * 2.0) - } -} - -#[test] -fn test_sync_host_func() { - let mut store = async_store(); - let mut linker = Linker::new(store.engine()); - add_to_linker(&mut linker).unwrap(); - let shim_mod = shim_module(linker.engine()); - let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); - - let results = run(shim_inst - .get_func(&mut store, "int_float_args_shim") - .unwrap() - .call_async(&mut store, &[0i32.into(), 123.45f32.into()])) - .unwrap(); - - assert_eq!(results.len(), 1, "one return value"); - assert_eq!( - results[0].unwrap_i32(), - types::Errno::Ok as i32, - "int_float_args errno" - ); -} - -#[test] -fn test_async_host_func() { - let mut store = async_store(); - let mut linker = Linker::new(store.engine()); - add_to_linker(&mut linker).unwrap(); - - let shim_mod = shim_module(linker.engine()); - let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); - - let input: i32 = 123; - let result_location: i32 = 0; - - let results = run(shim_inst - .get_func(&mut store, "double_int_return_float_shim") - .unwrap() - .call_async(&mut store, &[input.into(), result_location.into()])) - .unwrap(); - - assert_eq!(results.len(), 1, "one return value"); - assert_eq!( - results[0].unwrap_i32(), - types::Errno::Ok as i32, - "double_int_return_float errno" - ); - - // The actual result is in memory: - let mem = shim_inst.get_memory(&mut store, "memory").unwrap(); - let mut result_bytes: [u8; 4] = [0, 0, 0, 0]; - mem.read(&store, result_location as usize, &mut result_bytes) - .unwrap(); - let result = f32::from_le_bytes(result_bytes); - assert_eq!((input * 2) as f32, result); -} - -fn run(future: F) -> F::Output { - let mut f = Pin::from(Box::new(future)); - let waker = dummy_waker(); - let mut cx = Context::from_waker(&waker); - loop { - match f.as_mut().poll(&mut cx) { - Poll::Ready(val) => break val, - Poll::Pending => {} - } - } -} - -fn dummy_waker() -> Waker { - return unsafe { Waker::from_raw(clone(5 as *const _)) }; - - unsafe fn clone(ptr: *const ()) -> RawWaker { - assert_eq!(ptr as usize, 5); - const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); - RawWaker::new(ptr, &VTABLE) - } - - unsafe fn wake(ptr: *const ()) { - assert_eq!(ptr as usize, 5); - } - - unsafe fn wake_by_ref(ptr: *const ()) { - assert_eq!(ptr as usize, 5); - } - - unsafe fn drop(ptr: *const ()) { - assert_eq!(ptr as usize, 5); - } -} - -fn async_store() -> Store { - Store::new( - &Engine::new(Config::new().async_support(true)).unwrap(), - Ctx, - ) -} - -// Wiggle expects the caller to have an exported memory. Wasmtime can only -// provide this if the caller is a WebAssembly module, so we need to write -// a shim module: -fn shim_module(engine: &Engine) -> Module { - Module::new( - engine, - r#" - (module - (memory 1) - (export "memory" (memory 0)) - (import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32))) - (import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32))) - - (func $int_float_args_shim (param i32 f32) (result i32) - local.get 0 - local.get 1 - call $int_float_args - ) - (func $double_int_return_float_shim (param i32 i32) (result i32) - local.get 0 - local.get 1 - call $double_int_return_float - ) - (export "int_float_args_shim" (func $int_float_args_shim)) - (export "double_int_return_float_shim" (func $double_int_return_float_shim)) - ) - "#, - ) - .unwrap() -} diff --git a/crates/wiggle/wasmtime/tests/atoms_sync.rs b/crates/wiggle/wasmtime/tests/atoms_sync.rs deleted file mode 100644 index 4cfd0e6d006c..000000000000 --- a/crates/wiggle/wasmtime/tests/atoms_sync.rs +++ /dev/null @@ -1,130 +0,0 @@ -use wasmtime::{Engine, Linker, Store}; - -wasmtime_wiggle::from_witx!({ - witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - async: { - atoms::{double_int_return_float} - } -}); - -wasmtime_wiggle::wasmtime_integration!({ - target: crate, - witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], - block_on: { - atoms::double_int_return_float - } -}); - -pub struct Ctx; -impl wiggle::GuestErrorType for types::Errno { - fn success() -> Self { - types::Errno::Ok - } -} - -#[wasmtime_wiggle::async_trait] -impl atoms::Atoms for Ctx { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { - println!("INT FLOAT ARGS: {} {}", an_int, an_float); - Ok(()) - } - async fn double_int_return_float( - &mut self, - an_int: u32, - ) -> Result { - Ok((an_int as f32) * 2.0) - } -} - -#[test] -fn test_sync_host_func() { - let engine = Engine::default(); - let mut linker = Linker::new(&engine); - add_to_linker(&mut linker).unwrap(); - let mut store = store(&engine); - let shim_mod = shim_module(&engine); - let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); - - let results = shim_inst - .get_func(&mut store, "int_float_args_shim") - .unwrap() - .call(&mut store, &[0i32.into(), 123.45f32.into()]) - .unwrap(); - - assert_eq!(results.len(), 1, "one return value"); - assert_eq!( - results[0].unwrap_i32(), - types::Errno::Ok as i32, - "int_float_args errno" - ); -} - -#[test] -fn test_async_host_func() { - let engine = Engine::default(); - let mut linker = Linker::new(&engine); - add_to_linker(&mut linker).unwrap(); - let mut store = store(&engine); - - let shim_mod = shim_module(&engine); - let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); - - let input: i32 = 123; - let result_location: i32 = 0; - - let results = shim_inst - .get_func(&mut store, "double_int_return_float_shim") - .unwrap() - .call(&mut store, &[input.into(), result_location.into()]) - .unwrap(); - - assert_eq!(results.len(), 1, "one return value"); - assert_eq!( - results[0].unwrap_i32(), - types::Errno::Ok as i32, - "double_int_return_float errno" - ); - - // The actual result is in memory: - let mem = shim_inst.get_memory(&mut store, "memory").unwrap(); - let mut result_bytes: [u8; 4] = [0, 0, 0, 0]; - mem.read(&store, result_location as usize, &mut result_bytes) - .unwrap(); - let result = f32::from_le_bytes(result_bytes); - assert_eq!((input * 2) as f32, result); -} - -fn store(engine: &Engine) -> Store { - Store::new(engine, Ctx) -} - -// Wiggle expects the caller to have an exported memory. Wasmtime can only -// provide this if the caller is a WebAssembly module, so we need to write -// a shim module: -fn shim_module(engine: &Engine) -> wasmtime::Module { - wasmtime::Module::new( - engine, - r#" - (module - (memory 1) - (export "memory" (memory 0)) - (import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32))) - (import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32))) - - (func $int_float_args_shim (param i32 f32) (result i32) - local.get 0 - local.get 1 - call $int_float_args - ) - (func $double_int_return_float_shim (param i32 i32) (result i32) - local.get 0 - local.get 1 - call $double_int_return_float - ) - (export "int_float_args_shim" (func $int_float_args_shim)) - (export "double_int_return_float_shim" (func $double_int_return_float_shim)) - ) - "#, - ) - .unwrap() -} diff --git a/scripts/publish.rs b/scripts/publish.rs index e121cedc3ad4..e717f17cb80f 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -40,12 +40,10 @@ const CRATES_TO_PUBLISH: &[&str] = &[ "cranelift-interpreter", "cranelift", "cranelift-jit", - // wig/wiggle + // wiggle "wiggle-generate", "wiggle-macro", "wiggle", - "wiggle-borrow", - "wasmtime-wiggle-macro", // wasi-common "wasi-common", "wasi-cap-std-sync", @@ -63,7 +61,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[ "wasmtime-jit", "wasmtime-cache", "wasmtime", - "wasmtime-wiggle", "wasmtime-wasi", "wasmtime-wasi-nn", "wasmtime-wasi-crypto", diff --git a/src/commands/run.rs b/src/commands/run.rs index d195c19ad5e7..2f3d46921058 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -439,7 +439,7 @@ fn populate_with_wasi( } #[cfg(feature = "wasi-nn")] { - wasmtime_wasi_nn::add_wasi_nn_to_linker(linker)?; + wasmtime_wasi_nn::add_to_linker(linker)?; store.data_mut().wasi_nn = Some(WasiNnCtx::new()?); } } From eb63bf5836c90b604b2d64ffd8af9e1d8047d82e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 07:26:41 -0700 Subject: [PATCH 56/90] Don't store `VMOffsets` in the pooling allocator --- crates/c-api/include/wasmtime.h | 2 +- .../runtime/src/instance/allocator/pooling.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 31753c533c5c..9d1f7219ebf9 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -102,7 +102,7 @@ * multithreading story of the Rust API for Wasmtime. All objects are safe to * send to other threads so long as user-specific data is also safe to send to * other threads. Functions are safe to call from any thread but some functions - * cannot be called concurrently. For examples functions which correspond to + * cannot be called concurrently. For example, functions which correspond to * `&T` in Rust can be called concurrently with any other methods that take * `&T`. Functions that take `&mut T` in Rust, however, cannot be called * concurrently with any other function (but can still be invoked on any diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs index 85ab572b2bac..d0f4782a5b0b 100644 --- a/crates/runtime/src/instance/allocator/pooling.rs +++ b/crates/runtime/src/instance/allocator/pooling.rs @@ -290,7 +290,6 @@ impl Default for PoolingAllocationStrategy { #[derive(Debug)] struct InstancePool { mapping: Mmap, - offsets: VMOffsets, instance_size: usize, max_instances: usize, free_list: Mutex>, @@ -335,7 +334,6 @@ impl InstancePool { let pool = Self { mapping, - offsets, instance_size, max_instances, free_list: Mutex::new((0..max_instances).collect()), @@ -346,7 +344,7 @@ impl InstancePool { // Use a default module to initialize the instances to start for i in 0..instance_limits.count as usize { - pool.initialize(i); + pool.initialize(module_limits, i); } Ok(pool) @@ -357,7 +355,7 @@ impl InstancePool { &mut *(self.mapping.as_mut_ptr().add(index * self.instance_size) as *mut Instance) } - fn initialize(&self, index: usize) { + fn initialize(&self, limits: &ModuleLimits, index: usize) { unsafe { let instance = self.instance(index); @@ -366,9 +364,12 @@ impl InstancePool { instance as _, Instance { module: self.empty_module.clone(), - offsets: self.offsets, - memories: PrimaryMap::with_capacity(self.offsets.num_defined_memories as usize), - tables: PrimaryMap::with_capacity(self.offsets.num_defined_tables as usize), + offsets: VMOffsets::new( + std::mem::size_of::<*const u8>() as u8, + &self.empty_module, + ), + memories: PrimaryMap::with_capacity(limits.memories as usize), + tables: PrimaryMap::with_capacity(limits.tables as usize), dropped_elements: EntitySet::new(), dropped_data: EntitySet::new(), host_state: Box::new(()), @@ -502,7 +503,8 @@ impl InstancePool { // should put everything back in a relatively pristine state for each // fresh allocation later on. instance.module = self.empty_module.clone(); - instance.offsets = self.offsets; + instance.offsets = + VMOffsets::new(std::mem::size_of::<*const u8>() as u8, &self.empty_module); self.free_list.lock().unwrap().push(index); } From a903696efa2a6defa035655d9f77a3fa9adbce91 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 07:32:42 -0700 Subject: [PATCH 57/90] Tweak some comments on `HostFunc` --- crates/wasmtime/src/func.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 7e58cbb5a84d..8e6cafac959b 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -1799,14 +1799,28 @@ macro_rules! impl_into_func { for_each_function_signature!(impl_into_func); -/// Represents a host function. +/// Representation of a host-defined function. /// -/// This differs from `Func` in that it is not associated with a `Store`. -/// Host functions are associated with a `Config`. +/// This is used for `Func::new` but also for `Linker`-defined functions. For +/// `Func::new` this is stored within a `Store`, and for `Linker`-defined +/// functions they wrap this up in `Arc` to enable shared ownership of this +/// across many stores. +/// +/// Technically this structure needs a `` type parameter to connect to the +/// `Store` itself, but that's an unsafe contract of using this for now +/// rather than part of the struct type (to avoid `Func` in the API). pub(crate) struct HostFunc { + // Owned `*mut VMContext` allocation. Deallocated when this `HostFunc` is + // dropped. instance: InstanceHandle, + // Trampoline to enter this function from Rust. trampoline: VMTrampoline, + // The loaded `ExportFunction` from the above `InstanceHandle` which has raw + // pointers and information about how to actually call this function (e.g. + // the actual address in JIT code and the vm shared function index). export: ExportFunction, + // Stored to unregister this function's signature with the engine when this + // is dropped. engine: Engine, } @@ -1840,6 +1854,8 @@ impl HostFunc { HostFunc::_new(engine, instance, trampoline) } + /// Requires that this function's signature is already registered within + /// `Engine`. This happens automatically during the above two constructors. fn _new(engine: &Engine, instance: InstanceHandle, trampoline: VMTrampoline) -> Self { let idx = EntityIndex::Function(FuncIndex::from_u32(0)); let export = match instance.lookup_by_declaration(&idx) { From 77bff4a717e632130a468f939a3e9c4581c2f408 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 07:32:51 -0700 Subject: [PATCH 58/90] Shuffle around #[cfg] for async --- crates/wasmtime/src/store.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index fa56935e4a25..80fe55e08a46 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -116,6 +116,7 @@ pub struct StoreInner { /// An adjustment to add to the fuel consumed value in `interrupts` above /// to get the true amount of fuel consumed. fuel_adj: i64, + #[cfg(feature = "async")] async_state: AsyncState, out_of_gas_behavior: OutOfGas, store_data: StoreData, @@ -125,11 +126,10 @@ pub struct StoreInner { data: ManuallyDrop, } +#[cfg(feature = "async")] struct AsyncState { - #[cfg(feature = "async")] current_suspend: UnsafeCell<*const wasmtime_fiber::Suspend, (), Result<(), Trap>>>, - #[cfg(feature = "async")] current_poll_cx: UnsafeCell<*mut Context<'static>>, } @@ -203,10 +203,9 @@ impl Store { memory_count: 0, table_count: 0, fuel_adj: 0, + #[cfg(feature = "async")] async_state: AsyncState { - #[cfg(feature = "async")] current_suspend: UnsafeCell::new(ptr::null()), - #[cfg(feature = "async")] current_poll_cx: UnsafeCell::new(ptr::null_mut()), }, out_of_gas_behavior: OutOfGas::Trap, From 624b0f5cb240a961b3dc6575d741e9b36952e49b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 07:54:00 -0700 Subject: [PATCH 59/90] More review comments --- crates/wasmtime/src/store.rs | 6 +- tests/all/externals.rs | 160 ++++++++++++++++++----------------- tests/all/gc.rs | 20 ++--- 3 files changed, 90 insertions(+), 96 deletions(-) diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 80fe55e08a46..4ec2e2cf3bbc 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -964,7 +964,7 @@ impl StoreOpaqueSend<'_> { // // While somewhat onerous it shouldn't be too too hard (the TLS bit is // the hardest bit so far). This does mean, though, that no user should - // ever hae to worry about the `Send`-ness of Wasmtime. If rustc says + // ever have to worry about the `Send`-ness of Wasmtime. If rustc says // it's ok, then it's ok. // // With all that in mind we unsafely assert here that wasmtime is @@ -1198,8 +1198,8 @@ impl Drop for Store { impl Drop for StoreInner { fn drop(&mut self) { - // NB it's important that this destructor does not access `T`. That is - // deallocated by `Drop for Store` above. + // NB it's important that this destructor does not access `self.data`. + // That is deallocated by `Drop for Store` above. let allocator = self.engine.allocator(); unsafe { diff --git a/tests/all/externals.rs b/tests/all/externals.rs index dede68bf83a1..1f925e3baf06 100644 --- a/tests/all/externals.rs +++ b/tests/all/externals.rs @@ -56,85 +56,87 @@ fn bad_tables() { assert_eq!(t.size(&store), 1); } -// #[test] -// fn cross_store() -> anyhow::Result<()> { -// let mut cfg = Config::new(); -// cfg.wasm_reference_types(true); -// let engine = Engine::new(&cfg)?; -// let store1 = Store::new(&engine); -// let store2 = Store::new(&engine); - -// // ============ Cross-store instantiation ============== - -// let func = Func::wrap(&store2, || {}); -// let ty = GlobalType::new(ValType::I32, Mutability::Const); -// let global = Global::new(&store2, ty, Val::I32(0))?; -// let ty = MemoryType::new(Limits::new(1, None)); -// let memory = Memory::new(&store2, ty)?; -// let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); -// let table = Table::new(&store2, ty, Val::FuncRef(None))?; - -// let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?; -// assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err()); - -// let need_global = Module::new(&engine, r#"(module (import "" "" (global i32)))"#)?; -// assert!(Instance::new(&store1, &need_global, &[global.into()]).is_err()); - -// let need_table = Module::new(&engine, r#"(module (import "" "" (table 1 funcref)))"#)?; -// assert!(Instance::new(&store1, &need_table, &[table.into()]).is_err()); - -// let need_memory = Module::new(&engine, r#"(module (import "" "" (memory 1)))"#)?; -// assert!(Instance::new(&store1, &need_memory, &[memory.into()]).is_err()); - -// // ============ Cross-store globals ============== - -// let store1val = Val::FuncRef(Some(Func::wrap(&store1, || {}))); -// let store2val = Val::FuncRef(Some(Func::wrap(&store2, || {}))); - -// let ty = GlobalType::new(ValType::FuncRef, Mutability::Var); -// assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err()); -// if let Ok(g) = Global::new(&store2, ty.clone(), store2val.clone()) { -// assert!(g.set(store1val.clone()).is_err()); -// } - -// // ============ Cross-store tables ============== - -// let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); -// assert!(Table::new(&store2, ty.clone(), store1val.clone()).is_err()); -// let t1 = Table::new(&store2, ty.clone(), store2val.clone())?; -// assert!(t1.set(0, store1val.clone()).is_err()); -// assert!(t1.grow(0, store1val.clone()).is_err()); -// assert!(t1.fill(0, store1val.clone(), 1).is_err()); -// let t2 = Table::new(&store1, ty.clone(), store1val.clone())?; -// assert!(Table::copy(&t1, 0, &t2, 0, 0).is_err()); - -// // ============ Cross-store funcs ============== - -// let module = Module::new(&engine, r#"(module (func (export "f") (param funcref)))"#)?; -// let s1_inst = Instance::new(&store1, &module, &[])?; -// let s2_inst = Instance::new(&store2, &module, &[])?; -// let s1_f = s1_inst.get_func("f").unwrap(); -// let s2_f = s2_inst.get_func("f").unwrap(); - -// assert!(s1_f.call(&[Val::FuncRef(None)]).is_ok()); -// assert!(s2_f.call(&[Val::FuncRef(None)]).is_ok()); -// assert!(s1_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_ok()); -// assert!(s1_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_err()); -// assert!(s2_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_err()); -// assert!(s2_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_ok()); - -// let s1_f_t = s1_f.typed::, ()>()?; -// let s2_f_t = s2_f.typed::, ()>()?; - -// assert!(s1_f_t.call(None).is_ok()); -// assert!(s2_f_t.call(None).is_ok()); -// assert!(s1_f_t.call(Some(s1_f.clone())).is_ok()); -// assert!(s1_f_t.call(Some(s2_f.clone())).is_err()); -// assert!(s2_f_t.call(Some(s1_f.clone())).is_err()); -// assert!(s2_f_t.call(Some(s2_f.clone())).is_ok()); - -// Ok(()) -// } +#[test] +fn cross_store() -> anyhow::Result<()> { + let mut cfg = Config::new(); + cfg.wasm_reference_types(true); + let engine = Engine::new(&cfg)?; + let mut store1 = Store::new(&engine, ()); + let mut store2 = Store::new(&engine, ()); + + // ============ Cross-store instantiation ============== + + let func = Func::wrap(&mut store2, || {}); + let ty = GlobalType::new(ValType::I32, Mutability::Const); + let global = Global::new(&mut store2, ty, Val::I32(0))?; + let ty = MemoryType::new(Limits::new(1, None)); + let memory = Memory::new(&mut store2, ty)?; + let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); + let table = Table::new(&mut store2, ty, Val::FuncRef(None))?; + + let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?; + assert!(Instance::new(&mut store1, &need_func, &[func.into()]).is_err()); + + let need_global = Module::new(&engine, r#"(module (import "" "" (global i32)))"#)?; + assert!(Instance::new(&mut store1, &need_global, &[global.into()]).is_err()); + + let need_table = Module::new(&engine, r#"(module (import "" "" (table 1 funcref)))"#)?; + assert!(Instance::new(&mut store1, &need_table, &[table.into()]).is_err()); + + let need_memory = Module::new(&engine, r#"(module (import "" "" (memory 1)))"#)?; + assert!(Instance::new(&mut store1, &need_memory, &[memory.into()]).is_err()); + + // ============ Cross-store globals ============== + + let store1val = Val::FuncRef(Some(Func::wrap(&mut store1, || {}))); + let store2val = Val::FuncRef(Some(Func::wrap(&mut store2, || {}))); + + let ty = GlobalType::new(ValType::FuncRef, Mutability::Var); + assert!(Global::new(&mut store2, ty.clone(), store1val.clone()).is_err()); + if let Ok(g) = Global::new(&mut store2, ty.clone(), store2val.clone()) { + assert!(g.set(&mut store2, store1val.clone()).is_err()); + } + + // ============ Cross-store tables ============== + + let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); + assert!(Table::new(&mut store2, ty.clone(), store1val.clone()).is_err()); + let t1 = Table::new(&mut store2, ty.clone(), store2val.clone())?; + assert!(t1.set(&mut store2, 0, store1val.clone()).is_err()); + assert!(t1.grow(&mut store2, 0, store1val.clone()).is_err()); + assert!(t1.fill(&mut store2, 0, store1val.clone(), 1).is_err()); + + // ============ Cross-store funcs ============== + + let module = Module::new(&engine, r#"(module (func (export "f") (param funcref)))"#)?; + let s1_inst = Instance::new(&mut store1, &module, &[])?; + let s2_inst = Instance::new(&mut store2, &module, &[])?; + let s1_f = s1_inst.get_func(&mut store1, "f").unwrap(); + let s2_f = s2_inst.get_func(&mut store2, "f").unwrap(); + + assert!(s1_f.call(&mut store1, &[Val::FuncRef(None)]).is_ok()); + assert!(s2_f.call(&mut store2, &[Val::FuncRef(None)]).is_ok()); + assert!(s1_f.call(&mut store1, &[Some(s1_f.clone()).into()]).is_ok()); + assert!(s1_f + .call(&mut store1, &[Some(s2_f.clone()).into()]) + .is_err()); + assert!(s2_f + .call(&mut store2, &[Some(s1_f.clone()).into()]) + .is_err()); + assert!(s2_f.call(&mut store2, &[Some(s2_f.clone()).into()]).is_ok()); + + let s1_f_t = s1_f.typed::, (), _>(&store1)?; + let s2_f_t = s2_f.typed::, (), _>(&store2)?; + + assert!(s1_f_t.call(&mut store1, None).is_ok()); + assert!(s2_f_t.call(&mut store2, None).is_ok()); + assert!(s1_f_t.call(&mut store1, Some(s1_f.clone())).is_ok()); + assert!(s1_f_t.call(&mut store1, Some(s2_f.clone())).is_err()); + assert!(s2_f_t.call(&mut store2, Some(s1_f.clone())).is_err()); + assert!(s2_f_t.call(&mut store2, Some(s2_f.clone())).is_ok()); + + Ok(()) +} #[test] fn get_set_externref_globals_via_api() -> anyhow::Result<()> { diff --git a/tests/all/gc.rs b/tests/all/gc.rs index 45ce2ab21687..7274f4adfdec 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -237,13 +237,9 @@ fn drop_externref_via_table_set() -> anyhow::Result<()> { #[test] fn global_drops_externref() -> anyhow::Result<()> { test_engine(&Engine::default())?; - test_engine(&Engine::new(Config::new().allocation_strategy( - InstanceAllocationStrategy::Pooling { - strategy: PoolingAllocationStrategy::NextAvailable, - module_limits: ModuleLimits::default(), - instance_limits: InstanceLimits::default(), - }, - ))?)?; + test_engine(&Engine::new( + Config::new().allocation_strategy(InstanceAllocationStrategy::pooling()), + )?)?; return Ok(()); @@ -288,13 +284,9 @@ fn global_drops_externref() -> anyhow::Result<()> { #[cfg(not(feature = "old-x86-backend"))] // uses atomic instrs not implemented here fn table_drops_externref() -> anyhow::Result<()> { test_engine(&Engine::default())?; - test_engine(&Engine::new(Config::new().allocation_strategy( - InstanceAllocationStrategy::Pooling { - strategy: PoolingAllocationStrategy::NextAvailable, - module_limits: ModuleLimits::default(), - instance_limits: InstanceLimits::default(), - }, - ))?)?; + test_engine(&Engine::new( + Config::new().allocation_strategy(InstanceAllocationStrategy::pooling()), + )?)?; return Ok(()); From 774d4c7df30dfe1d3ad1b5460e8f1095d6127560 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 08:07:13 -0700 Subject: [PATCH 60/90] Add a FIXME --- crates/wasmtime/src/instance.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 0ca5a166bce2..899cc581b121 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -658,6 +658,12 @@ impl<'a> Instantiator<'a> { // `instance` (nothing does that except `Drop` for `Store`), so this in // theory should be safe. let instance = unsafe { store.instance(instance).clone() }; + + // FIXME(#2916) we should ideally just store the `InstanceId` within the + // store itself. There should be no reason we have to allocate a hash + // map here and allocate a bunch of strings, that's quite wasteful if + // only one or two exports are used. Additionally this can push items + // into the `Store` which never end up getting used. let exports = instance .module() .exports From 82ccab101fbda993aee7d957cdd2408b0a959b00 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 09:37:46 -0700 Subject: [PATCH 61/90] Fix a test --- crates/runtime/src/instance/allocator/pooling.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs index d0f4782a5b0b..98f87d3aea1b 100644 --- a/crates/runtime/src/instance/allocator/pooling.rs +++ b/crates/runtime/src/instance/allocator/pooling.rs @@ -1382,19 +1382,6 @@ mod test { let instances = InstancePool::new(&module_limits, &instance_limits)?; - assert_eq!( - instances.offsets.pointer_size, - std::mem::size_of::<*const u8>() as u8 - ); - assert_eq!(instances.offsets.num_signature_ids, 0); - assert_eq!(instances.offsets.num_imported_functions, 0); - assert_eq!(instances.offsets.num_imported_tables, 0); - assert_eq!(instances.offsets.num_imported_memories, 0); - assert_eq!(instances.offsets.num_imported_globals, 0); - assert_eq!(instances.offsets.num_defined_functions, 0); - assert_eq!(instances.offsets.num_defined_tables, 1); - assert_eq!(instances.offsets.num_defined_memories, 1); - assert_eq!(instances.offsets.num_defined_globals, 0); // As of April 2021, the instance struct's size is largely below the size of a single page, // so it's safe to assume it's been rounded to the size of a single memory page here. assert_eq!(instances.instance_size, region::page::size()); From 3006d75d9c165fd1fca3ed11e43347992893da65 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 20 May 2021 10:40:08 -0700 Subject: [PATCH 62/90] wasmtime-wiggle: only generate one add_{module}_to_linker func inlines all of the add_{func}_to_linker(linker, module, field) functions, hard-coding the module and field name. When multiple witx modules define functions of the same name (e.g. foo::new and bar::new), the macro would generate multiple add_new_to_linker functions. We now have a way to alias individual functions, which is all these individual add_func_to_linker functions were out-lined for in the old scheme where that took a closure and not just a value. --- crates/wiggle/wasmtime/macro/src/lib.rs | 74 +++++++++---------------- 1 file changed, 26 insertions(+), 48 deletions(-) diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 77fca091daf7..ffa89908b8e5 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -72,10 +72,6 @@ fn generate_module( ctx_type: &syn::Type, async_conf: &AsyncConf, ) -> TokenStream2 { - let target_path = &target_conf.path; - let module_id = names.module(&module.name); - let target_module = quote! { #target_path::#module_id }; - let mut host_funcs = Vec::new(); let mut any_async = false; @@ -92,7 +88,14 @@ fn generate_module( } _ => {} } - generate_func(&f, names, &target_module, asyncness, &mut host_funcs); + generate_func( + module, + &f, + names, + &target_conf.path, + asyncness, + &mut host_funcs, + ); } let send_bound = if any_async { @@ -101,32 +104,6 @@ fn generate_module( quote! {} }; - let linker_add_definitions = host_funcs.iter().map(|(func_name, body)| { - let adder_func = format_ident!("add_{}_to_linker", names.func(&func_name)); - let docs = format!( - "Add the host function for `{}` to a linker under a given module and field name.", - func_name.as_str() - ); - quote! { - #[doc = #docs] - pub fn #adder_func(linker: &mut wasmtime::Linker, module: &str, field: &str) - -> anyhow::Result<()> - where - T: std::borrow::BorrowMut<#ctx_type> #send_bound - { - #body - } - } - }); - let linker_add_invocations = host_funcs.iter().map(|(func_name, _body)| { - let adder_func = format_ident!("add_{}_to_linker", names.func(&func_name)); - let module = module.name.as_str(); - let field = func_name.as_str(); - quote! { - #adder_func(linker, #module, #field)?; - } - }); - let type_name = module_conf.name.clone(); let add_to_linker = format_ident!("add_{}_to_linker", type_name); quote! { @@ -135,23 +112,27 @@ fn generate_module( where T: std::borrow::BorrowMut<#ctx_type> #send_bound { - #(#linker_add_invocations)* + #(#host_funcs)* Ok(()) } - - #(#linker_add_definitions)* } } fn generate_func( + module: &witx::Module, func: &witx::InterfaceFunc, names: &Names, - target_module: &TokenStream2, + target_path: &syn::Path, asyncness: Asyncness, - host_funcs: &mut Vec<(witx::Id, TokenStream2)>, + host_funcs: &mut Vec, ) { let rt = names.runtime_mod(); - let name_ident = names.func(&func.name); + + let module_str = module.name.as_str(); + let module_ident = names.module(&module.name); + + let field_str = func.name.as_str(); + let field_ident = names.func(&func.name); let (params, results) = func.wasm_signature(); @@ -208,7 +189,7 @@ fn generate_func( let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); (caller.data_mut().borrow_mut(), #runtime::WasmtimeGuestMemory::new(mem)) }; - match #target_module::#name_ident(ctx, &mem #(, #arg_names)*) #await_ { + match #target_path::#module_ident::#field_ident(ctx, &mem #(, #arg_names)*) #await_ { Ok(r) => Ok(<#ret_ty>::from(r)), Err(wasmtime_wiggle::Trap::String(err)) => Err(wasmtime::Trap::new(err)), Err(wasmtime_wiggle::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)), @@ -220,42 +201,39 @@ fn generate_func( let wrapper = format_ident!("func_wrap{}_async", params.len()); quote! { linker.#wrapper( - module, - field, + #module_str, + #field_str, move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| { Box::new(async move { #body }) }, )?; - Ok(()) } } Asyncness::Blocking => { quote! { linker.func_wrap( - module, - field, + #module_str, + #field_str, move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { let result = async { #body }; #rt::run_in_dummy_executor(result) }, )?; - Ok(()) } } Asyncness::Sync => { quote! { linker.func_wrap( - module, - field, + #module_str, + #field_str, move |mut caller: wasmtime::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> { #body }, )?; - Ok(()) } } }; - host_funcs.push((func.name.clone(), host_wrapper)); + host_funcs.push(host_wrapper); } From 516068f042395f2eb7766d3251e9c88872c04416 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 09:36:48 -0700 Subject: [PATCH 63/90] Don't be clever with 64-bit types in the C API Instead let's just go ahead and inflate the C API types to match the Rust API types representation. This way we don't have any artificial limits about wasmtime and we can happily use wasmtime in a process for the next 15 million years without any issues. --- crates/c-api/include/wasmtime/extern.h | 35 +++++++-- crates/c-api/include/wasmtime/func.h | 9 ++- crates/c-api/include/wasmtime/global.h | 6 +- crates/c-api/include/wasmtime/instance.h | 6 +- crates/c-api/include/wasmtime/linker.h | 2 +- crates/c-api/include/wasmtime/memory.h | 10 +-- crates/c-api/include/wasmtime/table.h | 10 +-- crates/c-api/include/wasmtime/val.h | 10 ++- crates/c-api/src/func.rs | 15 ++-- crates/c-api/src/global.rs | 6 +- crates/c-api/src/instance.rs | 6 +- crates/c-api/src/linker.rs | 4 +- crates/c-api/src/memory.rs | 10 +-- crates/c-api/src/table.rs | 10 +-- crates/c-api/src/val.rs | 30 ++++++-- crates/wasmtime/src/store/data.rs | 94 ++++-------------------- examples/externref.c | 16 ++-- examples/fuel.c | 4 +- examples/gcd.c | 4 +- examples/hello.c | 7 +- examples/interrupt.c | 4 +- examples/linking.c | 6 +- examples/memory.c | 84 ++++++++++----------- examples/multi.c | 8 +- examples/serialize.c | 7 +- examples/wasi/main.c | 2 +- 26 files changed, 196 insertions(+), 209 deletions(-) diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index 587f942dce2d..f3369d636010 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -21,7 +21,12 @@ extern "C" { /// interoperate between #wasmtime_store_t instances and if the wrong function /// is passed to the wrong store then it may trigger an assertion to abort the /// process. -typedef uint64_t wasmtime_func_t; +typedef struct wasmtime_func { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_func_t; /// \brief Representation of a table in Wasmtime. /// @@ -30,7 +35,12 @@ typedef uint64_t wasmtime_func_t; /// interoperate between #wasmtime_store_t instances and if the wrong table /// is passed to the wrong store then it may trigger an assertion to abort the /// process. -typedef uint64_t wasmtime_table_t; +typedef struct wasmtime_table { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_table_t; /// \brief Representation of a memory in Wasmtime. /// @@ -39,7 +49,12 @@ typedef uint64_t wasmtime_table_t; /// interoperate between #wasmtime_store_t instances and if the wrong memory /// is passed to the wrong store then it may trigger an assertion to abort the /// process. -typedef uint64_t wasmtime_memory_t; +typedef struct wasmtime_memory { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_memory_t; /// \brief Representation of a instance in Wasmtime. /// @@ -48,7 +63,12 @@ typedef uint64_t wasmtime_memory_t; /// interoperate between #wasmtime_store_t instances and if the wrong instance /// is passed to the wrong store then it may trigger an assertion to abort the /// process. -typedef uint64_t wasmtime_instance_t; +typedef struct wasmtime_instance { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_instance_t; /// \brief Representation of a global in Wasmtime. /// @@ -57,7 +77,12 @@ typedef uint64_t wasmtime_instance_t; /// interoperate between #wasmtime_store_t instances and if the wrong global /// is passed to the wrong store then it may trigger an assertion to abort the /// process. -typedef uint64_t wasmtime_global_t; +typedef struct wasmtime_global { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_global_t; /// \brief Disciminant of #wasmtime_extern_t typedef uint8_t wasmtime_extern_kind_t; diff --git a/crates/c-api/include/wasmtime/func.h b/crates/c-api/include/wasmtime/func.h index 150f458cc786..57d2c3951cdd 100644 --- a/crates/c-api/include/wasmtime/func.h +++ b/crates/c-api/include/wasmtime/func.h @@ -77,12 +77,13 @@ typedef wasm_trap_t* (*wasmtime_func_callback_t)( * * The returned function can only be used with the specified `store`. */ -WASM_API_EXTERN wasmtime_func_t wasmtime_func_new( +WASM_API_EXTERN void wasmtime_func_new( wasmtime_context_t *store, const wasm_functype_t* type, wasmtime_func_callback_t callback, void *env, - void (*finalizer)(void*) + void (*finalizer)(void*), + wasmtime_func_t *ret ); /** @@ -92,7 +93,7 @@ WASM_API_EXTERN wasmtime_func_t wasmtime_func_new( */ WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( const wasmtime_context_t *store, - wasmtime_func_t func + const wasmtime_func_t *func ); /** @@ -132,7 +133,7 @@ WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( */ WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call( wasmtime_context_t *store, - wasmtime_func_t func, + const wasmtime_func_t *func, const wasmtime_val_t *args, size_t nargs, wasmtime_val_t *results, diff --git a/crates/c-api/include/wasmtime/global.h b/crates/c-api/include/wasmtime/global.h index 86631a1dbf7c..4e244285a57c 100644 --- a/crates/c-api/include/wasmtime/global.h +++ b/crates/c-api/include/wasmtime/global.h @@ -47,7 +47,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new( */ WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( const wasmtime_context_t *store, - wasmtime_global_t global + const wasmtime_global_t *global ); /** @@ -62,7 +62,7 @@ WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( */ WASM_API_EXTERN void wasmtime_global_get( wasmtime_context_t *store, - wasmtime_global_t global, + const wasmtime_global_t *global, wasmtime_val_t *out ); @@ -80,7 +80,7 @@ WASM_API_EXTERN void wasmtime_global_get( */ WASM_API_EXTERN wasmtime_error_t *wasmtime_global_set( wasmtime_context_t *store, - wasmtime_global_t global, + const wasmtime_global_t *global, const wasmtime_val_t *val ); diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h index 123752ad254d..6058d2170144 100644 --- a/crates/c-api/include/wasmtime/instance.h +++ b/crates/c-api/include/wasmtime/instance.h @@ -98,7 +98,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( */ WASM_API_EXTERN wasmtime_instancetype_t *wasmtime_instance_type( const wasmtime_context_t *store, - wasmtime_instance_t instance + const wasmtime_instance_t *instance ); /** @@ -118,7 +118,7 @@ WASM_API_EXTERN wasmtime_instancetype_t *wasmtime_instance_type( */ WASM_API_EXTERN bool wasmtime_instance_export_get( wasmtime_context_t *store, - wasmtime_instance_t instance, + const wasmtime_instance_t *instance, const char *name, size_t name_len, wasmtime_extern_t *item @@ -144,7 +144,7 @@ WASM_API_EXTERN bool wasmtime_instance_export_get( */ WASM_API_EXTERN bool wasmtime_instance_export_nth( wasmtime_context_t *store, - wasmtime_instance_t instance, + const wasmtime_instance_t *instance, size_t index, char **name, size_t *name_len, diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index 08344f6db212..b8793a7878ac 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -125,7 +125,7 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance( wasmtime_context_t *store, const char *name, size_t name_len, - wasmtime_instance_t instance + const wasmtime_instance_t *instance ); /** diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h index 83dd82e83c3e..173639b74752 100644 --- a/crates/c-api/include/wasmtime/memory.h +++ b/crates/c-api/include/wasmtime/memory.h @@ -37,7 +37,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( */ WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type( const wasmtime_context_t *store, - wasmtime_memory_t memory + const wasmtime_memory_t *memory ); /** @@ -45,7 +45,7 @@ WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type( */ WASM_API_EXTERN uint8_t *wasmtime_memory_data( const wasmtime_context_t *store, - wasmtime_memory_t memory + const wasmtime_memory_t *memory ); /** @@ -53,7 +53,7 @@ WASM_API_EXTERN uint8_t *wasmtime_memory_data( */ WASM_API_EXTERN size_t wasmtime_memory_data_size( const wasmtime_context_t *store, - wasmtime_memory_t memory + const wasmtime_memory_t *memory ); /** @@ -61,7 +61,7 @@ WASM_API_EXTERN size_t wasmtime_memory_data_size( */ WASM_API_EXTERN uint32_t wasmtime_memory_size( const wasmtime_context_t *store, - wasmtime_memory_t memory + const wasmtime_memory_t *memory ); /** @@ -78,7 +78,7 @@ WASM_API_EXTERN uint32_t wasmtime_memory_size( */ WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow( wasmtime_context_t *store, - wasmtime_memory_t memory, + const wasmtime_memory_t *memory, uint32_t delta, uint32_t *prev_size ); diff --git a/crates/c-api/include/wasmtime/table.h b/crates/c-api/include/wasmtime/table.h index 84a99e675829..e159088d7d85 100644 --- a/crates/c-api/include/wasmtime/table.h +++ b/crates/c-api/include/wasmtime/table.h @@ -43,7 +43,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new( */ WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( const wasmtime_context_t *store, - wasmtime_table_t table + const wasmtime_table_t *table ); /** @@ -60,7 +60,7 @@ WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( */ WASM_API_EXTERN bool wasmtime_table_get( wasmtime_context_t *store, - wasmtime_table_t table, + const wasmtime_table_t *table, uint32_t index, wasmtime_val_t *val ); @@ -80,7 +80,7 @@ WASM_API_EXTERN bool wasmtime_table_get( */ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( wasmtime_context_t *store, - wasmtime_table_t table, + const wasmtime_table_t *table, uint32_t index, const wasmtime_val_t *value ); @@ -90,7 +90,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( */ WASM_API_EXTERN uint32_t wasmtime_table_size( const wasmtime_context_t *store, - wasmtime_table_t table + const wasmtime_table_t *table ); /** @@ -112,7 +112,7 @@ WASM_API_EXTERN uint32_t wasmtime_table_size( */ WASM_API_EXTERN wasmtime_error_t *wasmtime_table_grow( wasmtime_context_t *store, - wasmtime_table_t table, + const wasmtime_table_t *table, uint32_t delta, const wasmtime_val_t *init, uint32_t *prev_size diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h index 6acacd61c992..43b40ff77e8b 100644 --- a/crates/c-api/include/wasmtime/val.h +++ b/crates/c-api/include/wasmtime/val.h @@ -104,8 +104,14 @@ typedef union wasmtime_valunion { /// Field used if #wasmtime_val_t::kind is #WASMTIME_F64 float64_t f64; /// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF + /// + /// If this value represents a `ref.null func` value then the `store_id` field + /// is set to zero. wasmtime_func_t funcref; /// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF + /// + /// If this value represents a `ref.null extern` value then this pointer will + /// be `NULL`. wasmtime_externref_t *externref; /// Field used if #wasmtime_val_t::kind is #WASMTIME_V128 wasmtime_v128 v128; @@ -131,10 +137,6 @@ typedef struct wasmtime_val { wasmtime_valunion_t of; } wasmtime_val_t; -/// \brief value for #wasmtime_valunion::funcref indicating that the funcref is -/// null. -#define WASMTIME_FUNCREF_NULL ((uint64_t) 0xffffffffffffffff) - /** * \brief Delets an owned #wasmtime_val_t. * diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 6792aebe0a66..ae76c3739676 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -201,10 +201,11 @@ pub unsafe extern "C" fn wasmtime_func_new( ) -> Option>, data: *mut c_void, finalizer: Option, -) -> Func { + func: &mut Func, +) { let foreign = crate::ForeignData { data, finalizer }; let ty = ty.ty().ty.clone(); - Func::new(store, ty, move |caller, params, results| { + let f = Func::new(store, ty, move |caller, params, results| { let params = params .iter() .cloned() @@ -233,13 +234,14 @@ pub unsafe extern "C" fn wasmtime_func_new( results[i] = unsafe { result.to_val() }; } Ok(()) - }) + }); + *func = f; } #[no_mangle] pub unsafe extern "C" fn wasmtime_func_call( store: CStoreContextMut<'_>, - func: Func, + func: &Func, args: *const wasmtime_val_t, nargs: usize, results: *mut MaybeUninit, @@ -291,7 +293,10 @@ pub unsafe extern "C" fn wasmtime_func_call( } #[no_mangle] -pub extern "C" fn wasmtime_func_type(store: CStoreContext<'_>, func: Func) -> Box { +pub extern "C" fn wasmtime_func_type( + store: CStoreContext<'_>, + func: &Func, +) -> Box { Box::new(wasm_functype_t::new(func.ty(store))) } diff --git a/crates/c-api/src/global.rs b/crates/c-api/src/global.rs index 2bd33700b8a6..653855b52894 100644 --- a/crates/c-api/src/global.rs +++ b/crates/c-api/src/global.rs @@ -93,7 +93,7 @@ pub unsafe extern "C" fn wasmtime_global_new( #[no_mangle] pub extern "C" fn wasmtime_global_type( store: CStoreContext<'_>, - global: Global, + global: &Global, ) -> Box { Box::new(wasm_globaltype_t::new(global.ty(store))) } @@ -101,7 +101,7 @@ pub extern "C" fn wasmtime_global_type( #[no_mangle] pub extern "C" fn wasmtime_global_get( store: CStoreContextMut<'_>, - global: Global, + global: &Global, val: &mut MaybeUninit, ) { crate::initialize(val, wasmtime_val_t::from_val(global.get(store))) @@ -110,7 +110,7 @@ pub extern "C" fn wasmtime_global_get( #[no_mangle] pub unsafe extern "C" fn wasmtime_global_set( store: CStoreContextMut<'_>, - global: Global, + global: &Global, val: &wasmtime_val_t, ) -> Option> { handle_result(global.set(store, val.to_val()), |()| {}) diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs index e1bbf064e21d..4c17bc3a069e 100644 --- a/crates/c-api/src/instance.rs +++ b/crates/c-api/src/instance.rs @@ -136,7 +136,7 @@ pub(crate) fn handle_instantiate( #[no_mangle] pub extern "C" fn wasmtime_instance_type( store: CStoreContext<'_>, - instance: Instance, + instance: &Instance, ) -> Box { Box::new(wasmtime_instancetype_t::new(instance.ty(store))) } @@ -144,7 +144,7 @@ pub extern "C" fn wasmtime_instance_type( #[no_mangle] pub unsafe extern "C" fn wasmtime_instance_export_get( store: CStoreContextMut<'_>, - instance: Instance, + instance: &Instance, name: *const u8, name_len: usize, item: &mut MaybeUninit, @@ -166,7 +166,7 @@ pub unsafe extern "C" fn wasmtime_instance_export_get( #[no_mangle] pub unsafe extern "C" fn wasmtime_instance_export_nth( store: CStoreContextMut<'_>, - instance: Instance, + instance: &Instance, index: usize, name_ptr: &mut *const u8, name_len: &mut usize, diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 14c8b28531af..9dac41cb1d4d 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -71,11 +71,11 @@ pub unsafe extern "C" fn wasmtime_linker_define_instance( store: CStoreContextMut<'_>, name: *const u8, name_len: usize, - instance: Instance, + instance: &Instance, ) -> Option> { let linker = &mut linker.linker; let name = to_str!(name, name_len); - handle_result(linker.instance(store, name, instance), |_linker| ()) + handle_result(linker.instance(store, name, *instance), |_linker| ()) } #[no_mangle] diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs index 72c4c493a1b4..7cc227fea628 100644 --- a/crates/c-api/src/memory.rs +++ b/crates/c-api/src/memory.rs @@ -97,30 +97,30 @@ pub extern "C" fn wasmtime_memory_new( #[no_mangle] pub extern "C" fn wasmtime_memory_type( store: CStoreContext<'_>, - mem: Memory, + mem: &Memory, ) -> Box { Box::new(wasm_memorytype_t::new(mem.ty(store))) } #[no_mangle] -pub extern "C" fn wasmtime_memory_data(store: CStoreContext<'_>, mem: Memory) -> *const u8 { +pub extern "C" fn wasmtime_memory_data(store: CStoreContext<'_>, mem: &Memory) -> *const u8 { mem.data(store).as_ptr() } #[no_mangle] -pub extern "C" fn wasmtime_memory_data_size(store: CStoreContext<'_>, mem: Memory) -> usize { +pub extern "C" fn wasmtime_memory_data_size(store: CStoreContext<'_>, mem: &Memory) -> usize { mem.data(store).len() } #[no_mangle] -pub extern "C" fn wasmtime_memory_size(store: CStoreContext<'_>, mem: Memory) -> u32 { +pub extern "C" fn wasmtime_memory_size(store: CStoreContext<'_>, mem: &Memory) -> u32 { mem.size(store) } #[no_mangle] pub extern "C" fn wasmtime_memory_grow( store: CStoreContextMut<'_>, - mem: Memory, + mem: &Memory, delta: u32, prev_size: &mut u32, ) -> Option> { diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs index c2211046e9e4..304754fd757f 100644 --- a/crates/c-api/src/table.rs +++ b/crates/c-api/src/table.rs @@ -131,7 +131,7 @@ pub unsafe extern "C" fn wasmtime_table_new( #[no_mangle] pub unsafe extern "C" fn wasmtime_table_type( store: CStoreContext<'_>, - table: Table, + table: &Table, ) -> Box { Box::new(wasm_tabletype_t::new(table.ty(store))) } @@ -139,7 +139,7 @@ pub unsafe extern "C" fn wasmtime_table_type( #[no_mangle] pub extern "C" fn wasmtime_table_get( store: CStoreContextMut<'_>, - table: Table, + table: &Table, index: u32, ret: &mut MaybeUninit, ) -> bool { @@ -155,7 +155,7 @@ pub extern "C" fn wasmtime_table_get( #[no_mangle] pub unsafe extern "C" fn wasmtime_table_set( store: CStoreContextMut<'_>, - table: Table, + table: &Table, index: u32, val: &wasmtime_val_t, ) -> Option> { @@ -163,14 +163,14 @@ pub unsafe extern "C" fn wasmtime_table_set( } #[no_mangle] -pub extern "C" fn wasmtime_table_size(store: CStoreContext<'_>, table: Table) -> u32 { +pub extern "C" fn wasmtime_table_size(store: CStoreContext<'_>, table: &Table) -> u32 { table.size(store) } #[no_mangle] pub unsafe extern "C" fn wasmtime_table_grow( store: CStoreContextMut<'_>, - table: Table, + table: &Table, delta: u32, val: &wasmtime_val_t, prev_size: &mut u32, diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs index e597cf4d2808..c088390cb2d9 100644 --- a/crates/c-api/src/val.rs +++ b/crates/c-api/src/val.rs @@ -161,11 +161,18 @@ pub union wasmtime_val_union { pub i64: i64, pub f32: u32, pub f64: u64, - pub funcref: u64, + pub funcref: wasmtime_func_t, pub externref: ManuallyDrop>, pub v128: [u8; 16], } +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wasmtime_func_t { + pub store_id: u64, + pub index: usize, +} + impl wasmtime_val_t { pub fn from_val(val: Val) -> wasmtime_val_t { match val { @@ -195,8 +202,11 @@ impl wasmtime_val_t { kind: crate::WASMTIME_FUNCREF, of: wasmtime_val_union { funcref: match i { - Some(func) => unsafe { mem::transmute::(func) }, - None => u64::max_value(), + Some(func) => unsafe { mem::transmute::(func) }, + None => wasmtime_func_t { + store_id: 0, + index: 0, + }, }, }, }, @@ -216,11 +226,15 @@ impl wasmtime_val_t { crate::WASMTIME_F32 => Val::F32(self.of.f32), crate::WASMTIME_F64 => Val::F64(self.of.f64), crate::WASMTIME_V128 => Val::V128(u128::from_le_bytes(self.of.v128)), - crate::WASMTIME_FUNCREF => Val::FuncRef(if self.of.funcref == u64::max_value() { - None - } else { - Some(mem::transmute::(self.of.funcref)) - }), + crate::WASMTIME_FUNCREF => { + let store = self.of.funcref.store_id; + let index = self.of.funcref.index; + Val::FuncRef(if store == 0 && index == 0 { + None + } else { + Some(mem::transmute::(self.of.funcref)) + }) + } crate::WASMTIME_EXTERNREF => Val::ExternRef((*self.of.externref).clone()), other => panic!("unknown wasmtime_valkind_t: {}", other), } diff --git a/crates/wasmtime/src/store/data.rs b/crates/wasmtime/src/store/data.rs index d920c0e980d8..27e58a09864b 100644 --- a/crates/wasmtime/src/store/data.rs +++ b/crates/wasmtime/src/store/data.rs @@ -3,6 +3,7 @@ use crate::{StoreContext, StoreContextMut}; use std::convert::TryFrom; use std::fmt; use std::marker; +use std::num::NonZeroU64; use std::ops::Index; use std::sync::atomic::{AtomicU64, Ordering::SeqCst}; @@ -14,7 +15,7 @@ use std::sync::atomic::{AtomicU64, Ordering::SeqCst}; pub struct InstanceId(pub(super) usize); pub struct StoreData { - id: u64, + id: NonZeroU64, funcs: Vec, tables: Vec, globals: Vec, @@ -63,30 +64,16 @@ impl StoreData { pub fn new() -> StoreData { static NEXT_ID: AtomicU64 = AtomicU64::new(0); - // Currently we neither recycle ids nor do we allow overlap of ids (e.g. - // the ABA problem). We also only allocate a certain number of bits - // (controlled by INDEX_BITS below) for the id. Make sure that the id - // fits in the allocated bits (currently 40 bits). - // - // Note that this is the maximal number of `Store` instances that a - // process can make before it needs to be restarted. That means this - // needs to be pretty reasonable. At the assumption of creating 10k - // stores per second 40 bits allows that program to run for ~3.5 years. - // Hopefully programs don't run that long. - // - // If a program does indeed run that long then we rest the counter back - // to a known bad value (and it's basically impossible the counter will - // wrap back to zero inbetween this time) and then panic the current - // thread. + // Only allow 2^63 stores at which point we start panicking to prevent + // overflow. This should still last us to effectively the end of time. let id = NEXT_ID.fetch_add(1, SeqCst); - let upper_bits_used = (id >> (64 - INDEX_BITS)) != 0; - if upper_bits_used { - NEXT_ID.store(1 << (64 - INDEX_BITS), SeqCst); + if id & (1 << 63) != 0 { + NEXT_ID.store(1 << 63, SeqCst); panic!("store id allocator overflow"); } StoreData { - id, + id: NonZeroU64::new(id + 1).unwrap(), funcs: Vec::new(), tables: Vec::new(), globals: Vec::new(), @@ -169,77 +156,28 @@ where } } -// NB the repre(transparent) here is for the usage of this throughout the C API. -// If the representation here changes then the C API will need changing as well. -#[repr(transparent)] +#[repr(C)] // used by reference in the C API pub struct Stored { - // See documentation below on `INDEX_BITS` for how this is interpreted. - bits: u64, + store_id: NonZeroU64, + index: usize, _marker: marker::PhantomData T>, } -// This is the maximal number of bits that the index of an item within a store -// can take up. As this is set to 24 that allows for 16 million items. Note -// that this is not a limit on something like the number of functions within an -// instance, only a limit on the number of externally referenced items in a -// Store. For example this is more equivalent to exported functions of a module -// rather than functions themselves. -// -// The reason for this limitation is that we want `Stored` to fit into a -// 64-bit value (for the C API). This 64-bit value gives us limited, well, uh, -// bits, to work with. We need to pack both a "store id" as well as an index -// within the store into those 64 bits. Given that there's no implementation of -// recycling store IDs at this time it also means that the number of bits -// allocated to the store id represents the maximal number of stores that a -// process can create for its entire lifetime. -// -// These factors led to the choice of bits here for this. This can be moved -// around a bit, but the hope is that this is good enough for all practical -// users. -// -// The choice of 24 means that the limitations of wasmtime are: -// -// * 24 bits for the index, meaning 16 million items maximum. As noted above -// this is 16 million *host references* to wasm items, so this is akin to -// creating 16 million instances within one store or creating an instance -// that has 16 million exported function. If 24 bits isn't enough then we -// may need to look into compile-time options to change this perhaps. -// -// * 40 bits for the store id. This is a whole lot more bits than the index, -// but intentionally so. As the maximal number of stores for the entire -// process that's far more limiting than the number of items within a store -// (which are typically drastically lower than 16 million and/or limited via -// other means, e.g. wasm module validation, instance limits, etc). -// -// So all-in-all we try to maximize the number of store bits without placing -// too many restrictions on the number of items within a store. Using 40 -// bits practically means that if you create 10k stores a second your program -// can run for ~3.5 years. Hopefully that's enough? -// -// If we didn't need to be clever in the C API and returned structs-by-value -// instead of returning 64-bit integers then we could just change this to a -// u64/usize pair which would solve all of these problems. Hopefully, though, -// no one will ever run into these limits... -const INDEX_BITS: usize = 24; - impl Stored { - fn new(store_id: u64, index: usize) -> Stored { - let masked_index = ((1 << INDEX_BITS) - 1) & index; - if masked_index != index { - panic!("too many items have been allocated into the store"); - } + fn new(store_id: NonZeroU64, index: usize) -> Stored { Stored { - bits: (store_id << INDEX_BITS) | u64::try_from(masked_index).unwrap(), + store_id, + index, _marker: marker::PhantomData, } } - fn store_id(&self) -> u64 { - self.bits >> INDEX_BITS + fn store_id(&self) -> NonZeroU64 { + self.store_id } fn index(&self) -> usize { - usize::try_from(self.bits & ((1 << INDEX_BITS) - 1)).unwrap() + self.index } } diff --git a/examples/externref.c b/examples/externref.c index bf3611389a23..f240cdfb4a0b 100644 --- a/examples/externref.c +++ b/examples/externref.c @@ -97,7 +97,7 @@ int main() { wasmtime_extern_t item; // Lookup the `table` export. - ok = wasmtime_instance_export_get(context, instance, "table", strlen("table"), &item); + ok = wasmtime_instance_export_get(context, &instance, "table", strlen("table"), &item); assert(ok); assert(item.kind == WASMTIME_EXTERN_TABLE); @@ -105,13 +105,13 @@ int main() { wasmtime_val_t externref_val; externref_val.kind = WASMTIME_EXTERNREF; externref_val.of.externref = externref; - error = wasmtime_table_set(context, item.of.table, 3, &externref_val); + error = wasmtime_table_set(context, &item.of.table, 3, &externref_val); if (error != NULL) exit_with_error("failed to set table", error, NULL); // `table[3]` should now be our `externref`. wasmtime_val_t elem; - ok = wasmtime_table_get(context, item.of.table, 3, &elem); + ok = wasmtime_table_get(context, &item.of.table, 3, &elem); assert(ok); assert(elem.kind == WASMTIME_EXTERNREF); assert(strcmp((char*)wasmtime_externref_data(elem.of.externref), "Hello, World!") == 0); @@ -120,18 +120,18 @@ int main() { printf("Touching `externref` global...\n"); // Lookup the `global` export. - ok = wasmtime_instance_export_get(context, instance, "global", strlen("global"), &item); + ok = wasmtime_instance_export_get(context, &instance, "global", strlen("global"), &item); assert(ok); assert(item.kind == WASMTIME_EXTERN_GLOBAL); // Set the global to our `externref`. - error = wasmtime_global_set(context, item.of.global, &externref_val); + error = wasmtime_global_set(context, &item.of.global, &externref_val); if (error != NULL) exit_with_error("failed to set global", error, NULL); // Get the global, and it should return our `externref` again. wasmtime_val_t global_val; - wasmtime_global_get(context, item.of.global, &global_val); + wasmtime_global_get(context, &item.of.global, &global_val); assert(global_val.kind == WASMTIME_EXTERNREF); assert(strcmp((char*)wasmtime_externref_data(elem.of.externref), "Hello, World!") == 0); wasmtime_val_delete(&global_val); @@ -139,13 +139,13 @@ int main() { printf("Calling `externref` func...\n"); // Lookup the `func` export. - ok = wasmtime_instance_export_get(context, instance, "func", strlen("func"), &item); + ok = wasmtime_instance_export_get(context, &instance, "func", strlen("func"), &item); assert(ok); assert(item.kind == WASMTIME_EXTERN_FUNC); // And call it! wasmtime_val_t results[1]; - error = wasmtime_func_call(context, item.of.func, &externref_val, 1, results, 1, &trap); + error = wasmtime_func_call(context, &item.of.func, &externref_val, 1, results, 1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); diff --git a/examples/fuel.c b/examples/fuel.c index e45368d3b163..3c5558cb3f8b 100644 --- a/examples/fuel.c +++ b/examples/fuel.c @@ -82,7 +82,7 @@ int main() { // Lookup our `fibonacci` export function wasmtime_extern_t fib; - bool ok = wasmtime_instance_export_get(context, instance, "fibonacci", strlen("fibonacci"), &fib); + bool ok = wasmtime_instance_export_get(context, &instance, "fibonacci", strlen("fibonacci"), &fib); assert(ok); assert(fib.kind == WASMTIME_EXTERN_FUNC); @@ -94,7 +94,7 @@ int main() { params[0].kind = WASMTIME_I32; params[0].of.i32 = n; wasmtime_val_t results[1]; - error = wasmtime_func_call(context, fib.of.func, params, 1, results, 1, &trap); + error = wasmtime_func_call(context, &fib.of.func, params, 1, results, 1, &trap); if (error != NULL || trap != NULL) { printf("Exhausted fuel computing fib(%d)\n", n); break; diff --git a/examples/gcd.c b/examples/gcd.c index eccdc3acab0b..714eb96263a1 100644 --- a/examples/gcd.c +++ b/examples/gcd.c @@ -73,7 +73,7 @@ int main() { // Lookup our `gcd` export function wasmtime_extern_t gcd; - bool ok = wasmtime_instance_export_get(context, instance, "gcd", 3, &gcd); + bool ok = wasmtime_instance_export_get(context, &instance, "gcd", 3, &gcd); assert(ok); assert(gcd.kind == WASMTIME_EXTERN_FUNC); @@ -86,7 +86,7 @@ int main() { params[1].kind = WASMTIME_I32; params[1].of.i32 = b; wasmtime_val_t results[1]; - error = wasmtime_func_call(context, gcd.of.func, params, 2, results, 1, &trap); + error = wasmtime_func_call(context, &gcd.of.func, params, 2, results, 1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call gcd", error, trap); assert(results[0].kind == WASMTIME_I32); diff --git a/examples/hello.c b/examples/hello.c index ba1411a82928..5c24e18db370 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -87,7 +87,8 @@ int main() { // for now). printf("Creating callback...\n"); wasm_functype_t *hello_ty = wasm_functype_new_0_0(); - wasmtime_func_t hello = wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL); + wasmtime_func_t hello; + wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL, &hello); // With our callback function we can now instantiate the compiled module, // giving us an instance we can then execute exports from. Note that @@ -106,13 +107,13 @@ int main() { // Lookup our `run` export function printf("Extracting export...\n"); wasmtime_extern_t run; - bool ok = wasmtime_instance_export_get(context, instance, "run", 3, &run); + bool ok = wasmtime_instance_export_get(context, &instance, "run", 3, &run); assert(ok); assert(run.kind == WASMTIME_EXTERN_FUNC); // And call it! printf("Calling export...\n"); - error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); + error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); diff --git a/examples/interrupt.c b/examples/interrupt.c index 69f4ca565eb7..41f25d7f93f0 100644 --- a/examples/interrupt.c +++ b/examples/interrupt.c @@ -103,7 +103,7 @@ int main() { // Lookup our `run` export function wasmtime_extern_t run; - bool ok = wasmtime_instance_export_get(context, instance, "run", 3, &run); + bool ok = wasmtime_instance_export_get(context, &instance, "run", 3, &run); assert(ok); assert(run.kind == WASMTIME_EXTERN_FUNC); @@ -112,7 +112,7 @@ int main() { // And call it! printf("Entering infinite loop...\n"); - error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); + error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap); assert(error == NULL); assert(trap != NULL); printf("Got a trap!...\n"); diff --git a/examples/linking.c b/examples/linking.c index 01de2ce37e18..330102c5c3c8 100644 --- a/examples/linking.c +++ b/examples/linking.c @@ -81,7 +81,7 @@ int main() { exit_with_error("failed to instantiate linking2", error, trap); // Register our new `linking2` instance with the linker - error = wasmtime_linker_define_instance(linker, context, "linking2", strlen("linking2"), linking2); + error = wasmtime_linker_define_instance(linker, context, "linking2", strlen("linking2"), &linking2); if (error != NULL) exit_with_error("failed to link linking2", error, NULL); @@ -93,10 +93,10 @@ int main() { // Lookup our `run` export function wasmtime_extern_t run; - bool ok = wasmtime_instance_export_get(context, linking1, "run", 3, &run); + bool ok = wasmtime_instance_export_get(context, &linking1, "run", 3, &run); assert(ok); assert(run.kind == WASMTIME_EXTERN_FUNC); - error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); + error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call run", error, trap); diff --git a/examples/memory.c b/examples/memory.c index 2fd906ca5dde..e9cb221cde2c 100644 --- a/examples/memory.c +++ b/examples/memory.c @@ -38,7 +38,7 @@ void check(bool success) { } void check_call(wasmtime_context_t *store, - wasmtime_func_t func, + wasmtime_func_t *func, const wasmtime_val_t* args, size_t nargs, int32_t expected) { @@ -55,18 +55,18 @@ void check_call(wasmtime_context_t *store, } } -void check_call0(wasmtime_context_t *store, wasmtime_func_t func, int32_t expected) { +void check_call0(wasmtime_context_t *store, wasmtime_func_t *func, int32_t expected) { check_call(store, func, NULL, 0, expected); } -void check_call1(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg, int32_t expected) { +void check_call1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg, int32_t expected) { wasmtime_val_t args[1]; args[0].kind = WASMTIME_I32; args[0].of.i32 = arg; check_call(store, func, args, 1, expected); } -void check_call2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, int32_t arg2, int32_t expected) { +void check_call2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2, int32_t expected) { wasmtime_val_t args[2]; args[0].kind = WASMTIME_I32; args[0].of.i32 = arg1; @@ -75,14 +75,14 @@ void check_call2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, check_call(store, func, args, 2, expected); } -void check_ok(wasmtime_context_t *store, wasmtime_func_t func, const wasmtime_val_t* args, size_t nargs) { +void check_ok(wasmtime_context_t *store, wasmtime_func_t *func, const wasmtime_val_t* args, size_t nargs) { wasm_trap_t *trap = NULL; wasmtime_error_t *error = wasmtime_func_call(store, func, args, nargs, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); } -void check_ok2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, int32_t arg2) { +void check_ok2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2) { wasmtime_val_t args[2]; args[0].kind = WASMTIME_I32; args[0].of.i32 = arg1; @@ -92,7 +92,7 @@ void check_ok2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, in } void check_trap(wasmtime_context_t *store, - wasmtime_func_t func, + wasmtime_func_t *func, const wasmtime_val_t *args, size_t nargs, size_t num_results) { @@ -109,14 +109,14 @@ void check_trap(wasmtime_context_t *store, wasm_trap_delete(trap); } -void check_trap1(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg) { +void check_trap1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg) { wasmtime_val_t args[1]; args[0].kind = WASMTIME_I32; args[0].of.i32 = arg; check_trap(store, func, args, 1, 1); } -void check_trap2(wasmtime_context_t *store, wasmtime_func_t func, int32_t arg1, int32_t arg2) { +void check_trap2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2) { wasmtime_val_t args[2]; args[0].kind = WASMTIME_I32; args[0].of.i32 = arg1; @@ -179,63 +179,63 @@ int main(int argc, const char* argv[]) { wasmtime_func_t size_func, load_func, store_func; wasmtime_extern_t item; bool ok; - ok = wasmtime_instance_export_get(context, instance, "memory", strlen("memory"), &item); + ok = wasmtime_instance_export_get(context, &instance, "memory", strlen("memory"), &item); assert(ok && item.kind == WASMTIME_EXTERN_MEMORY); memory = item.of.memory; - ok = wasmtime_instance_export_get(context, instance, "size", strlen("size"), &item); + ok = wasmtime_instance_export_get(context, &instance, "size", strlen("size"), &item); assert(ok && item.kind == WASMTIME_EXTERN_FUNC); size_func = item.of.func; - ok = wasmtime_instance_export_get(context, instance, "load", strlen("load"), &item); + ok = wasmtime_instance_export_get(context, &instance, "load", strlen("load"), &item); assert(ok && item.kind == WASMTIME_EXTERN_FUNC); load_func = item.of.func; - ok = wasmtime_instance_export_get(context, instance, "store", strlen("store"), &item); + ok = wasmtime_instance_export_get(context, &instance, "store", strlen("store"), &item); assert(ok && item.kind == WASMTIME_EXTERN_FUNC); store_func = item.of.func; // Check initial memory. printf("Checking memory...\n"); - check(wasmtime_memory_size(context, memory) == 2); - check(wasmtime_memory_data_size(context, memory) == 0x20000); - check(wasmtime_memory_data(context, memory)[0] == 0); - check(wasmtime_memory_data(context, memory)[0x1000] == 1); - check(wasmtime_memory_data(context, memory)[0x1003] == 4); - - check_call0(context, size_func, 2); - check_call1(context, load_func, 0, 0); - check_call1(context, load_func, 0x1000, 1); - check_call1(context, load_func, 0x1003, 4); - check_call1(context, load_func, 0x1ffff, 0); - check_trap1(context, load_func, 0x20000); + check(wasmtime_memory_size(context, &memory) == 2); + check(wasmtime_memory_data_size(context, &memory) == 0x20000); + check(wasmtime_memory_data(context, &memory)[0] == 0); + check(wasmtime_memory_data(context, &memory)[0x1000] == 1); + check(wasmtime_memory_data(context, &memory)[0x1003] == 4); + + check_call0(context, &size_func, 2); + check_call1(context, &load_func, 0, 0); + check_call1(context, &load_func, 0x1000, 1); + check_call1(context, &load_func, 0x1003, 4); + check_call1(context, &load_func, 0x1ffff, 0); + check_trap1(context, &load_func, 0x20000); // Mutate memory. printf("Mutating memory...\n"); - wasmtime_memory_data(context, memory)[0x1003] = 5; - check_ok2(context, store_func, 0x1002, 6); - check_trap2(context, store_func, 0x20000, 0); + wasmtime_memory_data(context, &memory)[0x1003] = 5; + check_ok2(context, &store_func, 0x1002, 6); + check_trap2(context, &store_func, 0x20000, 0); - check(wasmtime_memory_data(context, memory)[0x1002] == 6); - check(wasmtime_memory_data(context, memory)[0x1003] == 5); - check_call1(context, load_func, 0x1002, 6); - check_call1(context, load_func, 0x1003, 5); + check(wasmtime_memory_data(context, &memory)[0x1002] == 6); + check(wasmtime_memory_data(context, &memory)[0x1003] == 5); + check_call1(context, &load_func, 0x1002, 6); + check_call1(context, &load_func, 0x1003, 5); // Grow memory. printf("Growing memory...\n"); uint32_t old_size; - error = wasmtime_memory_grow(context, memory, 1, &old_size); + error = wasmtime_memory_grow(context, &memory, 1, &old_size); if (error != NULL) exit_with_error("failed to grow memory", error, trap); - check(wasmtime_memory_size(context, memory) == 3); - check(wasmtime_memory_data_size(context, memory) == 0x30000); + check(wasmtime_memory_size(context, &memory) == 3); + check(wasmtime_memory_data_size(context, &memory) == 0x30000); - check_call1(context, load_func, 0x20000, 0); - check_ok2(context, store_func, 0x20000, 0); - check_trap1(context, load_func, 0x30000); - check_trap2(context, store_func, 0x30000, 0); + check_call1(context, &load_func, 0x20000, 0); + check_ok2(context, &store_func, 0x20000, 0); + check_trap1(context, &load_func, 0x30000); + check_trap2(context, &store_func, 0x30000, 0); - error = wasmtime_memory_grow(context, memory, 1, &old_size); + error = wasmtime_memory_grow(context, &memory, 1, &old_size); assert(error != NULL); wasmtime_error_delete(error); - error = wasmtime_memory_grow(context, memory, 0, &old_size); + error = wasmtime_memory_grow(context, &memory, 0, &old_size); if (error != NULL) exit_with_error("failed to grow memory", error, trap); @@ -248,7 +248,7 @@ int main(int argc, const char* argv[]) { if (error != NULL) exit_with_error("failed to create memory", error, trap); wasm_memorytype_delete(memorytype); - check(wasmtime_memory_size(context, memory2) == 5); + check(wasmtime_memory_size(context, &memory2) == 5); // Shut down. printf("Shutting down...\n"); diff --git a/examples/multi.c b/examples/multi.c index 2cbf03fe7b6a..b4b962ef2eaf 100644 --- a/examples/multi.c +++ b/examples/multi.c @@ -115,8 +115,8 @@ int main(int argc, const char* argv[]) { wasm_valtype_new_i64(), wasm_valtype_new_i32() ); - wasmtime_func_t callback_func = - wasmtime_func_new(context, callback_type, callback, NULL, NULL); + wasmtime_func_t callback_func; + wasmtime_func_new(context, callback_type, callback, NULL, NULL, &callback_func); wasm_functype_delete(callback_type); // Instantiate. @@ -134,7 +134,7 @@ int main(int argc, const char* argv[]) { // Extract export. printf("Extracting export...\n"); wasmtime_extern_t run; - bool ok = wasmtime_instance_export_get(context, instance, "g", 1, &run); + bool ok = wasmtime_instance_export_get(context, &instance, "g", 1, &run); assert(ok); assert(run.kind == WASMTIME_EXTERN_FUNC); @@ -146,7 +146,7 @@ int main(int argc, const char* argv[]) { args[1].kind = WASMTIME_I64; args[1].of.i64 = 2; wasmtime_val_t results[2]; - error = wasmtime_func_call(context, run.of.func, args, 2, results, 2, &trap); + error = wasmtime_func_call(context, &run.of.func, args, 2, results, 2, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call run", error, trap); diff --git a/examples/serialize.c b/examples/serialize.c index 7951731c6451..e4f444764603 100644 --- a/examples/serialize.c +++ b/examples/serialize.c @@ -109,7 +109,8 @@ int deserialize(wasm_byte_vec_t* buffer) { // function above. printf("Creating callback...\n"); wasm_functype_t *hello_ty = wasm_functype_new_0_0(); - wasmtime_func_t hello = wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL); + wasmtime_func_t hello; + wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL, &hello); // With our callback function we can now instantiate the compiled module, // giving us an instance we can then execute exports from. Note that @@ -128,13 +129,13 @@ int deserialize(wasm_byte_vec_t* buffer) { // Lookup our `run` export function wasmtime_extern_t run; - bool ok = wasmtime_instance_export_get(context, instance, "run", 3, &run); + bool ok = wasmtime_instance_export_get(context, &instance, "run", 3, &run); assert(ok); assert(run.kind == WASMTIME_EXTERN_FUNC); // And call it! printf("Calling export...\n"); - error = wasmtime_func_call(context, run.of.func, NULL, 0, NULL, 0, &trap); + error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); diff --git a/examples/wasi/main.c b/examples/wasi/main.c index c1d23055e174..b44e44308991 100644 --- a/examples/wasi/main.c +++ b/examples/wasi/main.c @@ -89,7 +89,7 @@ int main() { if (error != NULL) exit_with_error("failed to locate default export for module", error, NULL); - error = wasmtime_func_call(context, func, NULL, 0, NULL, 0, &trap); + error = wasmtime_func_call(context, &func, NULL, 0, NULL, 0, &trap); if (error != NULL || trap != NULL) exit_with_error("error calling default export", error, trap); From 40ccaeb3ae49ef1d187e44906da4aafe0198cba1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 12:41:07 -0700 Subject: [PATCH 64/90] Remove unused import --- crates/wasmtime/src/store/data.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/wasmtime/src/store/data.rs b/crates/wasmtime/src/store/data.rs index 27e58a09864b..789c9fb111d3 100644 --- a/crates/wasmtime/src/store/data.rs +++ b/crates/wasmtime/src/store/data.rs @@ -1,6 +1,5 @@ use crate::store::StoreOpaque; use crate::{StoreContext, StoreContextMut}; -use std::convert::TryFrom; use std::fmt; use std::marker; use std::num::NonZeroU64; From 176c78534d035676d00b5d2428906efeb65b3887 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 12:42:06 -0700 Subject: [PATCH 65/90] Document new param in wasmtime_func_new --- crates/c-api/include/wasmtime/func.h | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/c-api/include/wasmtime/func.h b/crates/c-api/include/wasmtime/func.h index 57d2c3951cdd..f254d922aa0f 100644 --- a/crates/c-api/include/wasmtime/func.h +++ b/crates/c-api/include/wasmtime/func.h @@ -74,6 +74,7 @@ typedef wasm_trap_t* (*wasmtime_func_callback_t)( * \param env host-specific data passed to the callback invocation, can be * `NULL` * \param finalizer optional finalizer for `env`, can be `NULL` + * \param ret the #wasmtime_func_t return value to be filled in. * * The returned function can only be used with the specified `store`. */ From c208ebbc1768cf03cc4b23292c4f310236fe7ae3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 13:32:31 -0700 Subject: [PATCH 66/90] Fix fib-debug example --- examples/fib-debug/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fib-debug/main.c b/examples/fib-debug/main.c index a8cd38879d76..20f0b0acb2ad 100644 --- a/examples/fib-debug/main.c +++ b/examples/fib-debug/main.c @@ -59,7 +59,7 @@ int main(int argc, const char* argv[]) { // Extract export. wasmtime_extern_t fib; - bool ok = wasmtime_instance_export_get(context, instance, "fib", 3, &fib); + bool ok = wasmtime_instance_export_get(context, &instance, "fib", 3, &fib); assert(ok); // Call. @@ -68,7 +68,7 @@ int main(int argc, const char* argv[]) { params[0].kind = WASMTIME_I32; params[0].of.i32 = 6; wasmtime_val_t results[1]; - error = wasmtime_func_call(context, fib.of.func, params, 1, results, 1, &trap); + error = wasmtime_func_call(context, &fib.of.func, params, 1, results, 1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); From 80eeff7f161251981e663a8eacf79ad9e669343c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 13:41:38 -0700 Subject: [PATCH 67/90] Install another package --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c0e84ea1c368..30d31e5905ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -96,7 +96,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: true - - run: sudo apt-get update -y && sudo apt-get install -y libclang1-9 + - run: sudo apt-get update -y && sudo apt-get install -y libclang1-9 libclang-cpp9 - run: curl -L https://doxygen.nl/files/doxygen-1.9.1.linux.bin.tar.gz | tar xzf - - run: echo "`pwd`/doxygen-1.9.1/bin" >> $GITHUB_PATH - run: cd crates/c-api && doxygen doxygen.conf From 5bb3c63864033ac6fbd85e22a0236c88f424abc7 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 20 May 2021 15:09:21 -0700 Subject: [PATCH 68/90] explicit type param to wasi_nn for now --- src/commands/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/run.rs b/src/commands/run.rs index 2f3d46921058..9a65184fa64f 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -439,7 +439,7 @@ fn populate_with_wasi( } #[cfg(feature = "wasi-nn")] { - wasmtime_wasi_nn::add_to_linker(linker)?; + wasmtime_wasi_nn::add_to_linker::<_, WasiNnCtx>(linker)?; store.data_mut().wasi_nn = Some(WasiNnCtx::new()?); } } From 261b1062ed513d2f70bc1ba2cddc0f63d7c817db Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 15:24:11 -0700 Subject: [PATCH 69/90] Tweak LLDB/LLVM on CI --- .github/actions/define-llvm-env/README.md | 3 --- .github/actions/define-llvm-env/action.yml | 6 ------ .github/actions/define-llvm-env/main.js | 19 ------------------- .github/workflows/main.yml | 9 ++------- 4 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 .github/actions/define-llvm-env/README.md delete mode 100644 .github/actions/define-llvm-env/action.yml delete mode 100755 .github/actions/define-llvm-env/main.js diff --git a/.github/actions/define-llvm-env/README.md b/.github/actions/define-llvm-env/README.md deleted file mode 100644 index 3a9f5fc8ec79..000000000000 --- a/.github/actions/define-llvm-env/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# define-llvm-env - -Defines `DWARFDUMP` and `LLDB` path executable. diff --git a/.github/actions/define-llvm-env/action.yml b/.github/actions/define-llvm-env/action.yml deleted file mode 100644 index 168054a04fe5..000000000000 --- a/.github/actions/define-llvm-env/action.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: 'Set up a DWARFDUMP env' -description: 'Set up a DWARFDUMP env (see tests/all/debug/dump.rs)' - -runs: - using: node12 - main: 'main.js' diff --git a/.github/actions/define-llvm-env/main.js b/.github/actions/define-llvm-env/main.js deleted file mode 100755 index 8fc14e5d184c..000000000000 --- a/.github/actions/define-llvm-env/main.js +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); - -function set_env(name, val) { - fs.appendFileSync(process.env['GITHUB_ENV'], `${name}=${val}\n`) -} - -// On OSX pointing to brew's LLVM location. -if (process.platform == 'darwin') { - set_env("DWARFDUMP", "/usr/local/opt/llvm/bin/llvm-dwarfdump"); - set_env("LLDB", "/usr/local/opt/llvm/bin/lldb"); -} - -// On Linux pointing to specific version -if (process.platform == 'linux') { - set_env("DWARFDUMP", "/usr/bin/llvm-dwarfdump-9"); - set_env("LLDB", "/usr/bin/lldb-9"); -} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 30d31e5905ad..e66fddf06ec5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -235,7 +235,6 @@ jobs: - uses: ./.github/actions/install-rust with: toolchain: ${{ matrix.rust }} - - uses: ./.github/actions/define-llvm-env - name: Install libclang # Note: libclang is pre-installed on the macOS and linux images. @@ -278,21 +277,18 @@ jobs: # Test debug (DWARF) related functionality. - run: | - sudo apt-get update && sudo apt-get install -y gdb lldb + sudo apt-get update && sudo apt-get install -y gdb lldb llvm cargo test test_debug_dwarf -- --ignored --test-threads 1 if: matrix.os == 'ubuntu-latest' env: RUST_BACKTRACE: 1 - LLDB: /usr/bin/lldb # Test debug (DWARF) related functionality on new backend. - run: | - sudo apt-get update && sudo apt-get install -y gdb lldb cargo test test_debug_dwarf -- --ignored --test-threads 1 --test debug:: if: matrix.os == 'ubuntu-latest' env: RUST_BACKTRACE: 1 - LLDB: /usr/bin/lldb # Test uffd functionality on Linux - run: | @@ -312,7 +308,7 @@ jobs: RUST_BACKTRACE: 1 # Perform all tests (debug mode) for `wasmtime` with the old x86 backend. - test_x64: + test_x86: name: Test old x86 backend runs-on: ubuntu-latest steps: @@ -322,7 +318,6 @@ jobs: - uses: ./.github/actions/install-rust with: toolchain: stable - - uses: ./.github/actions/define-llvm-env # Install wasm32 targets in order to build various tests throughout the # repo. From 8d83b088b855ca380ea81442055955aea8937cd0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 May 2021 15:24:11 -0700 Subject: [PATCH 70/90] Try to fix CI Fixes a few issues that have been cropping up: * Update `rustup` on Windows to latest to skip over the 1.24.1 installed on GitHub Actions which can fail to install. * Remove the no-longer-needed `define-llvm-env` action * Install generic llvm/lldb packges instead of specific ones that may migrate in versions over time. --- .github/actions/install-rust/main.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/actions/install-rust/main.js b/.github/actions/install-rust/main.js index 2de34dbf16cf..b144d70aea43 100644 --- a/.github/actions/install-rust/main.js +++ b/.github/actions/install-rust/main.js @@ -6,11 +6,10 @@ function set_env(name, val) { fs.appendFileSync(process.env['GITHUB_ENV'], `${name}=${val}\n`) } -if (process.platform === 'darwin') { - child_process.execSync(`curl https://sh.rustup.rs | sh -s -- -y --default-toolchain=none --profile=minimal`); - const bindir = `${process.env.HOME}/.cargo/bin`; - fs.appendFileSync(process.env['GITHUB_PATH'], `${bindir}\n`); - process.env.PATH = `${process.env.PATH}:${bindir}`; +// Needed for now to get 1.24.2 which fixes a bug in 1.24.1 that causes issues +// on Windows. +if (process.platform === 'win32') { + child_process.execFileSync('rustup', ['self', 'update']); } child_process.execFileSync('rustup', ['set', 'profile', 'minimal']); From 20f75936e0287a90c5f2c13ebcf4070bdd95c0aa Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 08:14:27 -0700 Subject: [PATCH 71/90] Don't use `BorrowMut` for wiggle At this point it's too verbose on the consumer side and it requires too much guidance to type inference, let's just use generic closures instead. --- benches/instantiation.rs | 4 +-- crates/bench-api/src/lib.rs | 48 +++----------------------- crates/c-api/src/linker.rs | 2 +- crates/c-api/src/store.rs | 20 +---------- crates/wasi-crypto/src/lib.rs | 18 +++++----- crates/wasi/src/lib.rs | 13 ++++--- crates/wasmtime/src/lib.rs | 2 +- crates/wiggle/generate/src/wasmtime.rs | 12 ++++--- crates/wiggle/src/borrow.rs | 2 +- docs/examples-rust-wasi.md | 23 +++--------- examples/linking.rs | 2 +- examples/wasi/main.rs | 2 +- src/commands/run.rs | 47 ++----------------------- tests/all/host_funcs.rs | 2 +- tests/all/traps.rs | 2 +- 15 files changed, 43 insertions(+), 156 deletions(-) diff --git a/benches/instantiation.rs b/benches/instantiation.rs index dba68ece8eea..e967fef0c158 100644 --- a/benches/instantiation.rs +++ b/benches/instantiation.rs @@ -45,7 +45,7 @@ fn bench_sequential(c: &mut Criterion, modules: &[&str]) { let module = Module::from_file(&engine, &path) .expect(&format!("failed to load benchmark `{}`", path.display())); let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker).unwrap(); + wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap(); group.bench_function(BenchmarkId::new(benchmark_name(strategy), file_name), |b| { b.iter(|| instantiate(&linker, &module).expect("failed to instantiate module")); @@ -74,7 +74,7 @@ fn bench_parallel(c: &mut Criterion) { let module = Module::from_file(&engine, "benches/instantiation/wasi.wasm") .expect("failed to load WASI example module"); let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker).unwrap(); + wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap(); for threads in 1..=num_cpus::get_physical() { let pool = ThreadPoolBuilder::new() diff --git a/crates/bench-api/src/lib.rs b/crates/bench-api/src/lib.rs index 5291b0270b9e..c5d68ff1572e 100644 --- a/crates/bench-api/src/lib.rs +++ b/crates/bench-api/src/lib.rs @@ -79,7 +79,6 @@ //! ``` use anyhow::{anyhow, Context, Result}; -use std::borrow::{Borrow, BorrowMut}; use std::env; use std::os::raw::{c_int, c_void}; use std::path::Path; @@ -206,45 +205,6 @@ struct StoreState { wasi_crypto: wasmtime_wasi_crypto::WasiCryptoCtx, } -impl Borrow for StoreState { - fn borrow(&self) -> &WasiCtx { - &self.wasi - } -} -impl BorrowMut for StoreState { - fn borrow_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} - -#[cfg(feature = "wasi-nn")] -impl Borrow for StoreState { - fn borrow(&self) -> &wasmtime_wasi_nn::WasiNnCtx { - &self.wasi_nn - } -} - -#[cfg(feature = "wasi-nn")] -impl BorrowMut for StoreState { - fn borrow_mut(&mut self) -> &mut wasmtime_wasi_nn::WasiNnCtx { - &mut self.wasi_nn - } -} - -#[cfg(feature = "wasi-crypto")] -impl Borrow for StoreState { - fn borrow(&self) -> &wasmtime_wasi_crypto::WasiCryptoCtx { - &self.wasi_crypto - } -} - -#[cfg(feature = "wasi-crypto")] -impl BorrowMut for StoreState { - fn borrow_mut(&mut self) -> &mut wasmtime_wasi_crypto::WasiCryptoCtx { - &mut self.wasi_crypto - } -} - impl BenchState { fn new(working_dir: impl AsRef) -> Result { let mut config = Config::new(); @@ -252,7 +212,7 @@ impl BenchState { // NB: do not configure a code cache. let engine = Engine::new(&config)?; - let mut linker = Linker::new(&engine); + let mut linker = Linker::::new(&engine); // Create a WASI environment. @@ -269,17 +229,17 @@ impl BenchState { cx = cx.env("WASM_BENCH_USE_SMALL_WORKLOAD", &val)?; } let wasi = cx.build()?; - wasmtime_wasi::add_to_linker(&mut linker)?; + wasmtime_wasi::add_to_linker(&mut linker, |cx| &mut cx.wasi)?; #[cfg(feature = "wasi-nn")] let wasi_nn = { - wasmtime_wasi_nn::add_wasi_nn_to_linker(&mut linker)?; + wasmtime_wasi_nn::add_to_linker(&mut linker, |cx| &mut cx.wasi_nn)?; wasmtime_wasi_nn::WasiNnCtx::new()? }; #[cfg(feature = "wasi-crypto")] let wasi_crypto = { - wasmtime_wasi_crypto::add_to_linker(&mut linker)?; + wasmtime_wasi_crypto::add_to_linker(&mut linker, |cx| &mut cx.wasi_crypto)?; wasmtime_wasi_crypto::WasiCryptoCtx::new() }; diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 9dac41cb1d4d..280b15475af3 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -60,7 +60,7 @@ pub extern "C" fn wasmtime_linker_define_wasi( linker: &mut wasmtime_linker_t, ) -> Option> { handle_result( - wasmtime_wasi::add_to_linker(&mut linker.linker), + wasmtime_wasi::add_to_linker(&mut linker.linker, |cx| cx.wasi.as_mut().unwrap()), |_linker| (), ) } diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index 0a91cf22d1d3..5d588a0e0a43 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -66,25 +66,7 @@ pub type CStoreContextMut<'a> = StoreContextMut<'a, StoreData>; pub struct StoreData { foreign: crate::ForeignData, #[cfg(feature = "wasi")] - wasi: Option, -} - -#[cfg(feature = "wasi")] -impl std::borrow::Borrow for StoreData { - fn borrow(&self) -> &wasmtime_wasi::WasiCtx { - self.wasi - .as_ref() - .expect("wasi not configured via `wasmtime_context_set_wasi` yet") - } -} - -#[cfg(feature = "wasi")] -impl std::borrow::BorrowMut for StoreData { - fn borrow_mut(&mut self) -> &mut wasmtime_wasi::WasiCtx { - self.wasi - .as_mut() - .expect("wasi not configured via `wasmtime_context_set_wasi` yet") - } + pub(crate) wasi: Option, } #[no_mangle] diff --git a/crates/wasi-crypto/src/lib.rs b/crates/wasi-crypto/src/lib.rs index defce00142d6..9cd4b716bac0 100644 --- a/crates/wasi-crypto/src/lib.rs +++ b/crates/wasi-crypto/src/lib.rs @@ -1,17 +1,15 @@ -use std::borrow::BorrowMut; - mod wiggle_interfaces; pub use wiggle_interfaces::WasiCryptoCtx; -pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> -where - T: BorrowMut, -{ +pub fn add_to_linker( + linker: &mut wasmtime::Linker, + get_cx: impl Fn(&mut T) -> &mut WasiCryptoCtx + Send + Sync + Copy + 'static, +) -> anyhow::Result<()> { use wiggle_interfaces::wasi_modules as w; - w::wasi_ephemeral_crypto_common::add_to_linker(linker)?; - w::wasi_ephemeral_crypto_asymetric_common::add_to_linker(linker)?; - w::wasi_ephemeral_crypto_signatures::add_to_linker(linker)?; - w::wasi_ephemeral_crypto_symmetric::add_to_linker(linker)?; + w::wasi_ephemeral_crypto_common::add_to_linker(linker, get_cx)?; + w::wasi_ephemeral_crypto_asymmetric_common::add_to_linker(linker, get_cx)?; + w::wasi_ephemeral_crypto_signatures::add_to_linker(linker, get_cx)?; + w::wasi_ephemeral_crypto_symmetric::add_to_linker(linker, get_cx)?; Ok(()) } diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 6daf3dab8370..dd7be14cb0db 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -40,15 +40,14 @@ pub mod tokio { macro_rules! define_wasi { ($async_mode: tt $($bounds:tt)*) => { -use std::borrow::BorrowMut; use wasmtime::Linker; -pub fn add_to_linker(linker: &mut Linker) -> anyhow::Result<()> -where - T: BorrowMut $($bounds)*, -{ - snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker)?; - snapshots::preview_0::add_wasi_unstable_to_linker(linker)?; +pub fn add_to_linker( + linker: &mut Linker, + get_cx: impl Fn(&mut T) -> &mut crate::WasiCtx + Send + Sync + Copy + 'static, +) -> anyhow::Result<()> { + snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker, get_cx)?; + snapshots::preview_0::add_wasi_unstable_to_linker(linker, get_cx)?; Ok(()) } diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 801e9cd9b0fc..583cfba3132b 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -289,7 +289,7 @@ //! let engine = Engine::default(); //! let module = Module::from_file(&engine, "foo.wasm")?; //! let mut linker = Linker::new(&engine); -//! wasmtime_wasi::add_to_linker(&mut linker)?; +//! wasmtime_wasi::add_to_linker(&mut linker, |cx| cx)?; //! //! // Configure and create a `WasiCtx`, which WASI functions need access to //! // through the host state of the store (which in this case is the host state diff --git a/crates/wiggle/generate/src/wasmtime.rs b/crates/wiggle/generate/src/wasmtime.rs index cdce7a97f565..e15fa85d0a39 100644 --- a/crates/wiggle/generate/src/wasmtime.rs +++ b/crates/wiggle/generate/src/wasmtime.rs @@ -14,7 +14,7 @@ pub fn link_module( let module_ident = names.module(&module.name); let send_bound = if settings.async_.contains_async(module) { - quote! { + Send } + quote! { + Send, T: Send } } else { quote! {} }; @@ -50,10 +50,12 @@ pub fn link_module( quote! { /// Adds all instance items to the specified `Linker`. - pub fn #func_name(linker: &mut #rt::wasmtime_crate::Linker) -> anyhow::Result<()> + pub fn #func_name( + linker: &mut #rt::wasmtime_crate::Linker, + get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> where - T: std::borrow::BorrowMut #send_bound, - C: #ctx_bound + U: #ctx_bound #send_bound { #(#bodies)* Ok(()) @@ -133,7 +135,7 @@ fn generate_func( // working from the previous iteration for now. let (ctx, mem) = unsafe { let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]); - (caller.data_mut().borrow_mut(), #rt::wasmtime::WasmtimeGuestMemory::new(mem)) + (get_cx(caller.data_mut()), #rt::wasmtime::WasmtimeGuestMemory::new(mem)) }; match #abi_func(ctx, &mem #(, #arg_names)*) #await_ { Ok(r) => Ok(<#ret_ty>::from(r)), diff --git a/crates/wiggle/src/borrow.rs b/crates/wiggle/src/borrow.rs index cf42757a6541..d0a85288e9ad 100644 --- a/crates/wiggle/src/borrow.rs +++ b/crates/wiggle/src/borrow.rs @@ -1,6 +1,6 @@ +use crate::{BorrowHandle, GuestError, Region}; use std::collections::HashMap; use std::sync::Mutex; -use crate::{BorrowHandle, GuestError, Region}; pub struct BorrowChecker { /// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index 71c22d08a9d6..2358c0ea500b 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -26,11 +26,10 @@ WebAssembly module. ## WASI state with other custom host state -The [`add_to_linker`] function requires that the host state of a [`Store`] -implements the [`BorrowMut`] trait. In the above example this is true -because the host state itself is [`WasiCtx`], but you may also have custom host -state you'd like to store adjacent to the [`WasiCtx`]. An example of doing this -looks like: +The [`add_to_linker`] takes a second argument which is a closure to access `&mut +WasiCtx` from within the `T` stored in the `Store` itself. In the above +example this is trivial because the `T` in `Store` is `WasiCtx` itself, but +you can also store other state in `Store` like so: [`add_to_linker`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/sync/fn.add_to_linker.html [`Store`]: https://docs.rs/wasmtime/0.26.0/wasmtime/struct.Store.html @@ -51,22 +50,10 @@ struct MyState { wasi: WasiCtx, } -impl Borrow for MyState { - fn borrow(&self) -> &WasiCtx { - &self.wasi - } -} - -impl BorrowMut for MyState { - fn borrow_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} - fn main() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker)?; + wasmtime_wasi::add_to_linker(&mut linker, |state| &mut state.wasi)?; let wasi = WasiCtxBuilder::new() .inherit_stdio() diff --git a/examples/linking.rs b/examples/linking.rs index 279d5fa663ab..6500e2c9d14d 100644 --- a/examples/linking.rs +++ b/examples/linking.rs @@ -12,7 +12,7 @@ fn main() -> Result<()> { // First set up our linker which is going to be linking modules together. We // want our linker to have wasi available, so we set that up here as well. let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker)?; + wasmtime_wasi::add_to_linker(&mut linker, |s| s)?; // Load and compile our two modules let linking1 = Module::from_file(&engine, "examples/linking1.wat")?; diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index 5d5092b2bc31..ce2d57524d30 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -11,7 +11,7 @@ fn main() -> Result<()> { // Define the WASI functions globally on the `Config`. let engine = Engine::default(); let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker)?; + wasmtime_wasi::add_to_linker(&mut linker, |s| s)?; // Create a WASI context and put it in a Store; all instances in the store // share this context. `WasiCtxBuilder` provides a number of ways to diff --git a/src/commands/run.rs b/src/commands/run.rs index 9a65184fa64f..aa297ba0d5b3 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -2,7 +2,6 @@ use crate::{CommonOptions, WasiModules}; use anyhow::{anyhow, bail, Context as _, Result}; -use std::borrow::{Borrow, BorrowMut}; use std::thread; use std::time::Duration; use std::{ @@ -371,46 +370,6 @@ struct Host { wasi_crypto: Option, } -impl Borrow for Host { - fn borrow(&self) -> &wasmtime_wasi::WasiCtx { - self.wasi.as_ref().unwrap() - } -} - -impl BorrowMut for Host { - fn borrow_mut(&mut self) -> &mut wasmtime_wasi::WasiCtx { - self.wasi.as_mut().unwrap() - } -} - -#[cfg(feature = "wasi-nn")] -impl Borrow for Host { - fn borrow(&self) -> &WasiNnCtx { - self.wasi_nn.as_ref().unwrap() - } -} - -#[cfg(feature = "wasi-nn")] -impl BorrowMut for Host { - fn borrow_mut(&mut self) -> &mut WasiNnCtx { - self.wasi_nn.as_mut().unwrap() - } -} - -#[cfg(feature = "wasi-crypto")] -impl Borrow for Host { - fn borrow(&self) -> &WasiCryptoCtx { - self.wasi_crypto.as_ref().unwrap() - } -} - -#[cfg(feature = "wasi-crypto")] -impl BorrowMut for Host { - fn borrow_mut(&mut self) -> &mut WasiCryptoCtx { - self.wasi_crypto.as_mut().unwrap() - } -} - /// Populates the given `Linker` with WASI APIs. fn populate_with_wasi( store: &mut Store, @@ -421,7 +380,7 @@ fn populate_with_wasi( wasi_modules: &WasiModules, ) -> Result<()> { if wasi_modules.wasi_common { - wasmtime_wasi::add_to_linker(linker)?; + wasmtime_wasi::add_to_linker(linker, |host| host.wasi.as_mut().unwrap())?; let mut builder = WasiCtxBuilder::new(); builder = builder.inherit_stdio().args(argv)?.envs(vars)?; @@ -439,7 +398,7 @@ fn populate_with_wasi( } #[cfg(feature = "wasi-nn")] { - wasmtime_wasi_nn::add_to_linker::<_, WasiNnCtx>(linker)?; + wasmtime_wasi_nn::add_to_linker(linker, |host| host.wasi_nn.as_mut().unwrap())?; store.data_mut().wasi_nn = Some(WasiNnCtx::new()?); } } @@ -451,7 +410,7 @@ fn populate_with_wasi( } #[cfg(feature = "wasi-crypto")] { - wasmtime_wasi_crypto::add_to_linker(linker)?; + wasmtime_wasi_crypto::add_to_linker(linker, |host| host.wasi_crypto.as_mut().unwrap())?; store.data_mut().wasi_crypto = Some(WasiCryptoCtx::new()); } } diff --git a/tests/all/host_funcs.rs b/tests/all/host_funcs.rs index dd28e93734ca..ff0a4fd46f37 100644 --- a/tests/all/host_funcs.rs +++ b/tests/all/host_funcs.rs @@ -717,7 +717,7 @@ fn store_with_context() -> Result<()> { fn wasi_imports() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker)?; + wasmtime_wasi::add_to_linker(&mut linker, |s| s)?; let wasm = wat::parse_str( r#" diff --git a/tests/all/traps.rs b/tests/all/traps.rs index 0f4f1c7abdb0..2869b29e3a4a 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -510,7 +510,7 @@ fn parse_dwarf_info() -> Result<()> { let engine = Engine::new(&config)?; let module = Module::new(&engine, &wasm)?; let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker)?; + wasmtime_wasi::add_to_linker(&mut linker, |s| s)?; let mut store = Store::new( &engine, wasmtime_wasi::sync::WasiCtxBuilder::new() From e4f4b00964d828d1f83c63159f70edfbc482657e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 08:15:04 -0700 Subject: [PATCH 72/90] Re-add build of wasmtime --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a7cd7806f59f..5159595fbd69 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,6 +54,7 @@ jobs: curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.4.4/mdbook-v0.4.4-x86_64-unknown-linux-gnu.tar.gz | tar xzf - echo `pwd` >> $GITHUB_PATH - run: (cd docs && mdbook build) + - run: cargo build -p wasmtime --features wasmtime/wat - run: (cd docs && mdbook test -L ../target/debug/deps) - uses: actions/upload-artifact@v1 with: From 995f57ffc6a6d40cf6c902a3507d24d20dd005d2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 08:20:23 -0700 Subject: [PATCH 73/90] more build tweak --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5159595fbd69..c13531b49f63 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,7 +54,7 @@ jobs: curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.4.4/mdbook-v0.4.4-x86_64-unknown-linux-gnu.tar.gz | tar xzf - echo `pwd` >> $GITHUB_PATH - run: (cd docs && mdbook build) - - run: cargo build -p wasmtime --features wasmtime/wat + - run: cargo build -p wasmtime-wasi --features wasmtime/wat - run: (cd docs && mdbook test -L ../target/debug/deps) - uses: actions/upload-artifact@v1 with: From 7f4b340d69db55fbbb3bf91aca6669e14087a0d7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 08:34:45 -0700 Subject: [PATCH 74/90] Recognize WASMTIME_V128 as wasm_valkind_t --- crates/c-api/src/types/val.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/c-api/src/types/val.rs b/crates/c-api/src/types/val.rs index ab372df4d6a6..7508170e6c30 100644 --- a/crates/c-api/src/types/val.rs +++ b/crates/c-api/src/types/val.rs @@ -36,6 +36,7 @@ pub(crate) fn into_valtype(kind: wasm_valkind_t) -> ValType { WASM_F64 => ValType::F64, WASM_EXTERNREF => ValType::ExternRef, WASM_FUNCREF => ValType::FuncRef, + WASMTIME_V128 => ValType::V128, _ => panic!("unexpected kind: {}", kind), } } @@ -48,7 +49,7 @@ pub(crate) fn from_valtype(ty: &ValType) -> wasm_valkind_t { ValType::F64 => WASM_F64, ValType::ExternRef => WASM_EXTERNREF, ValType::FuncRef => WASM_FUNCREF, - _ => panic!("wasm_valkind_t has no known conversion for {:?}", ty), + ValType::V128 => WASMTIME_V128, } } From 40806de59c0aed757a5c4249acf0988908406333 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 08:36:26 -0700 Subject: [PATCH 75/90] Fix build of async wasi --- crates/wasi/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index dd7be14cb0db..3ec7dd4cce3b 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -28,7 +28,7 @@ pub use sync::*; #[cfg(feature = "tokio")] pub mod tokio { pub use wasi_tokio::*; - super::define_wasi!(async + Send); + super::define_wasi!(async T: Send); } // The only difference between these definitions for sync vs async is whether @@ -38,14 +38,16 @@ pub mod tokio { #[doc(hidden)] #[macro_export] macro_rules! define_wasi { - ($async_mode: tt $($bounds:tt)*) => { + ($async_mode:tt $($bounds:tt)*) => { use wasmtime::Linker; pub fn add_to_linker( linker: &mut Linker, get_cx: impl Fn(&mut T) -> &mut crate::WasiCtx + Send + Sync + Copy + 'static, -) -> anyhow::Result<()> { +) -> anyhow::Result<()> + where $($bounds)* +{ snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker, get_cx)?; snapshots::preview_0::add_wasi_unstable_to_linker(linker, get_cx)?; Ok(()) From 8a407d0af0849e9a9bd9777e6580b563328b9d55 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 08:52:48 -0700 Subject: [PATCH 76/90] Fix some wiggle tests --- crates/wiggle/generate/src/funcs.rs | 12 ++++++------ crates/wiggle/generate/src/module_trait.rs | 6 +++--- crates/wiggle/tests/wasmtime_async.rs | 4 ++-- crates/wiggle/tests/wasmtime_integration.rs | 4 ++-- crates/wiggle/tests/wasmtime_sync.rs | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/wiggle/generate/src/funcs.rs b/crates/wiggle/generate/src/funcs.rs index 74c2fee4d98e..31e4a7bc2607 100644 --- a/crates/wiggle/generate/src/funcs.rs +++ b/crates/wiggle/generate/src/funcs.rs @@ -73,10 +73,10 @@ fn _define_func( }, ); - let asyncness = if settings.get_async(&module, &func).is_async() { - quote!(async) - } else { + let asyncness = if settings.get_async(&module, &func).is_sync() { quote!() + } else { + quote!(async) }; let mod_name = &module.name.as_str(); let func_name = &func.name.as_str(); @@ -245,13 +245,13 @@ impl witx::Bindgen for Rust<'_> { let trait_name = self.names.trait_name(&self.module.name); let ident = self.names.func(&func.name); - if self.settings.get_async(&self.module, &func).is_async() { + if self.settings.get_async(&self.module, &func).is_sync() { self.src.extend(quote! { - let ret = #trait_name::#ident(ctx, #(#args),*).await; + let ret = #trait_name::#ident(ctx, #(#args),*); }) } else { self.src.extend(quote! { - let ret = #trait_name::#ident(ctx, #(#args),*); + let ret = #trait_name::#ident(ctx, #(#args),*).await; }) }; self.src.extend(quote! { diff --git a/crates/wiggle/generate/src/module_trait.rs b/crates/wiggle/generate/src/module_trait.rs index 402ac004226d..2e108f56f097 100644 --- a/crates/wiggle/generate/src/module_trait.rs +++ b/crates/wiggle/generate/src/module_trait.rs @@ -75,10 +75,10 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings _ => unimplemented!(), }; - let asyncness = if settings.get_async(&m, &f).is_async() { - quote!(async) - } else { + let asyncness = if settings.get_async(&m, &f).is_sync() { quote!() + } else { + quote!(async) }; if is_anonymous { diff --git a/crates/wiggle/tests/wasmtime_async.rs b/crates/wiggle/tests/wasmtime_async.rs index 41ccd12f15b0..65cb021b4f50 100644 --- a/crates/wiggle/tests/wasmtime_async.rs +++ b/crates/wiggle/tests/wasmtime_async.rs @@ -35,7 +35,7 @@ impl atoms::Atoms for Ctx { fn test_sync_host_func() { let mut store = async_store(); let mut linker = Linker::new(store.engine()); - atoms::add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker, |cx| cx).unwrap(); let shim_mod = shim_module(linker.engine()); let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); @@ -57,7 +57,7 @@ fn test_sync_host_func() { fn test_async_host_func() { let mut store = async_store(); let mut linker = Linker::new(store.engine()); - atoms::add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker, |cx| cx).unwrap(); let shim_mod = shim_module(linker.engine()); let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap(); diff --git a/crates/wiggle/tests/wasmtime_integration.rs b/crates/wiggle/tests/wasmtime_integration.rs index f222260647d5..59c1cae2ecae 100644 --- a/crates/wiggle/tests/wasmtime_integration.rs +++ b/crates/wiggle/tests/wasmtime_integration.rs @@ -44,7 +44,7 @@ impl atoms::Atoms for Ctx { fn test_sync_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - integration::add_atoms_to_linker(&mut linker).unwrap(); + integration::add_atoms_to_linker(&mut linker, |cx| cx).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); @@ -67,7 +67,7 @@ fn test_sync_host_func() { fn test_async_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - integration::add_atoms_to_linker(&mut linker).unwrap(); + integration::add_atoms_to_linker(&mut linker, |cx| cx).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); diff --git a/crates/wiggle/tests/wasmtime_sync.rs b/crates/wiggle/tests/wasmtime_sync.rs index 7c8b4c09141a..afd9f6081080 100644 --- a/crates/wiggle/tests/wasmtime_sync.rs +++ b/crates/wiggle/tests/wasmtime_sync.rs @@ -32,7 +32,7 @@ impl atoms::Atoms for Ctx { fn test_sync_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - atoms::add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker, |cx| cx).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); @@ -55,7 +55,7 @@ fn test_sync_host_func() { fn test_async_host_func() { let engine = Engine::default(); let mut linker = Linker::new(&engine); - atoms::add_to_linker(&mut linker).unwrap(); + atoms::add_to_linker(&mut linker, |cx| cx).unwrap(); let mut store = store(&engine); let shim_mod = shim_module(&engine); From 7d2e95e319f7dcd694e186d13e05b29fd64b9986 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 08:53:40 -0700 Subject: [PATCH 77/90] Move wiggle in publish script, now it depends on wasmtime --- scripts/publish.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/publish.rs b/scripts/publish.rs index e717f17cb80f..3baea0dc14f4 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -43,7 +43,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[ // wiggle "wiggle-generate", "wiggle-macro", - "wiggle", // wasi-common "wasi-common", "wasi-cap-std-sync", @@ -61,6 +60,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[ "wasmtime-jit", "wasmtime-cache", "wasmtime", + "wiggle", "wasmtime-wasi", "wasmtime-wasi-nn", "wasmtime-wasi-crypto", From 7054c2e851ddff10d9443335cfa7f57d6a7aaf13 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 09:00:26 -0700 Subject: [PATCH 78/90] Fix a book example --- docs/examples-rust-wasi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index 2358c0ea500b..e94d0ec9869e 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -53,7 +53,7 @@ struct MyState { fn main() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker, |state| &mut state.wasi)?; + wasmtime_wasi::add_to_linker(&mut linker, |state: &mut MyState| &mut state.wasi)?; let wasi = WasiCtxBuilder::new() .inherit_stdio() From 52cc3693f619d3d54e2ada5ee08a0383762a7e08 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 11:07:43 -0700 Subject: [PATCH 79/90] Fix some tests --- crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs | 2 +- crates/test-programs/tests/wasm_tests/runtime/tokio.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs b/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs index 2349471429ca..bea5406b473b 100644 --- a/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs +++ b/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs @@ -28,7 +28,7 @@ fn run( let engine = Engine::default(); let module = Module::new(&engine, &data).context("failed to create wasm module")?; let mut linker = Linker::new(&engine); - add_to_linker(&mut linker)?; + add_to_linker(&mut linker, |cx| cx)?; // Create our wasi context. // Additionally register any preopened directories if we have them. diff --git a/crates/test-programs/tests/wasm_tests/runtime/tokio.rs b/crates/test-programs/tests/wasm_tests/runtime/tokio.rs index a75f95936e1e..d8870af5116b 100644 --- a/crates/test-programs/tests/wasm_tests/runtime/tokio.rs +++ b/crates/test-programs/tests/wasm_tests/runtime/tokio.rs @@ -34,7 +34,7 @@ fn run( let engine = Engine::new(&config)?; let module = Module::new(&engine, &data).context("failed to create wasm module")?; let mut linker = Linker::new(&engine); - add_to_linker(&mut linker)?; + add_to_linker(&mut linker, |cx| cx)?; // Create our wasi context. let mut builder = WasiCtxBuilder::new(); From b832de41b311418be1d108fd94463efd87aa2f22 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 11:45:03 -0700 Subject: [PATCH 80/90] Fix c-api build --- crates/c-api/src/wasi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index bcbc2840d29f..411d29c1c48c 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -90,7 +90,7 @@ impl wasi_config_t { for (dir, path) in self.preopens { builder = builder.preopened_dir(dir, path)?; } - Ok(builder.build()?) + Ok(builder.build()) } } From 57c49174bbe66662eaa35b41d3043e15e1cd42da Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 11:45:28 -0700 Subject: [PATCH 81/90] Fix benchmark --- benches/instantiation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/instantiation.rs b/benches/instantiation.rs index e967fef0c158..43553d664b7d 100644 --- a/benches/instantiation.rs +++ b/benches/instantiation.rs @@ -6,7 +6,7 @@ use wasmtime::*; use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx}; fn instantiate(linker: &Linker, module: &Module) -> Result<()> { - let wasi = WasiCtxBuilder::new().build()?; + let wasi = WasiCtxBuilder::new().build(); let mut store = Store::new(module.engine(), wasi); let _instance = linker.instantiate(&mut store, module)?; From 6ee0c54cf7d9f6d9073ac9c7f08cb8fd7974ebf1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 12:41:15 -0700 Subject: [PATCH 82/90] Fix more tests --- Cargo.lock | 4092 ++++++++++++++++++++++++ crates/wiggle/Cargo.toml | 3 +- crates/wiggle/generate/src/wasmtime.rs | 2 +- crates/wiggle/src/lib.rs | 2 + docs/examples-rust-wasi.md | 2 +- 5 files changed, 4098 insertions(+), 3 deletions(-) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000000..bcd8a519e2e3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4092 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03345e98af8f3d786b6d9f656ccfa6ac316d954e92bc4841f0bba20789d5fb5a" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "ahash" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "arbitrary" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "698b65a961a9d730fb45b6b0327e20207810c9f61ee421b082b27ba003f49e2b" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-trait" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bincode" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bindgen" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd4865004a46a0aafb2a0a5eb19d3c9fc46ee5f063a6cfc605c69ac9ecf5263d" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", +] + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bitvec" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98fcd36dda4e17b7d7abc64cb549bf0201f4ab71e00700c798ca7e62ed3761fa" +dependencies = [ + "funty", + "radium", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cap-fs-ext" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff3a1e32332db9ad29d6da34693ce9a7ac26a9edf96abb5c1788d193410031ab" +dependencies = [ + "cap-primitives", + "cap-std", + "rustc_version", + "unsafe-io", + "winapi", +] + +[[package]] +name = "cap-primitives" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d253b74de50b097594462618e7dd17b93b3e3bef19f32d2e512996f9095661f" +dependencies = [ + "errno", + "fs-set-times", + "ipnet", + "libc", + "maybe-owned", + "once_cell", + "posish", + "rustc_version", + "unsafe-io", + "winapi", + "winapi-util", + "winx", +] + +[[package]] +name = "cap-rand" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458e98ed00e4276d0ac60da888d80957a177dfa7efa8dbb3be59f1e2b0e02ae5" +dependencies = [ + "rand 0.8.3", +] + +[[package]] +name = "cap-std" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7019d48ea53c5f378e0fdab0fe5f627fc00e76d65e75dffd6fb1cbc0c9b382ee" +dependencies = [ + "cap-primitives", + "posish", + "rustc_version", + "unsafe-io", +] + +[[package]] +name = "cap-tempfile" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2f6f45ddb06ff26f4cf2ba9838d5826d52e1a5f6b321d71f114bb38cf34a57" +dependencies = [ + "cap-std", + "rand 0.8.3", + "uuid", +] + +[[package]] +name = "cap-time-ext" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90585adeada7f804e6dcf71b8ff74217ad8742188fc870b9da5deab4722baa04" +dependencies = [ + "cap-primitives", + "once_cell", + "posish", + "winx", +] + +[[package]] +name = "capstone" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60e7f097987a09a9b678c6214b5a5eb326f9fc1e3eac88cce5d086c2b3b8dc9" +dependencies = [ + "capstone-sys", + "libc", +] + +[[package]] +name = "capstone-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebe07897b48983847943662bfc3198aabfa51636c81313c1955d04d857ed739" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "cast" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57cdfa5d50aad6cb4d44dcab6101a7f79925bd59d82ca42f38a9856a28865374" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed8738f14471a99f0e316c327e68fc82a3611cc2895fcb604b89eedaf8f39d95" +dependencies = [ + "cipher", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1fc18e6d90c40164bf6c317476f2a98f04661e310e79830366b7e914c58a8e" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + +[[package]] +name = "clang-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54d78e30b388d4815220c8dd03fea5656b6c6d32adb59e89061552a102f8da1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term 0.11.0", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cmake" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +dependencies = [ + "cc", +] + +[[package]] +name = "console" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "const-oid" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6b64db6932c7e49332728e3a6bd82c6b7e16016607d20923b537c3bc4c0d5f" + +[[package]] +name = "cpp_demangle" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44919ecaf6f99e8e737bc239408931c9a01e9a6c74814fee8242dd2506b65390" +dependencies = [ + "cfg-if 1.0.0", + "glob", +] + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + +[[package]] +name = "cranelift" +version = "0.74.0" +dependencies = [ + "cranelift-codegen", + "cranelift-frontend", +] + +[[package]] +name = "cranelift-bforest" +version = "0.74.0" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.74.0" +dependencies = [ + "bincode", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "criterion", + "gimli", + "hashbrown", + "log", + "peepmatic", + "peepmatic-runtime", + "peepmatic-traits", + "regalloc", + "serde", + "smallvec", + "souper-ir", + "target-lexicon", + "wast 35.0.2", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.74.0" +dependencies = [ + "cranelift-codegen-shared", + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.74.0" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-entity" +version = "0.74.0" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-filetests" +version = "0.73.0" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-frontend", + "cranelift-interpreter", + "cranelift-native", + "cranelift-preopt", + "cranelift-reader", + "file-per-thread-logger", + "filecheck", + "gimli", + "log", + "memmap2", + "num_cpus", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "cranelift-frontend" +version = "0.74.0" +dependencies = [ + "cranelift-codegen", + "hashbrown", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-interpreter" +version = "0.74.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-reader", + "log", + "smallvec", + "thiserror", +] + +[[package]] +name = "cranelift-jit" +version = "0.74.0" +dependencies = [ + "anyhow", + "cranelift", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-module", + "cranelift-native", + "libc", + "log", + "memmap2", + "region", + "target-lexicon", + "winapi", +] + +[[package]] +name = "cranelift-module" +version = "0.74.0" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "hashbrown", + "log", +] + +[[package]] +name = "cranelift-native" +version = "0.74.0" +dependencies = [ + "cranelift-codegen", + "target-lexicon", +] + +[[package]] +name = "cranelift-object" +version = "0.74.0" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-module", + "log", + "object", + "target-lexicon", +] + +[[package]] +name = "cranelift-preopt" +version = "0.74.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", +] + +[[package]] +name = "cranelift-reader" +version = "0.74.0" +dependencies = [ + "cranelift-codegen", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-serde" +version = "0.74.0" +dependencies = [ + "clap", + "cranelift-codegen", + "cranelift-reader", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "cranelift-tools" +version = "0.73.0" +dependencies = [ + "anyhow", + "capstone", + "cfg-if 1.0.0", + "cranelift", + "cranelift-codegen", + "cranelift-entity", + "cranelift-filetests", + "cranelift-frontend", + "cranelift-interpreter", + "cranelift-jit", + "cranelift-module", + "cranelift-native", + "cranelift-object", + "cranelift-preopt", + "cranelift-reader", + "cranelift-serde", + "cranelift-wasm", + "file-per-thread-logger", + "filecheck", + "indicatif", + "log", + "peepmatic-souper", + "pretty_env_logger", + "rayon", + "structopt", + "target-lexicon", + "termcolor", + "thiserror", + "walkdir", + "wat", +] + +[[package]] +name = "cranelift-wasm" +version = "0.74.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "hashbrown", + "itertools 0.10.0", + "log", + "serde", + "smallvec", + "target-lexicon", + "thiserror", + "wasmparser", + "wat", +] + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "criterion" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools 0.10.0", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" +dependencies = [ + "cast", + "itertools 0.9.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +dependencies = [ + "autocfg 1.0.1", + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-mac" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f627126b946c25a4638eec0ea634fc52506dea98db118aae985118ce7c3d723f" +dependencies = [ + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f59c66c30bb7445c8320a5f9233e437e3572368099f25532a59054328899b4" +dependencies = [ + "const-oid", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_arbitrary" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df89dd0d075dea5cc5fdd6d5df6b8a61172a710b3efac1d6bdb9dd8b78f82c1a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_utils" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532b4c15dccee12c7044f1fcad956e98410860b22231e44a3b827464797ca7bf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dynasm" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7d1242462849390bb2ad38aeed769499f1afc7383affa2ab0c1baa894c0200" +dependencies = [ + "bitflags", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dynasmrt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dd4d1d5ca12258cef339a57a7643e8b233a42dea9bb849630ddd9dd7726aa9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2", +] + +[[package]] +name = "ecdsa" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fbdb4ff710acb4db8ca29f93b897529ea6d6a45626d5183b47e012aa6ae7e4" +dependencies = [ + "elliptic-curve", + "hmac", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c66a534cbb46ab4ea03477eae19d5c22c01da8258030280b7bd9d8433fb6ef" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2", + "zeroize", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "elliptic-curve" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2db227e61a43a34915680bdda462ec0e212095518020a88a1f91acd16092c39" +dependencies = [ + "bitvec", + "digest", + "ff", + "funty", + "generic-array", + "group", + "pkcs8", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" +dependencies = [ + "atty", + "humantime 2.1.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "example-fib-debug-wasm" +version = "0.0.0" + +[[package]] +name = "example-tokio-wasm" +version = "0.0.0" + +[[package]] +name = "example-wasi-wasm" +version = "0.0.0" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "ff" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01646e077d4ebda82b73f1bca002ea1e91561a77df2431a9e79729bcc31950ef" +dependencies = [ + "bitvec", + "rand_core 0.5.1", + "subtle", +] + +[[package]] +name = "file-per-thread-logger" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126" +dependencies = [ + "env_logger 0.7.1", + "log", +] + +[[package]] +name = "filecheck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe00b427b7c4835f8b82170eb7b9a63634376b63d73b9a9093367e82570bbaa" +dependencies = [ + "regex", + "thiserror", +] + +[[package]] +name = "filetime" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fs-set-times" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28f1ca01f517bba5770c067dc6c466d290b962e08214c8f2598db98d66087e55" +dependencies = [ + "posish", + "unsafe-io", + "winapi", +] + +[[package]] +name = "fst" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d79238883cf0307100b90aba4a755d8051a3182305dfe7f649a1e9dc0517006f" + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "group" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc11f9f5fbf1943b48ae7c2bf6846e7d827a512d1be4f23af708f5ca5d01dde1" +dependencies = [ + "ff", + "rand_core 0.5.1", + "subtle", +] + +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "hkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error 1.2.3", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg 1.0.1", + "hashbrown", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8572bccfb0665e70b7faf44ee28841b8e0823450cd4ad562a76b5a3c4bf48487" +dependencies = [ + "console", + "lazy_static", + "number_prefix", + "regex", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + +[[package]] +name = "iter-enum" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f947f0d9df7e69c4df60a950c0a83741455bb9ebd8fd9b5a87994dda4dbb005" +dependencies = [ + "derive_utils", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "ittapi-rs" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa16daf7106319e5c4456733e33aeb64d8c986af0127bc25eb6d9e84e2f1f8b0" +dependencies = [ + "cmake", +] + +[[package]] +name = "jobserver" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf02ecc966e1b7e8db1c81ac8f321ba24d1cfab5b634961fab10111f015858e1" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa", + "elliptic-curve", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" + +[[package]] +name = "libc" +version = "0.2.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86c975d637bc2a2f99440932b731491fc34c7f785d239e38af3addd3c2fd0e46" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + +[[package]] +name = "lightbeam" +version = "0.27.0" +dependencies = [ + "anyhow", + "arrayvec", + "capstone", + "cranelift-codegen", + "derive_more", + "dynasm", + "dynasmrt", + "iter-enum", + "itertools 0.10.0", + "lazy_static", + "memoffset", + "more-asserts", + "quickcheck", + "smallvec", + "thiserror", + "typemap", + "wasmparser", + "wat", +] + +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "memmap2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg 1.0.1", +] + +[[package]] +name = "memory_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg 1.0.1", +] + +[[package]] +name = "mio" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "more-asserts" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" + +[[package]] +name = "nix" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d51546d704f52ef14b3c962b5776e53d5b862e5790e40a350d366c209bd7f7a" +dependencies = [ + "autocfg 0.1.7", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.7.3", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg 1.0.1", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg 1.0.1", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" + +[[package]] +name = "object" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" +dependencies = [ + "crc32fast", + "indexmap", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openvino" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb74b3d8c653f7a9928bda494d329e6363ea0b428d3a3e5805b45ebb74ace76" +dependencies = [ + "openvino-sys", + "thiserror", +] + +[[package]] +name = "openvino-finder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426587a131841eb1e1111b0fea96cbd4fd0fd5d7b6526fb9c41400587d1c525c" + +[[package]] +name = "openvino-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d5e5d5e913f4e9aa42b2a7ae9c8719aedb4bc0eb443bf92f07d9ee9a05e7b1" +dependencies = [ + "cmake", + "lazy_static", + "libloading", + "openvino-finder", +] + +[[package]] +name = "os_pipe" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "p256" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca0196a204bb3f33305ba4a48b38f6e6e621cba8603a4e0650e6532e0949de4" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "parity-wasm" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "peepmatic" +version = "0.74.0" +dependencies = [ + "anyhow", + "peepmatic-automata", + "peepmatic-macro", + "peepmatic-runtime", + "peepmatic-test-operator", + "peepmatic-traits", + "serde", + "wast 35.0.2", + "z3", +] + +[[package]] +name = "peepmatic-automata" +version = "0.74.0" +dependencies = [ + "serde", +] + +[[package]] +name = "peepmatic-fuzzing" +version = "0.66.0" +dependencies = [ + "arbitrary", + "bincode", + "env_logger 0.8.3", + "fst", + "log", + "peepmatic", + "peepmatic-automata", + "peepmatic-runtime", + "peepmatic-test", + "peepmatic-test-operator", + "peepmatic-traits", + "rand 0.8.3", + "serde", + "wast 35.0.2", +] + +[[package]] +name = "peepmatic-macro" +version = "0.74.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "peepmatic-runtime" +version = "0.74.0" +dependencies = [ + "bincode", + "bumpalo", + "log", + "peepmatic-automata", + "peepmatic-test-operator", + "peepmatic-traits", + "serde", + "serde_test", + "thiserror", + "wast 35.0.2", +] + +[[package]] +name = "peepmatic-souper" +version = "0.74.0" +dependencies = [ + "anyhow", + "log", + "peepmatic", + "peepmatic-test-operator", + "souper-ir", + "wast 35.0.2", +] + +[[package]] +name = "peepmatic-test" +version = "0.2.0" +dependencies = [ + "env_logger 0.8.3", + "log", + "peepmatic", + "peepmatic-runtime", + "peepmatic-test-operator", + "peepmatic-traits", +] + +[[package]] +name = "peepmatic-test-operator" +version = "0.74.0" +dependencies = [ + "peepmatic-traits", + "serde", + "wast 35.0.2", +] + +[[package]] +name = "peepmatic-traits" +version = "0.74.0" + +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64", + "once_cell", + "regex", +] + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" + +[[package]] +name = "pkcs8" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4839a901843f3942576e65857f0ebf2e190ef7024d3c62a94099ba3f819ad1d" +dependencies = [ + "der", + "subtle-encoding", + "zeroize", +] + +[[package]] +name = "plotters" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" + +[[package]] +name = "plotters-svg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "poly1305" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b7456bc1ad2d4cf82b3a016be4c2ac48daf11bf990c1603ebd447fe6f30fca8" +dependencies = [ + "cpuid-bool 0.2.0", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool 0.2.0", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "posish" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1601f90b2342aaf3aadb891b71f584155d176b0e891bea92eeb11995e0ab25" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "errno", + "itoa", + "libc", + "unsafe-io", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "pqcrypto" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d3874384bf37d988b83f806d632e2f7fca69a8cd0338efaa64e8e7664573052" +dependencies = [ + "pqcrypto-kyber", + "pqcrypto-traits", +] + +[[package]] +name = "pqcrypto-kyber" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33550a5b6e0844d1b2363f67e15e4ca64586bb4fb2363a83af762e6c2d092bff" +dependencies = [ + "cc", + "glob", + "libc", + "pqcrypto-traits", +] + +[[package]] +name = "pqcrypto-traits" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e1563eff60a9ae869cacee0a33fa5c4ba27861fec6e3e23de95eb0ae805e4b" + +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger 0.7.1", + "log", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "proptest" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "quick-error 2.0.0", + "rand 0.8.3", + "rand_chacha 0.3.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", +] + +[[package]] +name = "psm" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abf49e5417290756acfd26501536358560c4a5cc4a0934d390939acb3e7083a" +dependencies = [ + "cc", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda" + +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger 0.8.3", + "log", + "rand 0.8.3", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.2", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.2", +] + +[[package]] +name = "rawbytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26d81f4c222fd11ad63bf56cbda89d1810aecf1a720a423ff7eb2020475d8bb" + +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg 1.0.1", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom 0.2.2", + "redox_syscall", +] + +[[package]] +name = "regalloc" +version = "0.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" +dependencies = [ + "log", + "rustc-hash", + "serde", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" + +[[package]] +name = "region" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rsa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd" +dependencies = [ + "byteorder", + "digest", + "lazy_static", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand 0.7.3", + "sha2", + "simple_asn1 0.4.1", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "rsa-export" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "358de25c89a5a71597ebc85f7ad222e2c67ed553e0ce31170104c3a77296a01c" +dependencies = [ + "num-bigint-dig", + "pem", + "rsa", + "simple_asn1 0.5.1", +] + +[[package]] +name = "run-examples" +version = "0.19.0" +dependencies = [ + "anyhow", + "cc", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scroll" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_test" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38145a8510bdf71d9a8cceeb57664049538446e77f24648328bdbcf22dc7e169" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool 0.1.2", + "digest", + "opaque-debug", +] + +[[package]] +name = "sharded-slab" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shellexpand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" +dependencies = [ + "dirs-next", +] + +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + +[[package]] +name = "shuffling-allocator" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee9977fa98489d9006f4ab26fc5cbe2a139985baed09d2ec08dee6e506fc496" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand 0.8.3", + "winapi", +] + +[[package]] +name = "signature" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210" +dependencies = [ + "digest", + "rand_core 0.5.1", +] + +[[package]] +name = "simple_asn1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +dependencies = [ + "chrono", + "num-bigint 0.2.6", + "num-traits", +] + +[[package]] +name = "simple_asn1" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8d597fce66eb0f19dd129b9956e4054cba21aeaf97d4116595027b670fac50" +dependencies = [ + "chrono", + "num-bigint 0.3.1", + "num-traits", + "thiserror", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "souper-ir" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50c18ce33988e1973003afbaa66e6a465ad7a614dc33f246879ccc209c2c044" +dependencies = [ + "id-arena", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" + +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "syn" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "system-interface" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff09d1260270c02199b44e68140aab5225c27b365a38684e0d7b6155f0c37ffb" +dependencies = [ + "atty", + "bitflags", + "cap-fs-ext", + "cap-std", + "posish", + "rustc_version", + "unsafe-io", + "winapi", + "winx", +] + +[[package]] +name = "target-lexicon" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ae3b39281e4b14b8123bdbaddd472b7dfe215e444181f2f9d2443c2444f834" + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand 0.8.3", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "test-programs" +version = "0.19.0" +dependencies = [ + "anyhow", + "cap-std", + "cfg-if 1.0.0", + "os_pipe", + "pretty_env_logger", + "target-lexicon", + "tempfile", + "tokio", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wasmtime-wasi", + "wat", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokio" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" +dependencies = [ + "autocfg 1.0.1", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab8966ac3ca27126141f7999361cc97dd6fb4b71da04c02044fa9045d98bb96" +dependencies = [ + "ansi_term 0.12.1", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" + +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" +dependencies = [ + "unsafe-any", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "universal-hash" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" +dependencies = [ + "traitobject", +] + +[[package]] +name = "unsafe-io" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe39acfe60d3754452ea6881613c3240100b23ffd94a627c138863f8cd314b1b" +dependencies = [ + "rustc_version", + "winapi", +] + +[[package]] +name = "userfaultfd" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d8164d4a8198fa546e7553b529f53e82907214a25fafda4a6f90d978b30a5c" +dependencies = [ + "bitflags", + "libc", + "nix", + "thiserror", + "userfaultfd-sys", +] + +[[package]] +name = "userfaultfd-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ada4f4ae167325015f52cc65f9fb6c251b868d8fb3b6dd0ce2d60e497c4870a" +dependencies = [ + "bindgen", + "cc", + "cfg-if 0.1.10", +] + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.2", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasi-cap-std-sync" +version = "0.27.0" +dependencies = [ + "anyhow", + "async-trait", + "bitflags", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "lazy_static", + "libc", + "system-interface", + "tempfile", + "tracing", + "unsafe-io", + "wasi-common", + "winapi", +] + +[[package]] +name = "wasi-common" +version = "0.27.0" +dependencies = [ + "anyhow", + "bitflags", + "cap-rand", + "cap-std", + "libc", + "thiserror", + "tracing", + "wiggle", + "winapi", +] + +[[package]] +name = "wasi-crypto" +version = "0.1.4" +dependencies = [ + "aes-gcm", + "anyhow", + "bincode", + "byteorder", + "chacha20poly1305", + "curve25519-dalek", + "derivative", + "ed25519-dalek", + "hkdf", + "hmac", + "k256", + "p256", + "parking_lot", + "pqcrypto", + "rand_core 0.5.1", + "rsa", + "rsa-export", + "serde", + "sha2", + "subtle", + "thiserror", + "xoodyak", + "zeroize", +] + +[[package]] +name = "wasi-tokio" +version = "0.27.0" +dependencies = [ + "anyhow", + "bitflags", + "cap-fs-ext", + "cap-std", + "cap-tempfile", + "cap-time-ext", + "fs-set-times", + "lazy_static", + "libc", + "posish", + "system-interface", + "tempfile", + "tokio", + "tracing", + "unsafe-io", + "wasi-cap-std-sync", + "wasi-common", + "wiggle", + "winapi", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" + +[[package]] +name = "wasm-encoder" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b4949d4f2b25a4b208317dcf86aacef9e7a5884e48dfc45d4aeb91808d6f86" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-smith" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a982408719f704307ac7f45247350f06ce739d759362ef8293ed7b4d922adee8" +dependencies = [ + "arbitrary", + "indexmap", + "leb128", + "wasm-encoder", +] + +[[package]] +name = "wasmi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6825d9b2147105789adb4c2d84b9b568719713f3ac39618b637b4dafc86c4" +dependencies = [ + "downcast-rs", + "libc", + "memory_units", + "num-rational", + "num-traits", + "parity-wasm", + "wasmi-validation", +] + +[[package]] +name = "wasmi-validation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmparser" +version = "0.78.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9255225d0124a59dd44def5cf98b62d70a27aed921f2c51e28035f08a73b0c" + +[[package]] +name = "wasmprinter" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ccec894c70710c2e4669320a532cb2b9cfb97adb0429745642f8ce76916ed85" +dependencies = [ + "anyhow", + "wasmparser", +] + +[[package]] +name = "wasmtime" +version = "0.27.0" +dependencies = [ + "anyhow", + "backtrace", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "indexmap", + "lazy_static", + "libc", + "log", + "paste", + "psm", + "region", + "rustc-demangle", + "serde", + "smallvec", + "target-lexicon", + "tempfile", + "wasi-cap-std-sync", + "wasmparser", + "wasmtime-cache", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-profiling", + "wasmtime-runtime", + "wasmtime-wasi", + "wat", + "winapi", +] + +[[package]] +name = "wasmtime-bench-api" +version = "0.19.0" +dependencies = [ + "anyhow", + "cap-std", + "shuffling-allocator", + "wasi-cap-std-sync", + "wasmtime", + "wasmtime-wasi", + "wasmtime-wasi-crypto", + "wasmtime-wasi-nn", + "wat", +] + +[[package]] +name = "wasmtime-c-api" +version = "0.19.0" +dependencies = [ + "anyhow", + "cap-std", + "env_logger 0.8.3", + "once_cell", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wasmtime-c-api-macros", + "wasmtime-wasi", + "wat", +] + +[[package]] +name = "wasmtime-c-api-macros" +version = "0.19.0" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "wasmtime-cache" +version = "0.27.0" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "errno", + "file-per-thread-logger", + "filetime", + "lazy_static", + "libc", + "log", + "more-asserts", + "pretty_env_logger", + "serde", + "sha2", + "tempfile", + "toml", + "winapi", + "zstd", +] + +[[package]] +name = "wasmtime-cli" +version = "0.27.0" +dependencies = [ + "anyhow", + "criterion", + "env_logger 0.8.3", + "file-per-thread-logger", + "filecheck", + "humantime 2.1.0", + "lazy_static", + "libc", + "log", + "more-asserts", + "num_cpus", + "object", + "pretty_env_logger", + "rayon", + "structopt", + "target-lexicon", + "tempfile", + "test-programs", + "tokio", + "tracing-subscriber", + "wasmparser", + "wasmtime", + "wasmtime-cache", + "wasmtime-debug", + "wasmtime-environ", + "wasmtime-fuzzing", + "wasmtime-jit", + "wasmtime-obj", + "wasmtime-runtime", + "wasmtime-wasi", + "wasmtime-wasi-crypto", + "wasmtime-wasi-nn", + "wasmtime-wast", + "wast 35.0.2", + "wat", +] + +[[package]] +name = "wasmtime-cranelift" +version = "0.27.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-wasm", + "target-lexicon", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-debug" +version = "0.27.0" +dependencies = [ + "anyhow", + "gimli", + "more-asserts", + "object", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "0.27.0" +dependencies = [ + "cfg-if 1.0.0", + "cranelift-codegen", + "cranelift-entity", + "cranelift-wasm", + "gimli", + "indexmap", + "log", + "more-asserts", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "wasmtime-fiber" +version = "0.27.0" +dependencies = [ + "backtrace", + "cc", + "libc", + "winapi", +] + +[[package]] +name = "wasmtime-fuzz" +version = "0.0.0" +dependencies = [ + "cranelift-codegen", + "cranelift-reader", + "cranelift-wasm", + "libfuzzer-sys", + "peepmatic-fuzzing", + "target-lexicon", + "wasm-smith", + "wasmtime", + "wasmtime-fuzzing", +] + +[[package]] +name = "wasmtime-fuzzing" +version = "0.19.0" +dependencies = [ + "anyhow", + "arbitrary", + "env_logger 0.8.3", + "log", + "rayon", + "wasm-encoder", + "wasm-smith", + "wasmi", + "wasmparser", + "wasmprinter", + "wasmtime", + "wasmtime-wast", + "wat", +] + +[[package]] +name = "wasmtime-jit" +version = "0.27.0" +dependencies = [ + "addr2line", + "anyhow", + "cfg-if 1.0.0", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "more-asserts", + "object", + "rayon", + "region", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-cranelift", + "wasmtime-debug", + "wasmtime-environ", + "wasmtime-lightbeam", + "wasmtime-obj", + "wasmtime-profiling", + "wasmtime-runtime", + "winapi", +] + +[[package]] +name = "wasmtime-lightbeam" +version = "0.27.0" +dependencies = [ + "cranelift-codegen", + "lightbeam", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-obj" +version = "0.27.0" +dependencies = [ + "anyhow", + "more-asserts", + "object", + "target-lexicon", + "wasmtime-debug", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-profiling" +version = "0.27.0" +dependencies = [ + "anyhow", + "cfg-if 1.0.0", + "gimli", + "ittapi-rs", + "lazy_static", + "libc", + "object", + "scroll", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-runtime", +] + +[[package]] +name = "wasmtime-runtime" +version = "0.27.0" +dependencies = [ + "anyhow", + "backtrace", + "cc", + "cfg-if 1.0.0", + "indexmap", + "lazy_static", + "libc", + "log", + "mach", + "memoffset", + "more-asserts", + "rand 0.8.3", + "region", + "thiserror", + "userfaultfd", + "wasmtime-environ", + "wasmtime-fiber", + "winapi", +] + +[[package]] +name = "wasmtime-rust" +version = "0.27.0" +dependencies = [ + "anyhow", + "wasmtime", + "wasmtime-rust-macro", + "wasmtime-wasi", +] + +[[package]] +name = "wasmtime-rust-macro" +version = "0.27.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmtime-wasi" +version = "0.27.0" +dependencies = [ + "anyhow", + "wasi-cap-std-sync", + "wasi-common", + "wasi-tokio", + "wasmtime", + "wiggle", +] + +[[package]] +name = "wasmtime-wasi-crypto" +version = "0.27.0" +dependencies = [ + "anyhow", + "wasi-crypto", + "wasmtime", + "wiggle", +] + +[[package]] +name = "wasmtime-wasi-nn" +version = "0.27.0" +dependencies = [ + "anyhow", + "log", + "openvino", + "thiserror", + "walkdir", + "wasmtime", + "wasmtime-runtime", + "wasmtime-wasi", + "wiggle", +] + +[[package]] +name = "wasmtime-wast" +version = "0.27.0" +dependencies = [ + "anyhow", + "wasmtime", + "wast 35.0.2", +] + +[[package]] +name = "wast" +version = "33.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d04fe175c7f78214971293e7d8875673804e736092206a3a4544dbc12811c1b" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wat" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec280a739b69173e0ffd12c1658507996836ba4e992ed9bc1e5385a0bd72a02" +dependencies = [ + "wast 35.0.2", +] + +[[package]] +name = "web-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wiggle" +version = "0.27.0" +dependencies = [ + "anyhow", + "async-trait", + "bitflags", + "proptest", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", + "wiggle-test", + "witx", +] + +[[package]] +name = "wiggle-generate" +version = "0.27.0" +dependencies = [ + "anyhow", + "heck", + "proc-macro2", + "quote", + "shellexpand", + "syn", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "0.27.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wiggle", + "wiggle-generate", + "witx", +] + +[[package]] +name = "wiggle-test" +version = "0.21.0" +dependencies = [ + "env_logger 0.8.3", + "proptest", + "thiserror", + "tracing", + "tracing-subscriber", + "wiggle", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winx" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdb79e12a5ac98f09e863b99c38c72f942a41f643ae0bb05d4d6d2633481341" +dependencies = [ + "bitflags", + "winapi", +] + +[[package]] +name = "witx" +version = "0.9.0" +dependencies = [ + "anyhow", + "diff", + "log", + "pretty_env_logger", + "rayon", + "structopt", + "thiserror", + "wast 33.0.0", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "xoodyak" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f9c85605c3a376cec858899f7d284f453359743adaeddf09c7d6ef18474a481" +dependencies = [ + "rawbytes", + "zeroize", +] + +[[package]] +name = "z3" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa17be9852c6c2a8de2ea0875350dc5f8626f3eaa6b683148753b827f1b39ce5" +dependencies = [ + "lazy_static", + "log", + "z3-sys", +] + +[[package]] +name = "z3-sys" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa18ba5fbd4933e41ffb440c3fd91f91fe9cdb7310cce3ddfb6648563811de0" +dependencies = [ + "cmake", +] + +[[package]] +name = "zeroize" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zstd" +version = "0.6.1+zstd.1.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de55e77f798f205d8561b8fe2ef57abfb6e0ff2abe7fd3c089e119cdb5631a3" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "3.0.1+zstd.1.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1387cabcd938127b30ce78c4bf00b30387dddf704e3f0881dbc4ff62b5566f8c" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.4.20+zstd.1.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd5b733d7cf2d9447e2c3e76a5589b4f5e5ae065c22a2bc0b023cbc331b6c8e" +dependencies = [ + "cc", + "libc", +] diff --git a/crates/wiggle/Cargo.toml b/crates/wiggle/Cargo.toml index c7f6722ac66c..61d5224f72e4 100644 --- a/crates/wiggle/Cargo.toml +++ b/crates/wiggle/Cargo.toml @@ -17,7 +17,8 @@ wiggle-macro = { path = "macro", version = "0.27.0" } tracing = "0.1.15" bitflags = "1.2" async-trait = "0.1.42" -wasmtime = { path = "../wasmtime", version = "0.26.0", optional = true } +wasmtime = { path = "../wasmtime", version = "0.27.0", optional = true } +anyhow = "1.0" [badges] maintenance = { status = "actively-developed" } diff --git a/crates/wiggle/generate/src/wasmtime.rs b/crates/wiggle/generate/src/wasmtime.rs index e15fa85d0a39..76c82baf2006 100644 --- a/crates/wiggle/generate/src/wasmtime.rs +++ b/crates/wiggle/generate/src/wasmtime.rs @@ -53,7 +53,7 @@ pub fn link_module( pub fn #func_name( linker: &mut #rt::wasmtime_crate::Linker, get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, - ) -> anyhow::Result<()> + ) -> #rt::anyhow::Result<()> where U: #ctx_bound #send_bound { diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index 7ff0ba18639c..3c7acd718c0e 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -5,6 +5,8 @@ use std::sync::Arc; pub use wiggle_macro::{async_trait, from_witx}; +#[cfg(feature = "wasmtime")] +pub use anyhow; #[cfg(feature = "wasmtime")] pub use wiggle_macro::wasmtime_integration; diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md index e94d0ec9869e..1cafb1c87e9d 100644 --- a/docs/examples-rust-wasi.md +++ b/docs/examples-rust-wasi.md @@ -58,7 +58,7 @@ fn main() -> Result<()> { let wasi = WasiCtxBuilder::new() .inherit_stdio() .inherit_args()? - .build()?; + .build(); let mut store = Store::new(&engine, MyState { message: format!("hello!"), wasi, From e771b918b470a80f9a7d7ff12796da155d47373e Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 21 May 2021 15:01:55 -0700 Subject: [PATCH 83/90] docs: in new-api, trap handlers are installed at Engine creation which is a really nice design improvement! --- crates/runtime/src/traphandlers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs index 62dc531bdb2f..7332ae0a7e1a 100644 --- a/crates/runtime/src/traphandlers.rs +++ b/crates/runtime/src/traphandlers.rs @@ -54,7 +54,7 @@ static mut IS_WASM_PC: fn(usize) -> bool = |_| false; /// This function must not only be called globally once before entering /// WebAssembly but it must also be called once-per-thread that enters /// WebAssembly. Currently in wasmtime's integration this function is called on -/// creation of a `Store`. +/// creation of a `Engine`. /// /// The `is_wasm_pc` argument is used when a trap happens to determine if a /// program counter is the pc of an actual wasm trap or not. This is then used From 181ed09886ae5b377f826886ccbe8fc547996824 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 21 May 2021 22:14:09 -0700 Subject: [PATCH 84/90] Fix doc examples --- crates/wasi-common/src/pipe.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wasi-common/src/pipe.rs b/crates/wasi-common/src/pipe.rs index d4281d8329b6..0f28391a92d0 100644 --- a/crates/wasi-common/src/pipe.rs +++ b/crates/wasi-common/src/pipe.rs @@ -30,7 +30,7 @@ use std::sync::{Arc, RwLock}; /// let clocks = todo!(); /// let sched = todo!(); /// let table = Table::new(); -/// let ctx = WasiCtx::builder(random, clocks, sched, table) +/// let ctx = WasiCtx::new(random, clocks, sched, table) /// .stdin(Box::new(stdin.clone())) /// .build(); /// ``` @@ -199,7 +199,7 @@ impl WasiFile for ReadPipe { /// let clocks = todo!(); /// let sched = todo!(); /// let table = Table::new(); -/// let ctx = WasiCtx::builder(random, clocks, sched, table) +/// let ctx = WasiCtx::new(random, clocks, sched, table) /// .stdout(Box::new(stdout.clone())) /// .build(); /// // use ctx in an instance, then make sure it is dropped: From 8515dc05033ade66d265668f01ab1e6b173687a4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 22 May 2021 09:14:38 -0700 Subject: [PATCH 85/90] Try to fix tests --- crates/wasi-common/src/pipe.rs | 10 ++++------ scripts/publish.rs | 9 +++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/wasi-common/src/pipe.rs b/crates/wasi-common/src/pipe.rs index 0f28391a92d0..f1e088c4b411 100644 --- a/crates/wasi-common/src/pipe.rs +++ b/crates/wasi-common/src/pipe.rs @@ -30,9 +30,8 @@ use std::sync::{Arc, RwLock}; /// let clocks = todo!(); /// let sched = todo!(); /// let table = Table::new(); -/// let ctx = WasiCtx::new(random, clocks, sched, table) -/// .stdin(Box::new(stdin.clone())) -/// .build(); +/// let mut ctx = WasiCtx::new(random, clocks, sched, table); +/// ctx.set_stdin(Box::new(stdin.clone())); /// ``` #[derive(Debug)] pub struct ReadPipe { @@ -199,9 +198,8 @@ impl WasiFile for ReadPipe { /// let clocks = todo!(); /// let sched = todo!(); /// let table = Table::new(); -/// let ctx = WasiCtx::new(random, clocks, sched, table) -/// .stdout(Box::new(stdout.clone())) -/// .build(); +/// let mut ctx = WasiCtx::new(random, clocks, sched, table); +/// ctx.set_stdout(Box::new(stdout.clone())); /// // use ctx in an instance, then make sure it is dropped: /// drop(ctx); /// let contents: Vec = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner(); diff --git a/scripts/publish.rs b/scripts/publish.rs index 3baea0dc14f4..2cbd8b469ed3 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -43,10 +43,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[ // wiggle "wiggle-generate", "wiggle-macro", - // wasi-common - "wasi-common", - "wasi-cap-std-sync", - "wasi-tokio", // wasmtime "lightbeam", "wasmtime-fiber", @@ -60,7 +56,12 @@ const CRATES_TO_PUBLISH: &[&str] = &[ "wasmtime-jit", "wasmtime-cache", "wasmtime", + // wasi-common/wiggle "wiggle", + "wasi-common", + "wasi-cap-std-sync", + "wasi-tokio", + // other mic wasmtime crates "wasmtime-wasi", "wasmtime-wasi-nn", "wasmtime-wasi-crypto", From a8fd750a1b1ad45491d88f93d13bd1a06eb139d7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 22 May 2021 09:40:08 -0700 Subject: [PATCH 86/90] Fix signature of `wasmtime_table_new` --- crates/c-api/include/wasmtime/table.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/c-api/include/wasmtime/table.h b/crates/c-api/include/wasmtime/table.h index e159088d7d85..630cee9ecae6 100644 --- a/crates/c-api/include/wasmtime/table.h +++ b/crates/c-api/include/wasmtime/table.h @@ -32,7 +32,7 @@ extern "C" { WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new( wasmtime_context_t *store, const wasm_tabletype_t *ty, - wasmtime_val_t *init, + const wasmtime_val_t *init, wasmtime_table_t *table ); From c3ab4df07e5f5c5c7f93c905a25fcf4f87edd2f8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 28 May 2021 08:37:15 -0700 Subject: [PATCH 87/90] Refactor bench-api slightly --- crates/bench-api/src/lib.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/bench-api/src/lib.rs b/crates/bench-api/src/lib.rs index 7d441ce9f4e5..8835391b074f 100644 --- a/crates/bench-api/src/lib.rs +++ b/crates/bench-api/src/lib.rs @@ -466,24 +466,19 @@ impl BenchState { .as_ref() .expect("compile the module before instantiating it"); - let wasi_cx = (self.make_wasi_cx)().context("failed to create a WASI context")?; + let host = HostState { + wasi: (self.make_wasi_cx)().context("failed to create a WASI context")?, + #[cfg(feature = "wasi-nn")] + wasi_nn: wasmtime_wasi_nn::WasiNnCtx::new()?, + #[cfg(feature = "wasi-crypto")] + wasi_crypto: wasmtime_wasi_nn::WasiCryptoCtx::new(), + }; // NB: Start measuring instantiation time *after* we've created the WASI // context, since that needs to do file I/O to setup // stdin/stdout/stderr. (self.instantiation_start)(self.instantiation_timer); - - let mut store = Store::new( - self.linker.engine(), - HostState { - wasi: wasi_cx, - #[cfg(feature = "wasi-nn")] - wasi_nn: wasmtime_wasi_nn::WasiNnCtx::new()?, - #[cfg(feature = "wasi-crypto")] - wasi_crypto: wasmtime_wasi_nn::WasiCryptoCtx::new(), - }, - ); - + let mut store = Store::new(self.linker.engine(), host); let instance = self.linker.instantiate(&mut store, &module)?; (self.instantiation_end)(self.instantiation_timer); From 2fa715b2f8847f7011be4481e2a7717120ac7d9b Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 1 Jun 2021 10:24:28 -0700 Subject: [PATCH 88/90] Update crates/wiggle/Cargo.toml Co-authored-by: Benjamin Bouvier --- crates/wiggle/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wiggle/Cargo.toml b/crates/wiggle/Cargo.toml index 61d5224f72e4..b2a72f355050 100644 --- a/crates/wiggle/Cargo.toml +++ b/crates/wiggle/Cargo.toml @@ -17,7 +17,7 @@ wiggle-macro = { path = "macro", version = "0.27.0" } tracing = "0.1.15" bitflags = "1.2" async-trait = "0.1.42" -wasmtime = { path = "../wasmtime", version = "0.27.0", optional = true } +wasmtime = { path = "../wasmtime", version = "0.27.0", optional = true, default-features = false, features = ["async"] } anyhow = "1.0" [badges] From d4965d83d1116a7303fc855e54838eeda6089144 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 1 Jun 2021 14:03:04 -0700 Subject: [PATCH 89/90] Remove async from wiggle's wasmtime dep That's enabled through `wasmtime_async` --- crates/wiggle/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wiggle/Cargo.toml b/crates/wiggle/Cargo.toml index b2a72f355050..9b6334b26799 100644 --- a/crates/wiggle/Cargo.toml +++ b/crates/wiggle/Cargo.toml @@ -17,7 +17,7 @@ wiggle-macro = { path = "macro", version = "0.27.0" } tracing = "0.1.15" bitflags = "1.2" async-trait = "0.1.42" -wasmtime = { path = "../wasmtime", version = "0.27.0", optional = true, default-features = false, features = ["async"] } +wasmtime = { path = "../wasmtime", version = "0.27.0", optional = true, default-features = false } anyhow = "1.0" [badges] From c99baa3f0e72e8582af4acc5441295979f046b70 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 1 Jun 2021 14:32:02 -0700 Subject: [PATCH 90/90] Don't generate wasmtime config in wasi-common This is relegated to wasmtime-wasi --- crates/wasi-common/src/snapshots/preview_0.rs | 1 + crates/wasi-common/src/snapshots/preview_1.rs | 3 ++- crates/wiggle/generate/src/config.rs | 15 +++++++++++++++ crates/wiggle/macro/src/lib.rs | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/wasi-common/src/snapshots/preview_0.rs b/crates/wasi-common/src/snapshots/preview_0.rs index ec059bf7fa46..616a362aab75 100644 --- a/crates/wasi-common/src/snapshots/preview_0.rs +++ b/crates/wasi-common/src/snapshots/preview_0.rs @@ -18,6 +18,7 @@ wiggle::from_witx!({ witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"], errors: { errno => Error }, async: *, + wasmtime: false, }); impl wiggle::GuestErrorType for types::Errno { diff --git a/crates/wasi-common/src/snapshots/preview_1.rs b/crates/wasi-common/src/snapshots/preview_1.rs index 1d74c64dc6a5..896d7a1e1dfc 100644 --- a/crates/wasi-common/src/snapshots/preview_1.rs +++ b/crates/wasi-common/src/snapshots/preview_1.rs @@ -25,7 +25,8 @@ wiggle::from_witx!({ // Note: not every function actually needs to be async, however, nearly all of them do, and // keeping that set the same in this macro and the wasmtime_wiggle / lucet_wiggle macros is // tedious, and there is no cost to having a sync function be async in this case. - async: * + async: *, + wasmtime: false }); impl wiggle::GuestErrorType for types::Errno { diff --git a/crates/wiggle/generate/src/config.rs b/crates/wiggle/generate/src/config.rs index 8739d6a121a1..3d164d70f1d9 100644 --- a/crates/wiggle/generate/src/config.rs +++ b/crates/wiggle/generate/src/config.rs @@ -14,6 +14,7 @@ pub struct Config { pub witx: WitxConf, pub errors: ErrorConf, pub async_: AsyncConf, + pub wasmtime: bool, } mod kw { @@ -22,6 +23,7 @@ mod kw { syn::custom_keyword!(block_on); syn::custom_keyword!(errors); syn::custom_keyword!(target); + syn::custom_keyword!(wasmtime); } #[derive(Debug, Clone)] @@ -29,6 +31,7 @@ pub enum ConfigField { Witx(WitxConf), Error(ErrorConf), Async(AsyncConf), + Wasmtime(bool), } impl Parse for ConfigField { @@ -60,6 +63,10 @@ impl Parse for ConfigField { blocking: true, functions: input.parse()?, })) + } else if lookahead.peek(kw::wasmtime) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::Wasmtime(input.parse::()?.value)) } else { Err(lookahead.error()) } @@ -71,6 +78,7 @@ impl Config { let mut witx = None; let mut errors = None; let mut async_ = None; + let mut wasmtime = None; for f in fields { match f { ConfigField::Witx(c) => { @@ -91,6 +99,12 @@ impl Config { } async_ = Some(c); } + ConfigField::Wasmtime(c) => { + if wasmtime.is_some() { + return Err(Error::new(err_loc, "duplicate `wasmtime` field")); + } + wasmtime = Some(c); + } } } Ok(Config { @@ -99,6 +113,7 @@ impl Config { .ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, errors: errors.take().unwrap_or_default(), async_: async_.take().unwrap_or_default(), + wasmtime: wasmtime.unwrap_or(true), }) } diff --git a/crates/wiggle/macro/src/lib.rs b/crates/wiggle/macro/src/lib.rs index 582a4bdcb680..394ee47bb366 100644 --- a/crates/wiggle/macro/src/lib.rs +++ b/crates/wiggle/macro/src/lib.rs @@ -152,7 +152,7 @@ pub fn from_witx(args: TokenStream) -> TokenStream { &config.errors, &config.async_, &doc, - cfg!(feature = "wasmtime"), + cfg!(feature = "wasmtime") && config.wasmtime, ) .expect("validating codegen settings");