wasmtime: Refactor how imports are resolved (#2102)
This commit removes all import resolution handling from the `wasmtime-jit` crate, instead moving the logic to the `wasmtime` crate. Previously `wasmtime-jit` had a generic `Resolver` trait and would do all the import type matching itself, but with the upcoming module-linking implementation this is going to get much trickier. The goal of this commit is to centralize all meaty "preparation" logic for instantiation into one location, probably the `wasmtime` crate itself. Instantiation will soon involve recursive instantiation and management of alias definitions as well. Having everything in one location, especially with access to `Store` so we can persist instances for safety, will be quite convenient. Additionally the `Resolver` trait isn't really necessary any more since imports are, at the lowest level, provided as a list rather than a map of some kind. More generic resolution functionality is provided via `Linker` or user layers on top of `Instance::new` itself. This makes matching up provided items to expected imports much easier as well. Overall this is largely just moving code around, but most of the code in the previous `resolve_imports` phase can be deleted since a lot of it is handled by surrounding pieces of `wasmtime` as well.
This commit is contained in:
@@ -1,263 +0,0 @@
|
|||||||
//! Module imports resolving logic.
|
|
||||||
|
|
||||||
use crate::resolver::Resolver;
|
|
||||||
use more_asserts::assert_ge;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
|
||||||
use wasmtime_environ::wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
|
||||||
use wasmtime_environ::{EntityIndex, MemoryPlan, MemoryStyle, Module, TablePlan};
|
|
||||||
use wasmtime_runtime::{
|
|
||||||
Export, Imports, LinkError, SignatureRegistry, VMFunctionImport, VMGlobalImport,
|
|
||||||
VMMemoryImport, VMTableImport,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This function allows to match all imports of a `Module` with concrete definitions provided by
|
|
||||||
/// a `Resolver`.
|
|
||||||
///
|
|
||||||
/// If all imports are satisfied returns an `Imports` instance required for a module instantiation.
|
|
||||||
pub fn resolve_imports(
|
|
||||||
module: &Module,
|
|
||||||
signatures: &SignatureRegistry,
|
|
||||||
resolver: &mut dyn Resolver,
|
|
||||||
) -> Result<Imports, LinkError> {
|
|
||||||
let mut function_imports = PrimaryMap::with_capacity(module.num_imported_funcs);
|
|
||||||
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 (import_idx, (module_name, field_name, import)) in module.imports.iter().enumerate() {
|
|
||||||
let import_idx = import_idx.try_into().unwrap();
|
|
||||||
let export = resolver.resolve(import_idx, module_name, field_name);
|
|
||||||
|
|
||||||
match (import, &export) {
|
|
||||||
(EntityIndex::Function(func_index), Some(Export::Function(f))) => {
|
|
||||||
let import_signature = module.native_func_signature(*func_index);
|
|
||||||
let signature = signatures
|
|
||||||
.lookup_native(unsafe { f.anyfunc.as_ref().type_index })
|
|
||||||
.unwrap();
|
|
||||||
if signature != *import_signature {
|
|
||||||
// TODO: If the difference is in the calling convention,
|
|
||||||
// we could emit a wrapper function to fix it up.
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: exported function with signature {} \
|
|
||||||
incompatible with function import with signature {}",
|
|
||||||
module_name, field_name, signature, import_signature
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
function_imports.push(VMFunctionImport {
|
|
||||||
body: unsafe { f.anyfunc.as_ref().func_ptr },
|
|
||||||
vmctx: unsafe { f.anyfunc.as_ref().vmctx },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(EntityIndex::Function(_), Some(_)) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: export incompatible with function import",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
(EntityIndex::Function(_), None) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: unknown import function: function not provided",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
(EntityIndex::Table(table_index), Some(Export::Table(t))) => {
|
|
||||||
let import_table = &module.table_plans[*table_index];
|
|
||||||
if !is_table_compatible(&t.table, import_table) {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: exported table incompatible with \
|
|
||||||
table import",
|
|
||||||
module_name, field_name,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
table_imports.push(VMTableImport {
|
|
||||||
from: t.definition,
|
|
||||||
vmctx: t.vmctx,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(EntityIndex::Table(_), Some(_)) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: export incompatible with table import",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
(EntityIndex::Table(_), None) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: unknown import table: table not provided",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
(EntityIndex::Memory(memory_index), Some(Export::Memory(m))) => {
|
|
||||||
let import_memory = &module.memory_plans[*memory_index];
|
|
||||||
if !is_memory_compatible(&m.memory, import_memory) {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: exported memory incompatible with \
|
|
||||||
memory import",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity-check: Ensure that the imported memory has at least
|
|
||||||
// guard-page protections the importing module expects it to have.
|
|
||||||
if let (
|
|
||||||
MemoryStyle::Static { bound },
|
|
||||||
MemoryStyle::Static {
|
|
||||||
bound: import_bound,
|
|
||||||
},
|
|
||||||
) = (&m.memory.style, &import_memory.style)
|
|
||||||
{
|
|
||||||
assert_ge!(*bound, *import_bound);
|
|
||||||
}
|
|
||||||
assert_ge!(m.memory.offset_guard_size, import_memory.offset_guard_size);
|
|
||||||
|
|
||||||
memory_imports.push(VMMemoryImport {
|
|
||||||
from: m.definition,
|
|
||||||
vmctx: m.vmctx,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(EntityIndex::Memory(_), Some(_)) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: export incompatible with memory import",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
(EntityIndex::Memory(_), None) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: unknown import memory: memory not provided",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
(EntityIndex::Global(global_index), Some(Export::Global(g))) => {
|
|
||||||
let imported_global = module.globals[*global_index];
|
|
||||||
if !is_global_compatible(&g.global, &imported_global) {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: exported global incompatible with \
|
|
||||||
global import",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
global_imports.push(VMGlobalImport { from: g.definition });
|
|
||||||
}
|
|
||||||
(EntityIndex::Global(_), Some(_)) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: incompatible import type: export incompatible with global import",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
(EntityIndex::Global(_), None) => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: unknown import global: global not provided",
|
|
||||||
module_name, field_name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Imports::new(
|
|
||||||
function_imports,
|
|
||||||
table_imports,
|
|
||||||
memory_imports,
|
|
||||||
global_imports,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
|
|
||||||
match imported.initializer {
|
|
||||||
GlobalInit::Import => (),
|
|
||||||
_ => panic!("imported Global should have an Imported initializer"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let Global {
|
|
||||||
wasm_ty: exported_wasm_ty,
|
|
||||||
ty: exported_ty,
|
|
||||||
mutability: exported_mutability,
|
|
||||||
initializer: _exported_initializer,
|
|
||||||
} = exported;
|
|
||||||
let Global {
|
|
||||||
wasm_ty: imported_wasm_ty,
|
|
||||||
ty: imported_ty,
|
|
||||||
mutability: imported_mutability,
|
|
||||||
initializer: _imported_initializer,
|
|
||||||
} = imported;
|
|
||||||
exported_wasm_ty == imported_wasm_ty
|
|
||||||
&& exported_ty == imported_ty
|
|
||||||
&& imported_mutability == exported_mutability
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_table_element_type_compatible(
|
|
||||||
exported_type: TableElementType,
|
|
||||||
imported_type: TableElementType,
|
|
||||||
) -> bool {
|
|
||||||
match exported_type {
|
|
||||||
TableElementType::Func => match imported_type {
|
|
||||||
TableElementType::Func => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
TableElementType::Val(exported_val_ty) => match imported_type {
|
|
||||||
TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool {
|
|
||||||
let TablePlan {
|
|
||||||
table:
|
|
||||||
Table {
|
|
||||||
wasm_ty: exported_wasm_ty,
|
|
||||||
ty: exported_ty,
|
|
||||||
minimum: exported_minimum,
|
|
||||||
maximum: exported_maximum,
|
|
||||||
},
|
|
||||||
style: _exported_style,
|
|
||||||
} = exported;
|
|
||||||
let TablePlan {
|
|
||||||
table:
|
|
||||||
Table {
|
|
||||||
wasm_ty: imported_wasm_ty,
|
|
||||||
ty: imported_ty,
|
|
||||||
minimum: imported_minimum,
|
|
||||||
maximum: imported_maximum,
|
|
||||||
},
|
|
||||||
style: _imported_style,
|
|
||||||
} = imported;
|
|
||||||
|
|
||||||
exported_wasm_ty == imported_wasm_ty
|
|
||||||
&& is_table_element_type_compatible(*exported_ty, *imported_ty)
|
|
||||||
&& imported_minimum <= exported_minimum
|
|
||||||
&& (imported_maximum.is_none()
|
|
||||||
|| (!exported_maximum.is_none()
|
|
||||||
&& imported_maximum.unwrap() >= exported_maximum.unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool {
|
|
||||||
let MemoryPlan {
|
|
||||||
memory:
|
|
||||||
Memory {
|
|
||||||
minimum: exported_minimum,
|
|
||||||
maximum: exported_maximum,
|
|
||||||
shared: exported_shared,
|
|
||||||
},
|
|
||||||
style: _exported_style,
|
|
||||||
offset_guard_size: _exported_offset_guard_size,
|
|
||||||
} = exported;
|
|
||||||
let MemoryPlan {
|
|
||||||
memory:
|
|
||||||
Memory {
|
|
||||||
minimum: imported_minimum,
|
|
||||||
maximum: imported_maximum,
|
|
||||||
shared: imported_shared,
|
|
||||||
},
|
|
||||||
style: _imported_style,
|
|
||||||
offset_guard_size: _imported_offset_guard_size,
|
|
||||||
} = imported;
|
|
||||||
|
|
||||||
imported_minimum <= exported_minimum
|
|
||||||
&& (imported_maximum.is_none()
|
|
||||||
|| (!exported_maximum.is_none()
|
|
||||||
&& imported_maximum.unwrap() >= exported_maximum.unwrap()))
|
|
||||||
&& exported_shared == imported_shared
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,8 @@
|
|||||||
|
|
||||||
use crate::code_memory::CodeMemory;
|
use crate::code_memory::CodeMemory;
|
||||||
use crate::compiler::{Compilation, Compiler};
|
use crate::compiler::{Compilation, Compiler};
|
||||||
use crate::imports::resolve_imports;
|
|
||||||
use crate::link::link_module;
|
use crate::link::link_module;
|
||||||
use crate::object::ObjectUnwindInfo;
|
use crate::object::ObjectUnwindInfo;
|
||||||
use crate::resolver::Resolver;
|
|
||||||
use object::File as ObjectFile;
|
use object::File as ObjectFile;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
@@ -24,10 +22,10 @@ use wasmtime_environ::{
|
|||||||
ModuleEnvironment, ModuleTranslation, StackMapInformation, TrapInformation,
|
ModuleEnvironment, ModuleTranslation, StackMapInformation, TrapInformation,
|
||||||
};
|
};
|
||||||
use wasmtime_profiling::ProfilingAgent;
|
use wasmtime_profiling::ProfilingAgent;
|
||||||
use wasmtime_runtime::VMInterrupts;
|
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
|
GdbJitImageRegistration, Imports, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
|
||||||
SignatureRegistry, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, VMTrampoline,
|
SignatureRegistry, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, VMInterrupts,
|
||||||
|
VMTrampoline,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An error condition while setting up a wasm instance, be it validation,
|
/// An error condition while setting up a wasm instance, be it validation,
|
||||||
@@ -245,7 +243,7 @@ impl CompiledModule {
|
|||||||
/// See `InstanceHandle::new`
|
/// See `InstanceHandle::new`
|
||||||
pub unsafe fn instantiate(
|
pub unsafe fn instantiate(
|
||||||
&self,
|
&self,
|
||||||
resolver: &mut dyn Resolver,
|
imports: Imports<'_>,
|
||||||
signature_registry: &mut SignatureRegistry,
|
signature_registry: &mut SignatureRegistry,
|
||||||
mem_creator: Option<&dyn RuntimeMemoryCreator>,
|
mem_creator: Option<&dyn RuntimeMemoryCreator>,
|
||||||
interrupts: Arc<VMInterrupts>,
|
interrupts: Arc<VMInterrupts>,
|
||||||
@@ -271,7 +269,6 @@ impl CompiledModule {
|
|||||||
|
|
||||||
let finished_functions = self.finished_functions.0.clone();
|
let finished_functions = self.finished_functions.0.clone();
|
||||||
|
|
||||||
let imports = resolve_imports(&self.module, signature_registry, resolver)?;
|
|
||||||
InstanceHandle::new(
|
InstanceHandle::new(
|
||||||
self.module.clone(),
|
self.module.clone(),
|
||||||
self.code.clone(),
|
self.code.clone(),
|
||||||
|
|||||||
@@ -23,11 +23,9 @@
|
|||||||
|
|
||||||
mod code_memory;
|
mod code_memory;
|
||||||
mod compiler;
|
mod compiler;
|
||||||
mod imports;
|
|
||||||
mod instantiate;
|
mod instantiate;
|
||||||
mod link;
|
mod link;
|
||||||
mod object;
|
mod object;
|
||||||
mod resolver;
|
|
||||||
mod unwind;
|
mod unwind;
|
||||||
|
|
||||||
pub mod native;
|
pub mod native;
|
||||||
@@ -37,7 +35,6 @@ pub use crate::code_memory::CodeMemory;
|
|||||||
pub use crate::compiler::{Compilation, CompilationStrategy, Compiler};
|
pub use crate::compiler::{Compilation, CompilationStrategy, Compiler};
|
||||||
pub use crate::instantiate::{CompilationArtifacts, CompiledModule, SetupError};
|
pub use crate::instantiate::{CompilationArtifacts, CompiledModule, SetupError};
|
||||||
pub use crate::link::link_module;
|
pub use crate::link::link_module;
|
||||||
pub use crate::resolver::{NullResolver, Resolver};
|
|
||||||
|
|
||||||
/// Version number of this crate.
|
/// Version number of this crate.
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
//! Define the `Resolver` trait, allowing custom resolution for external
|
|
||||||
//! references.
|
|
||||||
|
|
||||||
use wasmtime_runtime::Export;
|
|
||||||
|
|
||||||
/// Import resolver connects imports with available exported values.
|
|
||||||
pub trait Resolver {
|
|
||||||
/// Resolves an import a WebAssembly module to an export it's hooked up to.
|
|
||||||
///
|
|
||||||
/// The `index` provided is the index of the import in the wasm module
|
|
||||||
/// that's being resolved. For example 1 means that it's the second import
|
|
||||||
/// listed in the wasm module.
|
|
||||||
///
|
|
||||||
/// The `module` and `field` arguments provided are the module/field names
|
|
||||||
/// listed on the import itself.
|
|
||||||
fn resolve(&mut self, index: u32, module: &str, field: &str) -> Option<Export>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Resolver` implementation that always resolves to `None`.
|
|
||||||
pub struct NullResolver {}
|
|
||||||
|
|
||||||
impl Resolver for NullResolver {
|
|
||||||
fn resolve(&mut self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +1,22 @@
|
|||||||
use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
|
use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
|
||||||
use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
|
|
||||||
use wasmtime_environ::wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
|
|
||||||
|
|
||||||
/// Resolved import pointers.
|
/// Resolved import pointers.
|
||||||
#[derive(Clone)]
|
///
|
||||||
pub struct Imports {
|
/// Note that each of these fields are slices, not `PrimaryMap`. They should be
|
||||||
|
/// stored in index-order as with the module that we're providing the imports
|
||||||
|
/// for, and indexing is all done the same way as the main module's index
|
||||||
|
/// spaces.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct Imports<'a> {
|
||||||
/// Resolved addresses for imported functions.
|
/// Resolved addresses for imported functions.
|
||||||
pub functions: BoxedSlice<FuncIndex, VMFunctionImport>,
|
pub functions: &'a [VMFunctionImport],
|
||||||
|
|
||||||
/// Resolved addresses for imported tables.
|
/// Resolved addresses for imported tables.
|
||||||
pub tables: BoxedSlice<TableIndex, VMTableImport>,
|
pub tables: &'a [VMTableImport],
|
||||||
|
|
||||||
/// Resolved addresses for imported memories.
|
/// Resolved addresses for imported memories.
|
||||||
pub memories: BoxedSlice<MemoryIndex, VMMemoryImport>,
|
pub memories: &'a [VMMemoryImport],
|
||||||
|
|
||||||
/// Resolved addresses for imported globals.
|
/// Resolved addresses for imported globals.
|
||||||
pub globals: BoxedSlice<GlobalIndex, VMGlobalImport>,
|
pub globals: &'a [VMGlobalImport],
|
||||||
}
|
|
||||||
|
|
||||||
impl Imports {
|
|
||||||
/// Construct a new `Imports` instance.
|
|
||||||
pub fn new(
|
|
||||||
function_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
|
|
||||||
table_imports: PrimaryMap<TableIndex, VMTableImport>,
|
|
||||||
memory_imports: PrimaryMap<MemoryIndex, VMMemoryImport>,
|
|
||||||
global_imports: PrimaryMap<GlobalIndex, VMGlobalImport>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
functions: function_imports.into_boxed_slice(),
|
|
||||||
tables: table_imports.into_boxed_slice(),
|
|
||||||
memories: memory_imports.into_boxed_slice(),
|
|
||||||
globals: global_imports.into_boxed_slice(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a new `Imports` instance with no imports.
|
|
||||||
pub fn none() -> Self {
|
|
||||||
Self {
|
|
||||||
functions: PrimaryMap::new().into_boxed_slice(),
|
|
||||||
tables: PrimaryMap::new().into_boxed_slice(),
|
|
||||||
memories: PrimaryMap::new().into_boxed_slice(),
|
|
||||||
globals: PrimaryMap::new().into_boxed_slice(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -897,28 +897,36 @@ impl InstanceHandle {
|
|||||||
};
|
};
|
||||||
let instance = handle.instance();
|
let instance = handle.instance();
|
||||||
|
|
||||||
|
debug_assert_eq!(vmshared_signatures.len(), handle.module().signatures.len());
|
||||||
ptr::copy(
|
ptr::copy(
|
||||||
vmshared_signatures.values().as_slice().as_ptr(),
|
vmshared_signatures.values().as_slice().as_ptr(),
|
||||||
instance.signature_ids_ptr() as *mut VMSharedSignatureIndex,
|
instance.signature_ids_ptr() as *mut VMSharedSignatureIndex,
|
||||||
vmshared_signatures.len(),
|
vmshared_signatures.len(),
|
||||||
);
|
);
|
||||||
|
debug_assert_eq!(imports.functions.len(), handle.module().num_imported_funcs);
|
||||||
ptr::copy(
|
ptr::copy(
|
||||||
imports.functions.values().as_slice().as_ptr(),
|
imports.functions.as_ptr(),
|
||||||
instance.imported_functions_ptr() as *mut VMFunctionImport,
|
instance.imported_functions_ptr() as *mut VMFunctionImport,
|
||||||
imports.functions.len(),
|
imports.functions.len(),
|
||||||
);
|
);
|
||||||
|
debug_assert_eq!(imports.tables.len(), handle.module().num_imported_tables);
|
||||||
ptr::copy(
|
ptr::copy(
|
||||||
imports.tables.values().as_slice().as_ptr(),
|
imports.tables.as_ptr(),
|
||||||
instance.imported_tables_ptr() as *mut VMTableImport,
|
instance.imported_tables_ptr() as *mut VMTableImport,
|
||||||
imports.tables.len(),
|
imports.tables.len(),
|
||||||
);
|
);
|
||||||
|
debug_assert_eq!(
|
||||||
|
imports.memories.len(),
|
||||||
|
handle.module().num_imported_memories
|
||||||
|
);
|
||||||
ptr::copy(
|
ptr::copy(
|
||||||
imports.memories.values().as_slice().as_ptr(),
|
imports.memories.as_ptr(),
|
||||||
instance.imported_memories_ptr() as *mut VMMemoryImport,
|
instance.imported_memories_ptr() as *mut VMMemoryImport,
|
||||||
imports.memories.len(),
|
imports.memories.len(),
|
||||||
);
|
);
|
||||||
|
debug_assert_eq!(imports.globals.len(), handle.module().num_imported_globals);
|
||||||
ptr::copy(
|
ptr::copy(
|
||||||
imports.globals.values().as_slice().as_ptr(),
|
imports.globals.as_ptr(),
|
||||||
instance.imported_globals_ptr() as *mut VMGlobalImport,
|
instance.imported_globals_ptr() as *mut VMGlobalImport,
|
||||||
imports.globals.len(),
|
imports.globals.len(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ impl SignatureRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Looks up a shared index from the wasm signature itself.
|
||||||
|
pub fn lookup(&self, wasm: &WasmFuncType) -> Option<VMSharedSignatureIndex> {
|
||||||
|
self.wasm2index.get(wasm).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
/// Looks up a shared native signature within this registry.
|
/// Looks up a shared native signature within this registry.
|
||||||
///
|
///
|
||||||
/// Note that for this operation to be semantically correct the `idx` must
|
/// Note that for this operation to be semantically correct the `idx` must
|
||||||
|
|||||||
@@ -86,15 +86,6 @@ impl Extern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_wasmtime_export(&self) -> wasmtime_runtime::Export {
|
|
||||||
match self {
|
|
||||||
Extern::Func(f) => f.wasmtime_function().clone().into(),
|
|
||||||
Extern::Global(g) => g.wasmtime_export.clone().into(),
|
|
||||||
Extern::Memory(m) => m.wasmtime_export.clone().into(),
|
|
||||||
Extern::Table(t) => t.wasmtime_export.clone().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_wasmtime_export(
|
pub(crate) fn from_wasmtime_export(
|
||||||
wasmtime_export: wasmtime_runtime::Export,
|
wasmtime_export: wasmtime_runtime::Export,
|
||||||
instance: StoreInstanceHandle,
|
instance: StoreInstanceHandle,
|
||||||
@@ -124,6 +115,15 @@ impl Extern {
|
|||||||
};
|
};
|
||||||
Store::same(my_store, store)
|
Store::same(my_store, store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn desc(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Extern::Func(_) => "function",
|
||||||
|
Extern::Table(_) => "table",
|
||||||
|
Extern::Memory(_) => "memory",
|
||||||
|
Extern::Global(_) => "global",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Func> for Extern {
|
impl From<Func> for Extern {
|
||||||
@@ -293,6 +293,19 @@ impl Global {
|
|||||||
wasmtime_export,
|
wasmtime_export,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn matches_expected(&self, expected: &wasmtime_environ::wasm::Global) -> bool {
|
||||||
|
let actual = &self.wasmtime_export.global;
|
||||||
|
expected.ty == actual.ty
|
||||||
|
&& expected.wasm_ty == actual.wasm_ty
|
||||||
|
&& expected.mutability == actual.mutability
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMGlobalImport {
|
||||||
|
wasmtime_runtime::VMGlobalImport {
|
||||||
|
from: self.wasmtime_export.definition,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A WebAssembly `table`, or an array of values.
|
/// A WebAssembly `table`, or an array of values.
|
||||||
@@ -524,6 +537,28 @@ impl Table {
|
|||||||
wasmtime_export,
|
wasmtime_export,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::TablePlan) -> bool {
|
||||||
|
let expected = &ty.table;
|
||||||
|
let actual = &self.wasmtime_export.table.table;
|
||||||
|
expected.wasm_ty == actual.wasm_ty
|
||||||
|
&& expected.ty == actual.ty
|
||||||
|
&& expected.minimum <= actual.minimum
|
||||||
|
&& match expected.maximum {
|
||||||
|
Some(expected) => match actual.maximum {
|
||||||
|
Some(actual) => expected >= actual,
|
||||||
|
None => false,
|
||||||
|
},
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMTableImport {
|
||||||
|
wasmtime_runtime::VMTableImport {
|
||||||
|
from: self.wasmtime_export.definition,
|
||||||
|
vmctx: self.wasmtime_export.vmctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A WebAssembly linear memory.
|
/// A WebAssembly linear memory.
|
||||||
@@ -924,6 +959,27 @@ impl Memory {
|
|||||||
wasmtime_export,
|
wasmtime_export,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::MemoryPlan) -> bool {
|
||||||
|
let expected = &ty.memory;
|
||||||
|
let actual = &self.wasmtime_export.memory.memory;
|
||||||
|
expected.shared == actual.shared
|
||||||
|
&& expected.minimum <= actual.minimum
|
||||||
|
&& match expected.maximum {
|
||||||
|
Some(expected) => match actual.maximum {
|
||||||
|
Some(actual) => expected >= actual,
|
||||||
|
None => false,
|
||||||
|
},
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMMemoryImport {
|
||||||
|
wasmtime_runtime::VMMemoryImport {
|
||||||
|
from: self.wasmtime_export.definition,
|
||||||
|
vmctx: self.wasmtime_export.vmctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A linear memory. This trait provides an interface for raw memory buffers which are used
|
/// A linear memory. This trait provides an interface for raw memory buffers which are used
|
||||||
|
|||||||
@@ -635,10 +635,6 @@ impl Func {
|
|||||||
Ok(results.into())
|
Ok(results.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn wasmtime_function(&self) -> &wasmtime_runtime::ExportFunction {
|
|
||||||
&self.export
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn caller_checked_anyfunc(
|
pub(crate) fn caller_checked_anyfunc(
|
||||||
&self,
|
&self,
|
||||||
) -> NonNull<wasmtime_runtime::VMCallerCheckedAnyfunc> {
|
) -> NonNull<wasmtime_runtime::VMCallerCheckedAnyfunc> {
|
||||||
@@ -789,6 +785,20 @@ impl Func {
|
|||||||
pub fn store(&self) -> &Store {
|
pub fn store(&self) -> &Store {
|
||||||
&self.instance.store
|
&self.instance.store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn matches_expected(&self, expected: VMSharedSignatureIndex) -> bool {
|
||||||
|
self.sig_index() == expected
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMFunctionImport {
|
||||||
|
unsafe {
|
||||||
|
let f = self.caller_checked_anyfunc();
|
||||||
|
wasmtime_runtime::VMFunctionImport {
|
||||||
|
body: f.as_ref().func_ptr,
|
||||||
|
vmctx: f.as_ref().vmctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Func {
|
impl fmt::Debug for Func {
|
||||||
|
|||||||
@@ -1,53 +1,25 @@
|
|||||||
use crate::trampoline::StoreInstanceHandle;
|
use crate::trampoline::StoreInstanceHandle;
|
||||||
use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
|
use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
|
||||||
use anyhow::{bail, Error, Result};
|
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use wasmtime_environ::EntityIndex;
|
use wasmtime_environ::EntityIndex;
|
||||||
use wasmtime_jit::{CompiledModule, Resolver};
|
use wasmtime_jit::CompiledModule;
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable, VMFunctionBody,
|
Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable,
|
||||||
|
VMFunctionBody,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SimpleResolver<'a> {
|
|
||||||
imports: &'a [Extern],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolver for SimpleResolver<'_> {
|
|
||||||
fn resolve(&mut self, idx: u32, _name: &str, _field: &str) -> Option<wasmtime_runtime::Export> {
|
|
||||||
self.imports
|
|
||||||
.get(idx as usize)
|
|
||||||
.map(|i| i.get_wasmtime_export())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instantiate(
|
fn instantiate(
|
||||||
store: &Store,
|
store: &Store,
|
||||||
compiled_module: &CompiledModule,
|
compiled_module: &CompiledModule,
|
||||||
imports: &[Extern],
|
imports: Imports<'_>,
|
||||||
host: Box<dyn Any>,
|
host: Box<dyn Any>,
|
||||||
) -> Result<StoreInstanceHandle, Error> {
|
) -> Result<StoreInstanceHandle, Error> {
|
||||||
// For now we have a restriction that the `Store` that we're working
|
|
||||||
// with is the same for everything involved here.
|
|
||||||
for import in imports {
|
|
||||||
if !import.comes_from_same_store(store) {
|
|
||||||
bail!("cross-`Store` instantiation is not currently supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if imports.len() != compiled_module.module().imports.len() {
|
|
||||||
bail!(
|
|
||||||
"wrong number of imports provided, {} != {}",
|
|
||||||
imports.len(),
|
|
||||||
compiled_module.module().imports.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut resolver = SimpleResolver { imports };
|
|
||||||
let config = store.engine().config();
|
let config = store.engine().config();
|
||||||
let instance = unsafe {
|
let instance = unsafe {
|
||||||
let instance = compiled_module.instantiate(
|
let instance = compiled_module.instantiate(
|
||||||
&mut resolver,
|
imports,
|
||||||
&mut store.signatures_mut(),
|
&mut store.signatures_mut(),
|
||||||
config.memory_creator.as_ref().map(|a| a as _),
|
config.memory_creator.as_ref().map(|a| a as _),
|
||||||
store.interrupts().clone(),
|
store.interrupts().clone(),
|
||||||
@@ -196,7 +168,9 @@ impl Instance {
|
|||||||
frame_info_registration
|
frame_info_registration
|
||||||
});
|
});
|
||||||
|
|
||||||
let handle = instantiate(store, module.compiled_module(), imports, host_info)?;
|
let handle = with_imports(store, module.compiled_module(), imports, |imports| {
|
||||||
|
instantiate(store, module.compiled_module(), imports, host_info)
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Instance {
|
Ok(Instance {
|
||||||
handle,
|
handle,
|
||||||
@@ -267,3 +241,82 @@ impl Instance {
|
|||||||
self.get_export(name)?.into_global()
|
self.get_export(name)?.into_global()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_imports<R>(
|
||||||
|
store: &Store,
|
||||||
|
module: &CompiledModule,
|
||||||
|
externs: &[Extern],
|
||||||
|
f: impl FnOnce(Imports<'_>) -> Result<R>,
|
||||||
|
) -> Result<R> {
|
||||||
|
let m = module.module();
|
||||||
|
if externs.len() != m.imports.len() {
|
||||||
|
bail!(
|
||||||
|
"wrong number of imports provided, {} != {}",
|
||||||
|
externs.len(),
|
||||||
|
m.imports.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tables = Vec::new();
|
||||||
|
let mut functions = Vec::new();
|
||||||
|
let mut globals = Vec::new();
|
||||||
|
let mut memories = Vec::new();
|
||||||
|
|
||||||
|
let mut process = |expected: &EntityIndex, actual: &Extern| {
|
||||||
|
// For now we have a restriction that the `Store` that we're working
|
||||||
|
// with is the same for everything involved here.
|
||||||
|
if !actual.comes_from_same_store(store) {
|
||||||
|
bail!("cross-`Store` instantiation is not currently supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
match *expected {
|
||||||
|
EntityIndex::Table(i) => tables.push(match actual {
|
||||||
|
Extern::Table(e) if e.matches_expected(&m.table_plans[i]) => e.vmimport(),
|
||||||
|
Extern::Table(_) => bail!("table types incompatible"),
|
||||||
|
_ => bail!("expected table, but found {}", actual.desc()),
|
||||||
|
}),
|
||||||
|
EntityIndex::Memory(i) => memories.push(match actual {
|
||||||
|
Extern::Memory(e) if e.matches_expected(&m.memory_plans[i]) => e.vmimport(),
|
||||||
|
Extern::Memory(_) => bail!("memory types incompatible"),
|
||||||
|
_ => bail!("expected memory, but found {}", actual.desc()),
|
||||||
|
}),
|
||||||
|
EntityIndex::Global(i) => globals.push(match actual {
|
||||||
|
Extern::Global(e) if e.matches_expected(&m.globals[i]) => e.vmimport(),
|
||||||
|
Extern::Global(_) => bail!("global types incompatible"),
|
||||||
|
_ => bail!("expected global, but found {}", actual.desc()),
|
||||||
|
}),
|
||||||
|
EntityIndex::Function(i) => {
|
||||||
|
let func = match actual {
|
||||||
|
Extern::Func(e) => e,
|
||||||
|
_ => bail!("expected function, but found {}", actual.desc()),
|
||||||
|
};
|
||||||
|
// Look up the `i`th function's type from the module in our
|
||||||
|
// signature registry. If it's not present then we have no
|
||||||
|
// functions registered with that type, so `func` is guaranteed
|
||||||
|
// to not match.
|
||||||
|
let ty = store
|
||||||
|
.signatures_mut()
|
||||||
|
.lookup(&m.signatures[m.functions[i]].0)
|
||||||
|
.ok_or_else(|| anyhow!("function types incompatible"))?;
|
||||||
|
if !func.matches_expected(ty) {
|
||||||
|
bail!("function types incompatible");
|
||||||
|
}
|
||||||
|
functions.push(func.vmimport());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
for (expected, actual) in m.imports.iter().zip(externs) {
|
||||||
|
process(&expected.2, actual).with_context(|| {
|
||||||
|
format!("incompatible import type for {}/{}", expected.0, expected.1)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return f(Imports {
|
||||||
|
tables: &tables,
|
||||||
|
functions: &functions,
|
||||||
|
globals: &globals,
|
||||||
|
memories: &memories,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::any::Any;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
use wasmtime_environ::entity::PrimaryMap;
|
||||||
use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex};
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
use wasmtime_environ::Module;
|
use wasmtime_environ::Module;
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
|
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
|
||||||
@@ -20,14 +20,10 @@ pub(crate) fn create_handle(
|
|||||||
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||||
state: Box<dyn Any>,
|
state: Box<dyn Any>,
|
||||||
func_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
|
func_imports: &[VMFunctionImport],
|
||||||
) -> Result<StoreInstanceHandle> {
|
) -> Result<StoreInstanceHandle> {
|
||||||
let imports = Imports::new(
|
let mut imports = Imports::default();
|
||||||
func_imports,
|
imports.functions = func_imports;
|
||||||
PrimaryMap::new(),
|
|
||||||
PrimaryMap::new(),
|
|
||||||
PrimaryMap::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compute indices into the shared signature table.
|
// Compute indices into the shared signature table.
|
||||||
let signatures = module
|
let signatures = module
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ pub fn create_handle_with_function(
|
|||||||
finished_functions,
|
finished_functions,
|
||||||
trampolines,
|
trampolines,
|
||||||
Box::new(trampoline_state),
|
Box::new(trampoline_state),
|
||||||
PrimaryMap::new(),
|
&[],
|
||||||
)
|
)
|
||||||
.map(|instance| (instance, trampoline))
|
.map(|instance| (instance, trampoline))
|
||||||
}
|
}
|
||||||
@@ -298,12 +298,5 @@ pub unsafe fn create_handle_with_raw_function(
|
|||||||
let sig_id = store.register_signature(ft.to_wasm_func_type(), sig);
|
let sig_id = store.register_signature(ft.to_wasm_func_type(), sig);
|
||||||
trampolines.insert(sig_id, trampoline);
|
trampolines.insert(sig_id, trampoline);
|
||||||
|
|
||||||
create_handle(
|
create_handle(module, store, finished_functions, trampolines, state, &[])
|
||||||
module,
|
|
||||||
store,
|
|
||||||
finished_functions,
|
|
||||||
trampolines,
|
|
||||||
state,
|
|
||||||
PrimaryMap::new(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use wasmtime_runtime::VMFunctionImport;
|
|||||||
|
|
||||||
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
|
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
let mut func_imports = PrimaryMap::new();
|
let mut func_imports = Vec::new();
|
||||||
let mut externref_init = None;
|
let mut externref_init = None;
|
||||||
|
|
||||||
let global = wasm::Global {
|
let global = wasm::Global {
|
||||||
@@ -68,7 +68,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
|
|||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Box::new(()),
|
Box::new(()),
|
||||||
func_imports,
|
&func_imports,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(x) = externref_init {
|
if let Some(x) = externref_init {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub fn create_handle_with_memory(
|
|||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Box::new(()),
|
Box::new(()),
|
||||||
PrimaryMap::new(),
|
&[],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,6 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Stor
|
|||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Box::new(()),
|
Box::new(()),
|
||||||
PrimaryMap::new(),
|
&[],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user