use crate::{Export, ExportFunctionMetadata, 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::{
FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle, ModuleInfo, TableStyle,
VMFunctionBody, VMFunctionEnvironment, 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.vm_function.signature.clone()),
Export::Table(ref t) => ExternType::Table(*t.vm_table.ty()),
Export::Memory(ref m) => ExternType::Memory(*m.vm_memory.ty()),
Export::Global(ref g) => {
let global = g.vm_global.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 host_function_env_initializers =
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.vm_function.kind {
VMFunctionKind::Dynamic => {
let index = FunctionIndex::new(function_imports.len());
finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _
}
VMFunctionKind::Static => {
if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
let num_params = f.vm_function.signature.params().len();
assert!(num_params < 9, "Only native functions with less than 9 arguments are allowed in Apple Silicon (for now). Received {} in the import {}.{}", num_params, module_name, field);
}
f.vm_function.address
}
};
let env = if let Some(ExportFunctionMetadata {
host_env_clone_fn: clone,
..
}) = f.metadata.as_deref()
{
unsafe {
assert!(!f.vm_function.vmctx.host_env.is_null());
(clone)(f.vm_function.vmctx.host_env)
}
} else {
unsafe { f.vm_function.vmctx.host_env }
};
function_imports.push(VMFunctionImport {
body: address,
environment: VMFunctionEnvironment { host_env: env },
});
let initializer = f.metadata.as_ref().and_then(|m| m.import_init_function_ptr);
let clone = f.metadata.as_ref().map(|m| m.host_env_clone_fn);
let destructor = f.metadata.as_ref().map(|m| m.host_env_drop_fn);
let import_function_env =
if let (Some(clone), Some(destructor)) = (clone, destructor) {
ImportFunctionEnv::Env {
env,
clone,
initializer,
destructor,
}
} else {
ImportFunctionEnv::NoEnv
};
host_function_env_initializers.push(import_function_env);
}
Export::Table(ref t) => {
table_imports.push(VMTableImport {
definition: t.vm_table.from.vmtable(),
from: t.vm_table.from.clone(),
});
}
Export::Memory(ref m) => {
match import_index {
ImportIndex::Memory(index) => {
let export_memory_style = m.vm_memory.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.vm_memory.from.vmmemory(),
from: m.vm_memory.from.clone(),
});
}
Export::Global(ref g) => {
global_imports.push(VMGlobalImport {
definition: g.vm_global.from.vmglobal(),
from: g.vm_global.from.clone(),
});
}
}
}
Ok(Imports::new(
function_imports,
host_function_env_initializers,
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(),
}
}
}