diff --git a/Cargo.lock b/Cargo.lock index b5a81b0377bb..ab6376f1bc9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2395,6 +2395,7 @@ dependencies = [ name = "wasmtime-c-api" version = "0.12.0" dependencies = [ + "anyhow", "wasi-common", "wasmtime", "wasmtime-wasi", diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index f7eb7b47df07..cf3a53759ebc 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -12,6 +12,7 @@ mod externals; mod frame_info; mod func; mod instance; +mod linker; mod module; mod r#ref; mod runtime; @@ -24,6 +25,7 @@ pub use crate::externals::*; pub use crate::frame_info::FrameInfo; pub use crate::func::*; pub use crate::instance::Instance; +pub use crate::linker::*; pub use crate::module::Module; pub use crate::r#ref::{AnyRef, HostInfo, HostRef}; pub use crate::runtime::*; diff --git a/crates/api/src/linker.rs b/crates/api/src/linker.rs new file mode 100644 index 000000000000..b991e366b0fa --- /dev/null +++ b/crates/api/src/linker.rs @@ -0,0 +1,368 @@ +use crate::{ + Extern, ExternType, Func, FuncType, GlobalType, ImportType, Instance, IntoFunc, Module, Store, +}; +use anyhow::{bail, Result}; +use std::collections::hash_map::{Entry, HashMap}; +use std::rc::Rc; + +/// Structure used to link wasm modules/instances together. +/// +/// 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. +/// +/// ## Name Resolution +/// +/// As mentioned previously, `Linker` is a form of name resolver. It will be +/// using the string-based names of imports on a module to attempt to select a +/// matching item to hook up to it. This name resolution has two-levels of +/// namespaces, a module level and a name level. Each item is defined within a +/// module and then has its own name. This basically follows the wasm standard +/// for modularization. +/// +/// Names in a `Linker` can be defined twice, but only for different signatures +/// of items. This means that every item defined in a `Linker` has a unique +/// name/type pair. For example you can define two functions with the module +/// name `foo` and item name `bar`, so long as they have different function +/// signatures. Currently duplicate memories and tables are not allowed, only +/// one-per-name is allowed. +pub struct Linker { + store: Store, + string2idx: HashMap, usize>, + strings: Vec>, + map: HashMap, +} + +#[derive(Hash, PartialEq, Eq)] +struct ImportKey { + name: usize, + module: usize, + kind: ImportKind, +} + +#[derive(Hash, PartialEq, Eq, Debug)] +enum ImportKind { + Func(FuncType), + Global(GlobalType), + Memory, + Table, +} + +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(store: &Store) -> Linker { + Linker { + store: store.clone(), + map: HashMap::new(), + string2idx: HashMap::new(), + strings: Vec::new(), + } + } + + /// Defines a new item in this [`Linker`]. + /// + /// This method will add a new definition, by name, to this instance of + /// [`Linker`]. The `module` and `name` provided are what to name the + /// `item`. + /// + /// # Errors + /// + /// Returns an error if the `module` and `name` already identify an item + /// of the same type as the `item` provided. For more information see the + /// documentation on [`Linker`]. + /// + /// Also returns an error if `item` comes from a different store than this + /// [`Linker`] was created with. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let mut linker = Linker::new(&store); + /// let ty = GlobalType::new(ValType::I32, Mutability::Const); + /// let global = Global::new(&store, ty, Val::I32(0x1234))?; + /// linker.define("host", "offset", global); + /// + /// let wat = r#" + /// (module + /// (import "host" "offset" (global i32)) + /// (memory 1) + /// (data (global.get 0) "foo") + /// ) + /// "#; + /// let module = Module::new(&store, wat)?; + /// linker.instantiate(&module)?; + /// # Ok(()) + /// # } + /// ``` + pub fn define( + &mut self, + module: &str, + name: &str, + item: impl Into, + ) -> Result<&mut Self> { + self._define(module, name, item.into()) + } + + fn _define(&mut self, module: &str, name: &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.ty(), item)?; + Ok(self) + } + + /// Convenience wrapper to define a function import. + /// + /// This method is a convenience wrapper around [`Linker::define`] which + /// internally delegates to [`Func::wrap`]. + /// + /// # Errors + /// + /// Returns an error if the `module` and `name` already identify a function + /// of the same signature as `func`. For more information see the + /// documentation on [`Linker`]. + /// + /// # 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)?; + /// linker.func("host", "log_i32", |x: i32| println!("{}", x))?; + /// linker.func("host", "log_str", |caller: Caller, ptr: i32, len: i32| { + /// // ... + /// })?; + /// + /// let wat = r#" + /// (module + /// (import "host" "double" (func (param i32) (result i32))) + /// (import "host" "log_i32" (func (param i32))) + /// (import "host" "log_str" (func (param i32 i32))) + /// ) + /// "#; + /// let module = Module::new(&store, wat)?; + /// linker.instantiate(&module)?; + /// # Ok(()) + /// # } + /// ``` + pub fn func( + &mut self, + module: &str, + name: &str, + func: impl IntoFunc, + ) -> Result<&mut Self> { + self._define(module, name, Func::wrap(&self.store, func).into()) + } + + /// Convenience wrapper to define an entire [`Instance`] in this linker. + /// + /// This function is a convenience wrapper around [`Linker::define`] which + /// will define all exports on `instance` into this linker. The module name + /// for each export is `module_name`, and the name for each export is the + /// name in the instance itself. + /// + /// # Errors + /// + /// Returns an error if the any item is redefined twice in this linker (for + /// example the same `module_name` was already defined), or if `instance` + /// comes from a different [`Store`] than this [`Linker`] originally was + /// created with. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let mut linker = Linker::new(&store); + /// + /// // Instantiate a small instance... + /// let wat = r#"(module (func (export "run") ))"#; + /// let module = Module::new(&store, wat)?; + /// let instance = linker.instantiate(&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)?; + /// + /// let wat = r#" + /// (module + /// (import "instance1" "run" (func $instance1_run)) + /// (func (export "run") + /// call $instance1_run + /// ) + /// ) + /// "#; + /// let module = Module::new(&store, wat)?; + /// let instance = linker.instantiate(&module)?; + /// # 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, item) in instance.module().exports().iter().zip(instance.exports()) { + self.insert(module_name, export.name(), export.ty(), item.clone())?; + } + Ok(self) + } + + fn insert(&mut self, module: &str, name: &str, ty: &ExternType, item: Extern) -> Result<()> { + let key = self.import_key(module, name, ty); + match self.map.entry(key) { + Entry::Occupied(o) => bail!( + "import of `{}::{}` with as {:?} defined twice", + module, + name, + o.key().kind, + ), + Entry::Vacant(v) => { + v.insert(item); + } + } + Ok(()) + } + + fn import_key(&mut self, module: &str, name: &str, ty: &ExternType) -> ImportKey { + ImportKey { + module: self.intern_str(module), + name: self.intern_str(name), + kind: self.import_kind(ty), + } + } + + fn import_kind(&self, ty: &ExternType) -> ImportKind { + match ty { + ExternType::Func(f) => ImportKind::Func(f.clone()), + ExternType::Global(f) => ImportKind::Global(f.clone()), + ExternType::Memory(_) => ImportKind::Memory, + ExternType::Table(_) => ImportKind::Table, + } + } + + fn intern_str(&mut self, string: &str) -> usize { + if let Some(idx) = self.string2idx.get(string) { + return *idx; + } + let string: Rc = string.into(); + let idx = self.strings.len(); + self.strings.push(string.clone()); + self.string2idx.insert(string, idx); + idx + } + + /// Attempts to instantiate the `module` provided. + /// + /// This method will attempt to assemble a list of imports that correspond + /// to the imports required by the [`Module`] provided. This list + /// of imports is then passed to [`Instance::new`] to continue the + /// instantiation process. + /// + /// Each import of `module` will be looked up in this [`Linker`] and must + /// have previously been defined. If it was previously defined with an + /// incorrect signature or if it was not prevoiusly defined then an error + /// will be returned because the import can not be satisfied. + /// + /// # Errors + /// + /// This method can fail because an import may not be found, or because + /// instantiation itself may fail. For information on instantiation + /// failures see [`Instance::new`]. + /// + /// # 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 wat = r#" + /// (module + /// (import "host" "double" (func (param i32) (result i32))) + /// ) + /// "#; + /// let module = Module::new(&store, wat)?; + /// linker.instantiate(&module)?; + /// # Ok(()) + /// # } + /// ``` + pub fn instantiate(&self, module: &Module) -> Result { + let mut imports = Vec::new(); + for import in module.imports() { + if let Some(item) = self.import_get(import) { + imports.push(item.clone()); + continue; + } + + let mut options = String::new(); + for i in self.map.keys() { + if &*self.strings[i.module] != import.module() + || &*self.strings[i.name] != import.name() + { + continue; + } + options.push_str(" * "); + options.push_str(&format!("{:?}", i.kind)); + options.push_str("\n"); + } + if options.len() == 0 { + bail!( + "import of `{}::{}` has not been defined", + import.module(), + import.name() + ) + } + + bail!( + "failed to find import of `{}::{}` with matching signature\n\ + desired signature was: {:?}\n\ + signatures available:\n\n{}", + import.module(), + import.name(), + import.ty(), + options, + ) + } + + Instance::new(module, &imports) + } + + fn import_get(&self, import: &ImportType) -> Option<&Extern> { + let key = ImportKey { + module: *self.string2idx.get(import.module())?, + name: *self.string2idx.get(import.name())?, + kind: self.import_kind(import.ty()), + }; + self.map.get(&key) + } +} diff --git a/crates/api/src/types.rs b/crates/api/src/types.rs index 8e344d5d0a2a..a1a8d09699e2 100644 --- a/crates/api/src/types.rs +++ b/crates/api/src/types.rs @@ -5,7 +5,7 @@ use wasmtime_environ::{ir, wasm}; // Type attributes /// Indicator of whether a global is mutable or not -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] pub enum Mutability { /// The global is constant and its value does not change Const, @@ -17,7 +17,7 @@ pub enum Mutability { /// table/memory types. /// /// A minimum is always available but the maximum may not be present. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct Limits { min: u32, max: Option, @@ -48,7 +48,7 @@ impl Limits { // Value Types /// A list of all possible value types in WebAssembly. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub enum ValType { /// Signed 32 bit integer. I32, @@ -114,7 +114,7 @@ impl ValType { /// /// This list can be found in [`ImportType`] or [`ExportType`], so these types /// can either be imported or exported. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub enum ExternType { /// This external type is the type of a WebAssembly function. Func(FuncType), @@ -168,7 +168,7 @@ fn from_wasmtime_abiparam(param: &ir::AbiParam) -> Option { /// A descriptor for a function in a WebAssembly module. /// /// WebAssembly functions can have 0 or more parameters and results. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct FuncType { params: Box<[ValType]>, results: Box<[ValType]>, @@ -249,7 +249,7 @@ impl FuncType { /// This type describes an instance of a global in a WebAssembly module. Globals /// are local to an [`Instance`](crate::Instance) and are either immutable or /// mutable. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct GlobalType { content: ValType, mutability: Mutability, @@ -295,7 +295,7 @@ impl GlobalType { /// Tables are contiguous chunks of a specific element, typically a `funcref` or /// an `anyref`. The most common use for tables is a function table through /// which `call_indirect` can invoke other functions. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct TableType { element: ValType, limits: Limits, @@ -336,7 +336,7 @@ impl TableType { /// /// Memories are described in units of pages (64KB) and represent contiguous /// chunks of addressable memory. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct MemoryType { limits: Limits, } @@ -366,7 +366,7 @@ impl MemoryType { /// [`Module::imports`](crate::Module::imports) API. Each [`ImportType`] /// describes an import into the wasm module with the module/name that it's /// imported from as well as the type of item that's being imported. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct ImportType { module: String, name: String, @@ -409,7 +409,7 @@ impl ImportType { /// [`Module::exports`](crate::Module::exports) accessor and describes what /// names are exported from a wasm module and the type of the item that is /// exported. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct ExportType { name: String, ty: ExternType, diff --git a/crates/api/tests/linker.rs b/crates/api/tests/linker.rs new file mode 100644 index 000000000000..4edba8eee9fe --- /dev/null +++ b/crates/api/tests/linker.rs @@ -0,0 +1,66 @@ +use anyhow::Result; +use wasmtime::*; + +#[test] +fn link_undefined() -> Result<()> { + let store = Store::default(); + let linker = Linker::new(&store); + let module = Module::new(&store, r#"(module (import "" "" (func)))"#)?; + assert!(linker.instantiate(&module).is_err()); + let module = Module::new(&store, r#"(module (import "" "" (global i32)))"#)?; + assert!(linker.instantiate(&module).is_err()); + let module = Module::new(&store, r#"(module (import "" "" (memory 1)))"#)?; + assert!(linker.instantiate(&module).is_err()); + let module = Module::new(&store, r#"(module (import "" "" (table 1 funcref)))"#)?; + assert!(linker.instantiate(&module).is_err()); + Ok(()) +} + +#[test] +fn link_twice_bad() -> Result<()> { + let store = Store::default(); + let mut linker = Linker::new(&store); + + // functions + linker.func("", "", || {})?; + assert!(linker.func("", "", || {}).is_err()); + assert!(linker + .func("", "", || -> Result<(), Trap> { loop {} }) + .is_err()); + linker.func("", "", |_: i32| {})?; + + // globals + let ty = GlobalType::new(ValType::I32, Mutability::Const); + let global = Global::new(&store, ty, Val::I32(0))?; + linker.define("", "", global.clone())?; + assert!(linker.define("", "", global.clone()).is_err()); + + let ty = GlobalType::new(ValType::I32, Mutability::Var); + let global = Global::new(&store, ty, Val::I32(0))?; + linker.define("", "", global.clone())?; + assert!(linker.define("", "", global.clone()).is_err()); + + let ty = GlobalType::new(ValType::I64, Mutability::Const); + let global = Global::new(&store, ty, Val::I64(0))?; + linker.define("", "", global.clone())?; + assert!(linker.define("", "", global.clone()).is_err()); + + // memories + let ty = MemoryType::new(Limits::new(1, None)); + let memory = Memory::new(&store, ty); + linker.define("", "", memory.clone())?; + assert!(linker.define("", "", memory.clone()).is_err()); + let ty = MemoryType::new(Limits::new(2, None)); + let memory = Memory::new(&store, ty); + assert!(linker.define("", "", memory.clone()).is_err()); + + // tables + let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); + let table = Table::new(&store, ty, Val::AnyRef(AnyRef::Null))?; + linker.define("", "", table.clone())?; + assert!(linker.define("", "", table.clone()).is_err()); + let ty = TableType::new(ValType::FuncRef, Limits::new(2, None)); + let table = Table::new(&store, ty, Val::AnyRef(AnyRef::Null))?; + assert!(linker.define("", "", table.clone()).is_err()); + Ok(()) +} diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 597826957501..fdba969b1bde 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -17,6 +17,7 @@ test = false doctest = false [dependencies] +anyhow = "1.0" wasmtime = { path = "../api", default-features = false } wasi-common = { path = "../wasi-common" } wasmtime-wasi = { path = "../wasi" } diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 52e39825af8a..f3e8d3aa0a7a 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -4,6 +4,7 @@ #define WASMTIME_API_H #include +#include #ifdef __cplusplus extern "C" { @@ -67,6 +68,39 @@ WASM_API_EXTERN bool wasmtime_wat2wasm( own wasm_byte_vec_t *error_message ); +#define WASMTIME_DECLARE_OWN(name) \ + typedef struct wasmtime_##name##_t wasmtime_##name##_t; \ + \ + WASM_API_EXTERN void wasmtime_##name##_delete(own wasmtime_##name##_t*); + +WASMTIME_DECLARE_OWN(linker) + +WASM_API_EXTERN own wasmtime_linker_t* wasmtime_linker_new(wasm_store_t* store); + +WASM_API_EXTERN bool wasmtime_linker_define( + wasmtime_linker_t *linker, + const wasm_name_t *module, + const wasm_name_t *name, + const wasm_extern_t *item +); + +WASM_API_EXTERN bool wasmtime_linker_define_wasi( + wasmtime_linker_t *linker, + const wasi_instance_t *instance +); + +WASM_API_EXTERN bool wasmtime_linker_define_instance( + wasmtime_linker_t *linker, + const wasm_name_t *name, + const wasm_instance_t *instance +); + +WASM_API_EXTERN wasm_instance_t* wasmtime_linker_instantiate( + const wasmtime_linker_t *linker, + const wasm_module_t *module, + own wasm_trap_t **trap +); + #undef own #ifdef __cplusplus diff --git a/crates/c-api/src/ext.rs b/crates/c-api/src/ext.rs index a81be32b9c23..88688c8af7bb 100644 --- a/crates/c-api/src/ext.rs +++ b/crates/c-api/src/ext.rs @@ -1,9 +1,9 @@ //! This file defines the extern "C" API extension, which are specific //! to the wasmtime implementation. -use crate::{wasm_byte_vec_t, wasm_config_t, wasm_engine_t}; +use crate::*; use std::str; -use wasmtime::{OptLevel, ProfilingStrategy, Strategy}; +use wasmtime::{Extern, Linker, OptLevel, ProfilingStrategy, Strategy}; #[repr(u8)] #[derive(Clone)] @@ -136,3 +136,80 @@ pub unsafe extern "C" fn wasmtime_wat2wasm( } } } + +#[repr(C)] +pub struct wasmtime_linker_t { + linker: Linker, +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_new(store: *mut wasm_store_t) -> *mut wasmtime_linker_t { + Box::into_raw(Box::new(wasmtime_linker_t { + linker: Linker::new(&(*store).store.borrow()), + })) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_delete(linker: *mut wasmtime_linker_t) { + drop(Box::from_raw(linker)); +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_define( + linker: *mut wasmtime_linker_t, + module: *const wasm_name_t, + name: *const wasm_name_t, + item: *const wasm_extern_t, +) -> bool { + let linker = &mut (*linker).linker; + let module = match str::from_utf8((*module).as_slice()) { + Ok(s) => s, + Err(_) => return false, + }; + let name = match str::from_utf8((*name).as_slice()) { + Ok(s) => s, + Err(_) => return false, + }; + let item = match &(*item).which { + ExternHost::Func(e) => Extern::Func(e.borrow().clone()), + ExternHost::Table(e) => Extern::Table(e.borrow().clone()), + ExternHost::Global(e) => Extern::Global(e.borrow().clone()), + ExternHost::Memory(e) => Extern::Memory(e.borrow().clone()), + }; + linker.define(module, name, item).is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_define_wasi( + linker: *mut wasmtime_linker_t, + instance: *const wasi_instance_t, +) -> bool { + let linker = &mut (*linker).linker; + (*instance).wasi.add_to_linker(linker).is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_define_instance( + linker: *mut wasmtime_linker_t, + name: *const wasm_name_t, + instance: *const wasm_instance_t, +) -> bool { + let linker = &mut (*linker).linker; + let name = match str::from_utf8((*name).as_slice()) { + Ok(s) => s, + Err(_) => return false, + }; + linker + .instance(name, &(*instance).instance.borrow()) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_instantiate( + linker: *const wasmtime_linker_t, + module: *const wasm_module_t, + trap: *mut *mut wasm_trap_t, +) -> *mut wasm_instance_t { + let linker = &(*linker).linker; + handle_instantiate(linker.instantiate(&(*module).module.borrow()), trap) +} diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index b1d7eb5051f5..2cd1c06f88f9 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -5,6 +5,7 @@ // TODO complete the C API +use anyhow::Result; use std::cell::RefCell; use std::panic::{self, AssertUnwindSafe}; use std::{mem, ptr, slice}; @@ -691,6 +692,15 @@ pub unsafe extern "C" fn wasm_instance_delete(instance: *mut wasm_instance_t) { let _ = Box::from_raw(instance); } +impl wasm_instance_t { + fn new(instance: Instance) -> wasm_instance_t { + wasm_instance_t { + instance: HostRef::new(instance), + exports_cache: RefCell::new(None), + } + } +} + #[no_mangle] pub unsafe extern "C" fn wasm_instance_new( store: *mut wasm_store_t, @@ -722,12 +732,16 @@ pub unsafe extern "C" fn wasm_instance_new( } return ptr::null_mut(); } - match Instance::new(module, &externs) { + handle_instantiate(Instance::new(module, &externs), result) +} + +unsafe fn handle_instantiate( + instance: Result, + result: *mut *mut wasm_trap_t, +) -> *mut wasm_instance_t { + match instance { Ok(instance) => { - let instance = Box::new(wasm_instance_t { - instance: HostRef::new(instance), - exports_cache: RefCell::new(None), - }); + let instance = Box::new(wasm_instance_t::new(instance)); if !result.is_null() { (*result) = ptr::null_mut(); } diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 5aeeb75e546f..8905d023fbdd 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -168,7 +168,7 @@ pub unsafe extern "C" fn wasi_config_preopen_dir( #[repr(C)] pub struct wasi_instance_t { - wasi: Wasi, + pub wasi: Wasi, export_cache: HashMap>, } diff --git a/crates/wasi-common/wig/src/wasi.rs b/crates/wasi-common/wig/src/wasi.rs index e49cf3cb23f7..2c1fcdcc1a78 100644 --- a/crates/wasi-common/wig/src/wasi.rs +++ b/crates/wasi-common/wig/src/wasi.rs @@ -266,8 +266,10 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { let mut get_exports = Vec::new(); let mut ctor_externs = Vec::new(); let mut ctor_fields = Vec::new(); + let mut linker_add = Vec::new(); for module in doc.modules() { + let module_name = module.name.as_str(); let module_id = Ident::new(module.name.as_str(), Span::call_site()); for func in module.funcs() { let name = func.name.as_str(); @@ -275,6 +277,9 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { fields.push(quote! { pub #name_ident: wasmtime::Func }); get_exports.push(quote! { #name => Some(&self.#name_ident) }); ctor_fields.push(name_ident.clone()); + linker_add.push(quote! { + linker.define(#module_name, #name, self.#name_ident.clone())?; + }); let mut shim_arg_decls = Vec::new(); let mut params = Vec::new(); @@ -501,6 +506,12 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { _ => None, } } + + /// Adds all wasi items to the specified `Linker`. + pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> { + #(#linker_add)* + Ok(()) + } } } } diff --git a/examples/linking.c b/examples/linking.c index 8f3d73af8aeb..27cf612da337 100644 --- a/examples/linking.c +++ b/examples/linking.c @@ -64,68 +64,38 @@ int main() { exit(1); } - // Create imports for `linking2` - wasm_importtype_vec_t linking2_import_types; - wasm_module_imports(linking2_module, &linking2_import_types); - const wasm_extern_t **linking2_imports = calloc(linking2_import_types.size, sizeof(void*)); - assert(linking2_imports); - for (int i = 0; i < linking2_import_types.size; i++) { - const wasm_extern_t *binding = wasi_instance_bind_import(wasi, linking2_import_types.data[i]); - if (binding != NULL) { - linking2_imports[i] = binding; + // 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); + bool ok = wasmtime_linker_define_wasi(linker, wasi); + assert(ok); + + // Instantiate `linking2` with our linker. + wasm_instance_t *linking2 = wasmtime_linker_instantiate(linker, linking2_module, &trap); + if (linking2 == NULL) { + if (trap == NULL) { + printf("> failed to link!\n"); } else { - printf("> Failed to satisfy import\n"); - exit(1); + print_trap(trap); } - } - wasm_importtype_vec_delete(&linking2_import_types); - - // Instantiate `linking2` - wasm_instance_t *linking2 = wasm_instance_new(store, linking2_module, linking2_imports, &trap); - if (linking2 == NULL) { - print_trap(trap); exit(1); } - free(linking2_imports); - wasm_extern_vec_t linking2_externs; - wasm_instance_exports(linking2, &linking2_externs); - wasm_exporttype_vec_t linking2_exports; - wasm_module_exports(linking2_module, &linking2_exports); - - // Create imports for `linking1` - wasm_importtype_vec_t linking1_import_types; - wasm_module_imports(linking1_module, &linking1_import_types); - const wasm_extern_t **linking1_imports = calloc(linking1_import_types.size, sizeof(void*)); - assert(linking1_imports); - for (int i = 0; i < linking1_import_types.size; i++) { - const wasm_importtype_t *import = linking1_import_types.data[i]; - const wasm_name_t *module = wasm_importtype_module(import); - const wasm_name_t *name = wasm_importtype_name(import); - if (strncmp(module->data, "linking2", module->size) == 0) { - const wasm_extern_t *e = NULL; - for (int j = 0; j < linking2_exports.size; j++) { - const wasm_name_t *export_name = wasm_exporttype_name(linking2_exports.data[j]); - if (name->size == export_name->size && - strncmp(name->data, export_name->data, name->size) == 0) { - e = linking2_externs.data[j]; - break; - } - } - if (e) { - linking1_imports[i] = e; - continue; - } - } - printf("> Failed to satisfy import\n"); - exit(1); - } - wasm_importtype_vec_delete(&linking1_import_types); + // Register our new `linking2` instance with the linker + wasm_name_t linking2_name; + linking2_name.data = "linking2"; + linking2_name.size = strlen(linking2_name.data); + ok = wasmtime_linker_define_instance(linker, &linking2_name, linking2); + assert(ok); - // Instantiate `linking1` - wasm_instance_t *linking1 = wasm_instance_new(store, linking1_module, linking1_imports, &trap); + // Instantiate `linking1` with the linker now that `linking2` is defined + wasm_instance_t *linking1 = wasmtime_linker_instantiate(linker, linking1_module, &trap); if (linking1 == NULL) { - print_trap(trap); + if (trap == NULL) { + printf("> failed to link!\n"); + } else { + print_trap(trap); + } exit(1); } @@ -143,9 +113,9 @@ int main() { // Clean up after ourselves at this point wasm_extern_vec_delete(&linking1_externs); - wasm_extern_vec_delete(&linking2_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); diff --git a/examples/linking.rs b/examples/linking.rs index bee24e72754f..88dfb9956cda 100644 --- a/examples/linking.rs +++ b/examples/linking.rs @@ -9,47 +9,23 @@ use wasmtime_wasi::{Wasi, WasiCtx}; fn main() -> Result<()> { let store = Store::default(); + // 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, WasiCtx::new(std::env::args())?); + wasi.add_to_linker(&mut linker)?; + // Load and compile our two modules let linking1 = Module::from_file(&store, "examples/linking1.wat")?; let linking2 = Module::from_file(&store, "examples/linking2.wat")?; - // Instantiate the first, `linking2`, which uses WASI imports - let wasi = Wasi::new(&store, WasiCtx::new(std::env::args())?); - let mut imports = Vec::new(); - for import in linking2.imports() { - if import.module() == "wasi_snapshot_preview1" { - if let Some(export) = wasi.get_export(import.name()) { - imports.push(Extern::from(export.clone())); - continue; - } - } - panic!( - "couldn't find import for `{}::{}`", - import.module(), - import.name() - ); - } - let linking2 = Instance::new(&linking2, &imports)?; - - // And using the previous instance we can create the imports for `linking1`, - // using the previous exports. - let mut imports = Vec::new(); - for import in linking1.imports() { - if import.module() == "linking2" { - if let Some(export) = linking2.get_export(import.name()) { - imports.push(export.clone()); - continue; - } - } - panic!( - "couldn't find import for `{}::{}`", - import.module(), - import.name() - ); - } - let linking1 = Instance::new(&linking1, &imports)?; + // 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)?; - // And once everything is instantiated we can run! + // And with that we can perform the final link and the execute the module. + let linking1 = linker.instantiate(&linking1)?; let run = linking1.get_export("run").and_then(|e| e.func()).unwrap(); let run = run.get0::<()>()?; run()?;