use crate::{ImportError, LinkError};
use more_asserts::assert_ge;
use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasmer_types::{ExternType, FunctionIndex, ImportIndex, MemoryIndex, TableIndex};
use wasmer_vm::{
Export, FunctionBodyPtr, Imports, MemoryStyle, ModuleInfo, TableStyle, VMFunctionBody,
VMFunctionImport, VMFunctionKind, VMGlobalImport, VMMemoryImport, VMTableImport,
};
pub trait Resolver {
fn resolve(&self, _index: u32, module: &str, field: &str) -> Option<Export>;
}
pub trait NamedResolver {
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export>;
}
impl<T: NamedResolver> Resolver for T {
fn resolve(&self, _index: u32, module: &str, field: &str) -> Option<Export> {
self.resolve_by_name(module, field)
}
}
impl<T: NamedResolver> NamedResolver for &T {
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
(**self).resolve_by_name(module, field)
}
}
impl NamedResolver for Box<dyn NamedResolver> {
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
(**self).resolve_by_name(module, field)
}
}
pub struct NullResolver {}
impl Resolver for NullResolver {
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
None
}
}
fn get_extern_from_import(module: &ModuleInfo, import_index: &ImportIndex) -> ExternType {
match import_index {
ImportIndex::Function(index) => {
let func = module.signatures[module.functions[*index]].clone();
ExternType::Function(func)
}
ImportIndex::Table(index) => {
let table = module.tables[*index];
ExternType::Table(table)
}
ImportIndex::Memory(index) => {
let memory = module.memories[*index];
ExternType::Memory(memory)
}
ImportIndex::Global(index) => {
let global = module.globals[*index];
ExternType::Global(global)
}
}
}
fn get_extern_from_export(_module: &ModuleInfo, export: &Export) -> ExternType {
match export {
Export::Function(ref f) => ExternType::Function(f.signature.clone()),
Export::Table(ref t) => ExternType::Table(t.ty().clone()),
Export::Memory(ref m) => ExternType::Memory(m.ty().clone()),
Export::Global(ref g) => {
let global = g.from.ty();
ExternType::Global(*global)
}
}
}
pub fn resolve_imports(
module: &ModuleInfo,
resolver: &dyn Resolver,
finished_dynamic_function_trampolines: &BoxedSlice<FunctionIndex, FunctionBodyPtr>,
memory_styles: &PrimaryMap<MemoryIndex, MemoryStyle>,
_table_styles: &PrimaryMap<TableIndex, TableStyle>,
) -> Result<Imports, LinkError> {
let mut function_imports = PrimaryMap::with_capacity(module.num_imported_functions);
let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables);
let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories);
let mut global_imports = PrimaryMap::with_capacity(module.num_imported_globals);
for ((module_name, field, import_idx), import_index) in module.imports.iter() {
let resolved = resolver.resolve(*import_idx, module_name, field);
let import_extern = get_extern_from_import(module, import_index);
let resolved = match resolved {
None => {
return Err(LinkError::Import(
module_name.to_string(),
field.to_string(),
ImportError::UnknownImport(import_extern),
));
}
Some(r) => r,
};
let export_extern = get_extern_from_export(module, &resolved);
if !export_extern.is_compatible_with(&import_extern) {
return Err(LinkError::Import(
module_name.to_string(),
field.to_string(),
ImportError::IncompatibleType(import_extern, export_extern),
));
}
match resolved {
Export::Function(ref f) => {
let address = match f.kind {
VMFunctionKind::Dynamic => {
let index = FunctionIndex::new(function_imports.len());
finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _
}
VMFunctionKind::Static => f.address,
};
function_imports.push(VMFunctionImport {
body: address,
vmctx: f.vmctx,
});
}
Export::Table(ref t) => {
table_imports.push(VMTableImport {
definition: t.from.vmtable(),
from: t.from.clone(),
});
}
Export::Memory(ref m) => {
match import_index {
ImportIndex::Memory(index) => {
let export_memory_style = m.style();
let import_memory_style = &memory_styles[*index];
if let (
MemoryStyle::Static { bound, .. },
MemoryStyle::Static {
bound: import_bound,
..
},
) = (export_memory_style.clone(), &import_memory_style)
{
assert_ge!(bound, *import_bound);
}
assert_ge!(
export_memory_style.offset_guard_size(),
import_memory_style.offset_guard_size()
);
}
_ => {
panic!("Memory resolution didn't matched");
}
}
memory_imports.push(VMMemoryImport {
definition: m.from.vmmemory(),
from: m.from.clone(),
});
}
Export::Global(ref g) => {
global_imports.push(VMGlobalImport {
definition: g.from.vmglobal(),
from: g.from.clone(),
});
}
}
}
Ok(Imports::new(
function_imports,
table_imports,
memory_imports,
global_imports,
))
}
pub struct NamedResolverChain<A: NamedResolver, B: NamedResolver> {
a: A,
b: B,
}
pub trait ChainableNamedResolver: NamedResolver + Sized {
fn chain_front<U>(self, other: U) -> NamedResolverChain<U, Self>
where
U: NamedResolver,
{
NamedResolverChain { a: other, b: self }
}
fn chain_back<U>(self, other: U) -> NamedResolverChain<Self, U>
where
U: NamedResolver,
{
NamedResolverChain { a: self, b: other }
}
}
impl<T: NamedResolver> ChainableNamedResolver for T {}
impl<A, B> NamedResolver for NamedResolverChain<A, B>
where
A: NamedResolver,
B: NamedResolver,
{
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
self.a
.resolve_by_name(module, field)
.or_else(|| self.b.resolve_by_name(module, field))
}
}
impl<A, B> Clone for NamedResolverChain<A, B>
where
A: NamedResolver + Clone,
B: NamedResolver + Clone,
{
fn clone(&self) -> Self {
Self {
a: self.a.clone(),
b: self.b.clone(),
}
}
}