use crate::{Engine, ImportError, LinkError};
use more_asserts::assert_ge;
use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasmer_types::{ExternType, FunctionIndex, ImportCounts, MemoryType, TableType};
use wasmer_vm::{
Export, ExportFunctionMetadata, FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle,
Resolver, VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind,
VMGlobalImport, VMImport, VMImportType, VMMemoryImport, VMTableImport,
};
fn is_compatible_table(ex: &TableType, im: &TableType) -> bool {
(ex.ty == wasmer_types::Type::FuncRef || ex.ty == im.ty)
&& im.minimum <= ex.minimum
&& (im.maximum.is_none()
|| (!ex.maximum.is_none() && im.maximum.unwrap() >= ex.maximum.unwrap()))
}
fn is_compatible_memory(ex: &MemoryType, im: &MemoryType) -> bool {
im.minimum <= ex.minimum
&& (im.maximum.is_none()
|| (!ex.maximum.is_none() && im.maximum.unwrap() >= ex.maximum.unwrap()))
&& ex.shared == im.shared
}
pub fn resolve_imports(
engine: &dyn Engine,
resolver: &dyn Resolver,
import_counts: &ImportCounts,
imports: &[VMImport],
finished_dynamic_function_trampolines: &BoxedSlice<FunctionIndex, FunctionBodyPtr>,
) -> Result<Imports, LinkError> {
let mut function_imports = PrimaryMap::with_capacity(import_counts.functions as _);
let mut host_function_env_initializers =
PrimaryMap::with_capacity(import_counts.functions as _);
let mut table_imports = PrimaryMap::with_capacity(import_counts.tables as _);
let mut memory_imports = PrimaryMap::with_capacity(import_counts.memories as _);
let mut global_imports = PrimaryMap::with_capacity(import_counts.globals as _);
for VMImport {
import_no,
module,
field,
ty,
} in imports
{
let resolved = resolver.resolve(*import_no, module, field);
let import_extern = || match ty {
&VMImportType::Table(t) => ExternType::Table(t),
&VMImportType::Memory(t, _) => ExternType::Memory(t),
&VMImportType::Global(t) => ExternType::Global(t),
&VMImportType::Function {
sig,
static_trampoline: _,
} => ExternType::Function(
engine
.lookup_signature(sig)
.expect("VMSharedSignatureIndex is not valid?"),
),
};
let resolved = match resolved {
Some(r) => r,
None => {
return Err(LinkError::Import(
module.to_string(),
field.to_string(),
ImportError::UnknownImport(import_extern()),
));
}
};
let export_extern = || match resolved {
Export::Function(ref f) => ExternType::Function(
engine
.lookup_signature(f.vm_function.signature)
.expect("VMSharedSignatureIndex not registered with engine (wrong engine?)")
.clone(),
),
Export::Table(ref t) => ExternType::Table(*t.ty()),
Export::Memory(ref m) => ExternType::Memory(m.ty()),
Export::Global(ref g) => {
let global = g.from.ty();
ExternType::Global(*global)
}
};
match (&resolved, ty) {
(
Export::Function(ex),
VMImportType::Function {
sig,
static_trampoline,
},
) if ex.vm_function.signature == *sig => {
let address = match ex.vm_function.kind {
VMFunctionKind::Dynamic => {
let index = FunctionIndex::new(function_imports.len());
finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _
}
VMFunctionKind::Static => ex.vm_function.address,
};
let env = if let Some(ExportFunctionMetadata {
host_env_clone_fn: clone,
..
}) = ex.metadata.as_deref()
{
unsafe {
assert!(!ex.vm_function.vmctx.host_env.is_null());
(clone)(ex.vm_function.vmctx.host_env)
}
} else {
unsafe { ex.vm_function.vmctx.host_env }
};
let trampoline = if let Some(t) = ex.vm_function.call_trampoline {
Some(t)
} else if let VMFunctionKind::Static = ex.vm_function.kind {
Some(*static_trampoline)
} else {
None
};
function_imports.push(VMFunctionImport {
body: FunctionBodyPtr(address),
signature: *sig,
environment: VMFunctionEnvironment { host_env: env },
trampoline,
});
let initializer = ex
.metadata
.as_ref()
.and_then(|m| m.import_init_function_ptr);
let clone = ex.metadata.as_ref().map(|m| m.host_env_clone_fn);
let destructor = ex.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(ex), VMImportType::Table(im)) if is_compatible_table(ex.ty(), im) => {
let import_table_ty = ex.from.ty();
if import_table_ty.ty != im.ty {
return Err(LinkError::Import(
module.to_string(),
field.to_string(),
ImportError::IncompatibleType(import_extern(), export_extern()),
));
}
table_imports.push(VMTableImport {
definition: ex.from.vmtable(),
from: ex.from.clone(),
});
}
(Export::Memory(ex), VMImportType::Memory(im, import_memory_style))
if is_compatible_memory(&ex.ty(), im) =>
{
let export_memory_style = ex.style();
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()
);
memory_imports.push(VMMemoryImport {
definition: ex.from.vmmemory(),
from: ex.from.clone(),
});
}
(Export::Global(ex), VMImportType::Global(im)) if ex.from.ty() == im => {
global_imports.push(VMGlobalImport {
definition: ex.from.vmglobal(),
from: ex.from.clone(),
});
}
_ => {
return Err(LinkError::Import(
module.to_string(),
field.to_string(),
ImportError::IncompatibleType(import_extern(), export_extern()),
));
}
}
}
Ok(Imports::new(
function_imports,
host_function_env_initializers,
table_imports,
memory_imports,
global_imports,
))
}