use crate::{
AsContext,
AsContextMut,
Caller,
Engine,
Error,
Extern,
ExternType,
Func,
FuncType,
Instance,
IntoFunc,
Module,
Val,
collections::{
StringInterner,
string_interner::{InternHint, Sym as Symbol},
},
func::{FuncEntity, HostFuncEntity, HostFuncTrampolineEntity},
module::{ImportName, ImportType},
};
use alloc::{
collections::{BTreeMap, btree_map::Entry},
vec::Vec,
};
use core::fmt::{self, Debug, Display};
#[derive(Debug)]
pub enum LinkerError {
DuplicateDefinition {
import_name: ImportName,
},
MissingDefinition {
name: ImportName,
ty: ExternType,
},
InvalidTypeDefinition {
name: ImportName,
expected: ExternType,
found: ExternType,
},
}
impl LinkerError {
fn missing_definition(import: &ImportType) -> Self {
Self::MissingDefinition {
name: import.import_name().clone(),
ty: import.ty().clone(),
}
}
fn invalid_type_definition(import: &ImportType, found: &ExternType) -> Self {
Self::InvalidTypeDefinition {
name: import.import_name().clone(),
expected: import.ty().clone(),
found: found.clone(),
}
}
}
impl core::error::Error for LinkerError {}
impl Display for LinkerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::DuplicateDefinition { import_name } => {
write!(
f,
"encountered duplicate definition with name `{import_name}`",
)
}
Self::MissingDefinition { name, ty } => {
write!(
f,
"cannot find definition for import {name} with type {ty:?}",
)
}
Self::InvalidTypeDefinition {
name,
expected,
found,
} => {
write!(
f,
"found definition for import {name} with type {expected:?} but found type {found:?}"
)
}
}
}
}
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
#[repr(transparent)]
struct ImportKey {
module_and_name: u64,
}
impl Debug for ImportKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImportKey")
.field("module", &self.module())
.field("name", &self.name())
.finish()
}
}
impl ImportKey {
#[inline]
pub fn new(module: Symbol, name: Symbol) -> Self {
let module_and_name = (u64::from(module.into_u32()) << 32) | u64::from(name.into_u32());
Self { module_and_name }
}
#[inline]
pub fn module(&self) -> Symbol {
Symbol::from_u32((self.module_and_name >> 32) as u32)
}
#[inline]
pub fn name(&self) -> Symbol {
Symbol::from_u32(self.module_and_name as u32)
}
}
#[derive(Debug)]
enum Definition<T> {
Extern(Extern),
HostFunc(HostFuncTrampolineEntity<T>),
}
impl<T> Clone for Definition<T> {
fn clone(&self) -> Self {
match self {
Self::Extern(definition) => Self::Extern(*definition),
Self::HostFunc(host_func) => Self::HostFunc(host_func.clone()),
}
}
}
impl<T> Definition<T> {
fn as_extern(&self) -> Option<&Extern> {
match self {
Definition::Extern(item) => Some(item),
Definition::HostFunc(_) => None,
}
}
pub fn ty(&self, ctx: impl AsContext) -> ExternType {
match self {
Definition::Extern(item) => item.ty(ctx),
Definition::HostFunc(host_func) => ExternType::Func(host_func.func_type().clone()),
}
}
pub fn as_func(&self, mut ctx: impl AsContextMut<Data = T>) -> Option<Func> {
match self {
Definition::Extern(Extern::Func(func)) => Some(*func),
Definition::HostFunc(host_func) => {
let trampoline = ctx
.as_context_mut()
.store
.alloc_trampoline(host_func.trampoline().clone());
let ty = host_func.func_type();
let entity = HostFuncEntity::new(ctx.as_context().engine(), ty, trampoline);
let func = ctx
.as_context_mut()
.store
.inner
.alloc_func(FuncEntity::Host(entity));
Some(func)
}
_ => None,
}
}
}
#[derive(Debug)]
pub struct Linker<T> {
engine: Engine,
inner: LinkerInner<T>,
}
impl<T> Clone for Linker<T> {
fn clone(&self) -> Linker<T> {
Self {
engine: self.engine.clone(),
inner: self.inner.clone(),
}
}
}
impl<T> Default for Linker<T> {
fn default() -> Self {
Self::new(&Engine::default())
}
}
impl<T> Linker<T> {
pub fn new(engine: &Engine) -> Self {
Self {
engine: engine.clone(),
inner: LinkerInner::default(),
}
}
pub fn engine(&self) -> &Engine {
&self.engine
}
pub fn allow_shadowing(&mut self, allow: bool) -> &mut Self {
self.inner.allow_shadowing(allow);
self
}
pub fn define(
&mut self,
module: &str,
name: &str,
item: impl Into<Extern>,
) -> Result<&mut Self, LinkerError> {
let key = self.inner.new_import_key(module, name);
self.inner.insert(key, Definition::Extern(item.into()))?;
Ok(self)
}
pub fn func_new(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Error> + Send + Sync + 'static,
) -> Result<&mut Self, LinkerError> {
let func = HostFuncTrampolineEntity::new(ty, func);
let key = self.inner.new_import_key(module, name);
self.inner.insert(key, Definition::HostFunc(func))?;
Ok(self)
}
pub fn func_wrap<Params, Args>(
&mut self,
module: &str,
name: &str,
func: impl IntoFunc<T, Params, Args>,
) -> Result<&mut Self, LinkerError> {
let func = HostFuncTrampolineEntity::wrap(func);
let key = self.inner.new_import_key(module, name);
self.inner.insert(key, Definition::HostFunc(func))?;
Ok(self)
}
pub fn get(
&self,
context: impl AsContext<Data = T>,
module: &str,
name: &str,
) -> Option<Extern> {
match self.get_definition(context, module, name) {
Some(Definition::Extern(item)) => Some(*item),
_ => None,
}
}
fn get_definition(
&self,
context: impl AsContext<Data = T>,
module: &str,
name: &str,
) -> Option<&Definition<T>> {
assert!(Engine::same(
context.as_context().store.engine(),
self.engine()
));
self.inner.get_definition(module, name)
}
pub fn instance(
&mut self,
mut store: impl AsContextMut<Data = T>,
module_name: &str,
instance: Instance,
) -> Result<&mut Self, Error> {
assert!(Engine::same(
store.as_context().store.engine(),
self.engine()
));
let mut store = store.as_context_mut();
for export in instance.exports(&mut store) {
let key = self.inner.new_import_key(module_name, export.name());
let def = Definition::Extern(export.into_extern());
self.inner.insert(key, def)?;
}
Ok(self)
}
pub fn alias_module(&mut self, module: &str, as_module: &str) -> Result<(), Error> {
self.inner.alias_module(module, as_module)
}
pub fn instantiate_and_start(
&self,
mut context: impl AsContextMut<Data = T>,
module: &Module,
) -> Result<Instance, Error> {
assert!(Engine::same(self.engine(), context.as_context().engine()));
let externals = module
.imports()
.map(|import| self.process_import(&mut context, import))
.collect::<Result<Vec<Extern>, Error>>()?;
module.instantiate(context, externals)
}
fn process_import(
&self,
mut context: impl AsContextMut<Data = T>,
import: ImportType,
) -> Result<Extern, Error> {
assert!(Engine::same(self.engine(), context.as_context().engine()));
let module_name = import.module();
let field_name = import.name();
let resolved = self
.get_definition(context.as_context(), module_name, field_name)
.ok_or_else(|| LinkerError::missing_definition(&import))?;
let invalid_type =
|context| LinkerError::invalid_type_definition(&import, &resolved.ty(context));
match import.ty() {
ExternType::Func(_expected) => {
let func = resolved
.as_func(&mut context)
.ok_or_else(|| invalid_type(context))?;
Ok(Extern::Func(func))
}
ExternType::Table(_expected) => {
let table = resolved
.as_extern()
.copied()
.and_then(Extern::into_table)
.ok_or_else(|| invalid_type(context))?;
Ok(Extern::Table(table))
}
ExternType::Memory(_expected) => {
let memory = resolved
.as_extern()
.copied()
.and_then(Extern::into_memory)
.ok_or_else(|| invalid_type(context))?;
Ok(Extern::Memory(memory))
}
ExternType::Global(_expected) => {
let global = resolved
.as_extern()
.copied()
.and_then(Extern::into_global)
.ok_or_else(|| invalid_type(context))?;
Ok(Extern::Global(global))
}
}
}
}
#[derive(Debug)]
pub struct LinkerInner<T> {
strings: StringInterner,
definitions: BTreeMap<ImportKey, Definition<T>>,
allow_shadowing: bool,
}
impl<T> Default for LinkerInner<T> {
fn default() -> Self {
Self {
strings: StringInterner::default(),
definitions: BTreeMap::default(),
allow_shadowing: false,
}
}
}
impl<T> Clone for LinkerInner<T> {
fn clone(&self) -> Self {
Self {
strings: self.strings.clone(),
definitions: self.definitions.clone(),
allow_shadowing: self.allow_shadowing,
}
}
}
impl<T> LinkerInner<T> {
pub fn allow_shadowing(&mut self, allow: bool) {
self.allow_shadowing = allow;
}
fn new_import_key(&mut self, module: &str, name: &str) -> ImportKey {
ImportKey::new(
self.strings
.get_or_intern_with_hint(module, InternHint::LikelyExists),
self.strings
.get_or_intern_with_hint(name, InternHint::LikelyNew),
)
}
fn get_import_key(&self, module: &str, name: &str) -> Option<ImportKey> {
Some(ImportKey::new(
self.strings.get(module)?,
self.strings.get(name)?,
))
}
fn resolve_import_key(&self, key: ImportKey) -> Option<(&str, &str)> {
let module_name = self.strings.resolve(key.module())?;
let item_name = self.strings.resolve(key.name())?;
Some((module_name, item_name))
}
fn insert(&mut self, key: ImportKey, item: Definition<T>) -> Result<(), LinkerError> {
match self.definitions.entry(key) {
Entry::Occupied(mut entry) if self.allow_shadowing => {
entry.insert(item);
}
Entry::Occupied(_) => {
let (module_name, field_name) = self
.resolve_import_key(key)
.unwrap_or_else(|| panic!("encountered missing import names for key {key:?}"));
let import_name = ImportName::new(module_name, field_name);
return Err(LinkerError::DuplicateDefinition { import_name });
}
Entry::Vacant(v) => {
v.insert(item);
}
}
Ok(())
}
pub fn alias_module(&mut self, module: &str, as_module: &str) -> Result<(), Error> {
let module = self
.strings
.get_or_intern_with_hint(module, InternHint::LikelyExists);
let as_module = self
.strings
.get_or_intern_with_hint(as_module, InternHint::LikelyNew);
let items = self
.definitions
.iter()
.filter(|(key, _def)| key.module() == module)
.map(|(key, def)| (key.name(), def.clone()))
.collect::<Vec<_>>();
for (name, item) in items {
self.insert(ImportKey::new(as_module, name), item)?;
}
Ok(())
}
fn get_definition(&self, module: &str, name: &str) -> Option<&Definition<T>> {
let key = self.get_import_key(module, name)?;
self.definitions.get(&key)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Store, ValType};
struct HostState {
a: i32,
b: i64,
}
#[test]
fn linker_funcs_work() {
let engine = Engine::default();
let mut linker = <Linker<HostState>>::new(&engine);
linker
.func_new(
"host",
"get_a",
FuncType::new([], [ValType::I32]),
|ctx: Caller<HostState>, _params: &[Val], results: &mut [Val]| {
results[0] = Val::from(ctx.data().a);
Ok(())
},
)
.unwrap();
linker
.func_new(
"host",
"set_a",
FuncType::new([ValType::I32], []),
|mut ctx: Caller<HostState>, params: &[Val], _results: &mut [Val]| {
ctx.data_mut().a = params[0].i32().unwrap();
Ok(())
},
)
.unwrap();
linker
.func_wrap("host", "get_b", |ctx: Caller<HostState>| ctx.data().b)
.unwrap();
linker
.func_wrap("host", "set_b", |mut ctx: Caller<HostState>, value: i64| {
ctx.data_mut().b = value
})
.unwrap();
let a_init = 42;
let b_init = 77;
let mut store = <Store<HostState>>::new(
&engine,
HostState {
a: a_init,
b: b_init,
},
);
let wasm = r#"
(module
(import "host" "get_a" (func $host_get_a (result i32)))
(import "host" "set_a" (func $host_set_a (param i32)))
(import "host" "get_b" (func $host_get_b (result i64)))
(import "host" "set_b" (func $host_set_b (param i64)))
(func (export "wasm_get_a") (result i32)
(call $host_get_a)
)
(func (export "wasm_set_a") (param $param i32)
(call $host_set_a (local.get $param))
)
(func (export "wasm_get_b") (result i64)
(call $host_get_b)
)
(func (export "wasm_set_b") (param $param i64)
(call $host_set_b (local.get $param))
)
)
"#;
let module = Module::new(&engine, wasm).unwrap();
let instance = linker.instantiate_and_start(&mut store, &module).unwrap();
let wasm_get_a = instance
.get_typed_func::<(), i32>(&store, "wasm_get_a")
.unwrap();
let wasm_set_a = instance
.get_typed_func::<i32, ()>(&store, "wasm_set_a")
.unwrap();
let wasm_get_b = instance
.get_typed_func::<(), i64>(&store, "wasm_get_b")
.unwrap();
let wasm_set_b = instance
.get_typed_func::<i64, ()>(&store, "wasm_set_b")
.unwrap();
assert_eq!(wasm_get_a.call(&mut store, ()).unwrap(), a_init);
wasm_set_a.call(&mut store, 100).unwrap();
assert_eq!(wasm_get_a.call(&mut store, ()).unwrap(), 100);
assert_eq!(wasm_get_b.call(&mut store, ()).unwrap(), b_init);
wasm_set_b.call(&mut store, 200).unwrap();
assert_eq!(wasm_get_b.call(&mut store, ()).unwrap(), 200);
}
#[test]
fn populate_via_imports() {
use crate::{Engine, Func, Linker, Memory, MemoryType, Module, Store};
let wasm = r#"
(module
(import "host" "hello" (func $host_hello (param i32) (result i32)))
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result i32)
(call $host_hello (i32.const 3))
(i32.const 2)
i32.add
)
)"#;
let engine = Engine::default();
let mut linker = <Linker<()>>::new(&engine);
let mut store = Store::new(&engine, ());
let memory = Memory::new(&mut store, MemoryType::new(1, Some(4096))).unwrap();
let module = Module::new(&engine, wasm).unwrap();
linker.define("env", "memory", memory).unwrap();
let func = Func::new(
&mut store,
FuncType::new([ValType::I32], [ValType::I32]),
|_caller, _params, _results| todo!(),
);
linker.define("host", "hello", func).unwrap();
linker.instantiate_and_start(&mut store, &module).unwrap();
}
}