Add initial support for fused adapter trampolines (#4501)
* Add initial support for fused adapter trampolines This commit lands a significant new piece of functionality to Wasmtime's implementation of the component model in the form of the implementation of fused adapter trampolines. Internally within a component core wasm modules can communicate with each other by having their exports `canon lift`'d to get `canon lower`'d into a different component. This signifies that two components are communicating through a statically known interface via the canonical ABI at this time. Previously Wasmtime was able to identify that this communication was happening but it simply panicked with `unimplemented!` upon seeing it. This commit is the beginning of filling out this panic location with an actual implementation. The implementation route chosen here for fused adapters is to use a WebAssembly module itself for the implementation. This means that, at compile time of a component, Wasmtime is generating core WebAssembly modules which then get recursively compiled within Wasmtime as well. The choice to use WebAssembly itself as the implementation of fused adapters stems from a few motivations: * This does not represent a significant increase in the "trusted compiler base" of Wasmtime. Getting the Wasm -> CLIF translation correct once is hard enough much less for an entirely different IR to CLIF. By generating WebAssembly no new interactions with Cranelift are added which drastically reduces the possibilities for mistakes. * Using WebAssembly means that component adapters are insulated from miscompilations and mistakes. If something goes wrong it's defined well within the WebAssembly specification how it goes wrong and what happens as a result. This means that the "blast zone" for a wrong adapter is the component instance but not the entire host itself. Accesses to linear memory are guaranteed to be in-bounds and otherwise handled via well-defined traps. * A fully-finished fused adapter compiler is expected to be a significant and quite complex component of Wasmtime. Functionality along these lines is expected to be needed for Web-based polyfills of the component model and by using core WebAssembly it provides the opportunity to share code between Wasmtime and these polyfills for the component model. * Finally the runtime implementation of managing WebAssembly modules is already implemented and quite easy to integrate with, so representing fused adapters with WebAssembly results in very little extra support necessary for the runtime implementation of instantiating and managing a component. The compiler added in this commit is dubbed Wasmtime's Fused Adapter Compiler of Trampolines (FACT) because who doesn't like deriving a name from an acronym. Currently the trampoline compiler is limited in its support for interface types and only supports a few primitives. I plan on filing future PRs to flesh out the support here for all the variants of `InterfaceType`. For now this PR is primarily focused on all of the other infrastructure for the addition of a trampoline compiler. With the choice to use core WebAssembly to implement fused adapters it means that adapters need to be inserted into a module. Unfortunately adapters cannot all go into a single WebAssembly module because adapters themselves have dependencies which may be provided transitively through instances that were instantiated with other adapters. This means that a significant chunk of this PR (`adapt.rs`) is dedicated to determining precisely which adapters go into precisely which adapter modules. This partitioning process attempts to make large modules wherever it can to cut down on core wasm instantiations but is likely not optimal as it's just a simple heuristic today. With all of this added together it's now possible to start writing `*.wast` tests that internally have adapted modules communicating with one another. A `fused.wast` test suite was added as part of this PR which is the beginning of tests for the support of the fused adapter compiler added in this PR. Currently this is primarily testing some various topologies of adapters along with direct/indirect modes. This will grow many more tests over time as more types are supported. Overall I'm not 100% satisfied with the testing story of this PR. When a test fails it's very difficult to debug since everything is written in the text format of WebAssembly meaning there's no "conveniences" to print out the state of the world when things go wrong and easily debug. I think this will become even more apparent as more tests are written for more types in subsequent PRs. At this time though I know of no better alternative other than leaning pretty heavily on fuzz-testing to ensure this is all exercised. * Fix an unused field warning * Fix tests in `wasmtime-runtime` * Add some more tests for compiled trampolines * Remap exports when injecting adapters The exports of a component were accidentally left unmapped which meant that they indexed the instance indexes pre-adapter module insertion. * Fix typo * Rebase conflicts
This commit is contained in:
@@ -13,7 +13,7 @@ use wasmtime_environ::component::{
|
||||
AlwaysTrapInfo, ComponentTypes, GlobalInitializer, LoweredIndex, LoweringInfo,
|
||||
RuntimeAlwaysTrapIndex, StaticModuleIndex, Translator,
|
||||
};
|
||||
use wasmtime_environ::{PrimaryMap, SignatureIndex, Trampoline, TrapCode};
|
||||
use wasmtime_environ::{PrimaryMap, ScopeVec, SignatureIndex, Trampoline, TrapCode};
|
||||
use wasmtime_jit::CodeMemory;
|
||||
use wasmtime_runtime::VMFunctionBody;
|
||||
|
||||
@@ -119,10 +119,11 @@ impl Component {
|
||||
|
||||
let tunables = &engine.config().tunables;
|
||||
|
||||
let scope = ScopeVec::new();
|
||||
let mut validator =
|
||||
wasmparser::Validator::new_with_features(engine.config().features.clone());
|
||||
let mut types = Default::default();
|
||||
let (component, modules) = Translator::new(tunables, &mut validator, &mut types)
|
||||
let (component, modules) = Translator::new(tunables, &mut validator, &mut types, &scope)
|
||||
.translate(binary)
|
||||
.context("failed to parse WebAssembly module")?;
|
||||
let types = Arc::new(types.finish());
|
||||
|
||||
@@ -9,12 +9,10 @@ use std::ptr::NonNull;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::component::{
|
||||
CanonicalOptions, ComponentTypes, CoreDef, RuntimeComponentInstanceIndex, TypeFuncIndex,
|
||||
MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
||||
};
|
||||
use wasmtime_runtime::{Export, ExportFunction, VMTrampoline};
|
||||
|
||||
const MAX_STACK_PARAMS: usize = 16;
|
||||
const MAX_STACK_RESULTS: usize = 1;
|
||||
|
||||
/// A helper macro to safely map `MaybeUninit<T>` to `MaybeUninit<U>` where `U`
|
||||
/// is a field projection within `T`.
|
||||
///
|
||||
@@ -305,20 +303,20 @@ impl Func {
|
||||
self.call_raw(
|
||||
store,
|
||||
args,
|
||||
|store, options, args, dst: &mut MaybeUninit<[ValRaw; MAX_STACK_PARAMS]>| {
|
||||
if param_count > MAX_STACK_PARAMS {
|
||||
|store, options, args, dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>| {
|
||||
if param_count > MAX_FLAT_PARAMS {
|
||||
self.store_args(store, &options, ¶ms, args, dst)
|
||||
} else {
|
||||
dst.write([ValRaw::u64(0); MAX_STACK_PARAMS]);
|
||||
dst.write([ValRaw::u64(0); MAX_FLAT_PARAMS]);
|
||||
let dst = unsafe {
|
||||
mem::transmute::<_, &mut [MaybeUninit<ValRaw>; MAX_STACK_PARAMS]>(dst)
|
||||
mem::transmute::<_, &mut [MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>(dst)
|
||||
};
|
||||
args.iter()
|
||||
.try_for_each(|arg| arg.lower(store, &options, &mut dst.iter_mut()))
|
||||
}
|
||||
},
|
||||
|store, options, src: &[ValRaw; MAX_STACK_RESULTS]| {
|
||||
if result_count > MAX_STACK_RESULTS {
|
||||
|store, options, src: &[ValRaw; MAX_FLAT_RESULTS]| {
|
||||
if result_count > MAX_FLAT_RESULTS {
|
||||
Self::load_result(&Memory::new(store, &options), &result, &mut src.iter())
|
||||
} else {
|
||||
Val::lift(&result, store, &options, &mut src.iter())
|
||||
@@ -378,7 +376,7 @@ impl Func {
|
||||
assert!(mem::align_of_val(map_maybe_uninit!(space.ret)) == val_align);
|
||||
|
||||
let instance = store.0[instance.0].as_ref().unwrap().instance();
|
||||
let flags = instance.flags(component_instance);
|
||||
let mut flags = instance.instance_flags(component_instance);
|
||||
|
||||
unsafe {
|
||||
// Test the "may enter" flag which is a "lock" on this instance.
|
||||
@@ -388,15 +386,15 @@ impl Func {
|
||||
// from this point on the instance is considered "poisoned" and can
|
||||
// never be entered again. The only time this flag is set to `true`
|
||||
// again is after post-return logic has completed successfully.
|
||||
if !(*flags).may_enter() {
|
||||
if !flags.may_enter() {
|
||||
bail!("cannot reenter component instance");
|
||||
}
|
||||
(*flags).set_may_enter(false);
|
||||
flags.set_may_enter(false);
|
||||
|
||||
debug_assert!((*flags).may_leave());
|
||||
(*flags).set_may_leave(false);
|
||||
debug_assert!(flags.may_leave());
|
||||
flags.set_may_leave(false);
|
||||
let result = lower(store, &options, params, map_maybe_uninit!(space.params));
|
||||
(*flags).set_may_leave(true);
|
||||
flags.set_may_leave(true);
|
||||
result?;
|
||||
|
||||
// This is unsafe as we are providing the guarantee that all the
|
||||
@@ -430,7 +428,7 @@ impl Func {
|
||||
// is currently required to be 0 or 1 values according to the
|
||||
// canonical ABI, is saved within the `Store`'s `FuncData`. This'll
|
||||
// later get used in post-return.
|
||||
(*flags).set_needs_post_return(true);
|
||||
flags.set_needs_post_return(true);
|
||||
let val = lift(store.0, &options, ret)?;
|
||||
let ret_slice = cast_storage(ret);
|
||||
let data = &mut store.0[self.0];
|
||||
@@ -488,7 +486,7 @@ impl Func {
|
||||
let component_instance = data.component_instance;
|
||||
let post_return_arg = data.post_return_arg.take();
|
||||
let instance = store.0[instance.0].as_ref().unwrap().instance();
|
||||
let flags = instance.flags(component_instance);
|
||||
let mut flags = instance.instance_flags(component_instance);
|
||||
|
||||
unsafe {
|
||||
// First assert that the instance is in a "needs post return" state.
|
||||
@@ -508,18 +506,18 @@ impl Func {
|
||||
// `post_return` on the right function despite the call being a
|
||||
// separate step in the API.
|
||||
assert!(
|
||||
(*flags).needs_post_return(),
|
||||
flags.needs_post_return(),
|
||||
"post_return can only be called after a function has previously been called",
|
||||
);
|
||||
let post_return_arg = post_return_arg.expect("calling post_return on wrong function");
|
||||
|
||||
// This is a sanity-check assert which shouldn't ever trip.
|
||||
assert!(!(*flags).may_enter());
|
||||
assert!(!flags.may_enter());
|
||||
|
||||
// Unset the "needs post return" flag now that post-return is being
|
||||
// processed. This will cause future invocations of this method to
|
||||
// panic, even if the function call below traps.
|
||||
(*flags).set_needs_post_return(false);
|
||||
flags.set_needs_post_return(false);
|
||||
|
||||
// If the function actually had a `post-return` configured in its
|
||||
// canonical options that's executed here.
|
||||
@@ -539,7 +537,7 @@ impl Func {
|
||||
// And finally if everything completed successfully then the "may
|
||||
// enter" flag is set to `true` again here which enables further use
|
||||
// of the component.
|
||||
(*flags).set_may_enter(true);
|
||||
flags.set_may_enter(true);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -550,7 +548,7 @@ impl Func {
|
||||
options: &Options,
|
||||
params: &[Type],
|
||||
args: &[Val],
|
||||
dst: &mut MaybeUninit<[ValRaw; MAX_STACK_PARAMS]>,
|
||||
dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>,
|
||||
) -> Result<()> {
|
||||
let mut size = 0;
|
||||
let mut alignment = 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::component::func::{Memory, MemoryMut, Options, MAX_STACK_PARAMS, MAX_STACK_RESULTS};
|
||||
use crate::component::func::{Memory, MemoryMut, Options};
|
||||
use crate::component::{ComponentParams, ComponentType, Lift, Lower};
|
||||
use crate::{AsContextMut, StoreContextMut, ValRaw};
|
||||
use anyhow::{bail, Context, Result};
|
||||
@@ -7,9 +7,11 @@ use std::mem::MaybeUninit;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::component::{ComponentTypes, StringEncoding, TypeFuncIndex};
|
||||
use wasmtime_environ::component::{
|
||||
ComponentTypes, StringEncoding, TypeFuncIndex, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
||||
};
|
||||
use wasmtime_runtime::component::{
|
||||
VMComponentContext, VMComponentFlags, VMLowering, VMLoweringCallee,
|
||||
InstanceFlags, VMComponentContext, VMLowering, VMLoweringCallee,
|
||||
};
|
||||
use wasmtime_runtime::{VMCallerCheckedAnyfunc, VMMemoryDefinition, VMOpaqueContext};
|
||||
|
||||
@@ -27,7 +29,7 @@ pub trait IntoComponentFunc<T, Params, Return> {
|
||||
extern "C" fn entrypoint(
|
||||
cx: *mut VMOpaqueContext,
|
||||
data: *mut u8,
|
||||
flags: *mut VMComponentFlags,
|
||||
flags: InstanceFlags,
|
||||
memory: *mut VMMemoryDefinition,
|
||||
realloc: *mut VMCallerCheckedAnyfunc,
|
||||
string_encoding: StringEncoding,
|
||||
@@ -106,7 +108,7 @@ where
|
||||
/// the select few places it's intended to be called from.
|
||||
unsafe fn call_host<T, Params, Return, F>(
|
||||
cx: *mut VMOpaqueContext,
|
||||
flags: *mut VMComponentFlags,
|
||||
mut flags: InstanceFlags,
|
||||
memory: *mut VMMemoryDefinition,
|
||||
realloc: *mut VMCallerCheckedAnyfunc,
|
||||
string_encoding: StringEncoding,
|
||||
@@ -150,7 +152,7 @@ where
|
||||
// Perform a dynamic check that this instance can indeed be left. Exiting
|
||||
// the component is disallowed, for example, when the `realloc` function
|
||||
// calls a canonical import.
|
||||
if !(*flags).may_leave() {
|
||||
if !flags.may_leave() {
|
||||
bail!("cannot leave component instance");
|
||||
}
|
||||
|
||||
@@ -164,12 +166,12 @@ where
|
||||
// trivially DCE'd by LLVM. Perhaps one day with enough const programming in
|
||||
// Rust we can make monomorphizations of this function codegen only one
|
||||
// branch, but today is not that day.
|
||||
if Params::flatten_count() <= MAX_STACK_PARAMS {
|
||||
if Return::flatten_count() <= MAX_STACK_RESULTS {
|
||||
if Params::flatten_count() <= MAX_FLAT_PARAMS {
|
||||
if Return::flatten_count() <= MAX_FLAT_RESULTS {
|
||||
let storage = cast_storage::<ReturnStack<Params::Lower, Return::Lower>>(storage);
|
||||
let params = Params::lift(cx.0, &options, &storage.assume_init_ref().args)?;
|
||||
let ret = closure(cx.as_context_mut(), params)?;
|
||||
(*flags).set_may_leave(false);
|
||||
flags.set_may_leave(false);
|
||||
ret.lower(&mut cx, &options, map_maybe_uninit!(storage.ret))?;
|
||||
} else {
|
||||
let storage = cast_storage::<ReturnPointer<Params::Lower>>(storage).assume_init_ref();
|
||||
@@ -177,18 +179,18 @@ where
|
||||
let ret = closure(cx.as_context_mut(), params)?;
|
||||
let mut memory = MemoryMut::new(cx.as_context_mut(), &options);
|
||||
let ptr = validate_inbounds::<Return>(memory.as_slice_mut(), &storage.retptr)?;
|
||||
(*flags).set_may_leave(false);
|
||||
flags.set_may_leave(false);
|
||||
ret.store(&mut memory, ptr)?;
|
||||
}
|
||||
} else {
|
||||
let memory = Memory::new(cx.0, &options);
|
||||
if Return::flatten_count() <= MAX_STACK_RESULTS {
|
||||
if Return::flatten_count() <= MAX_FLAT_RESULTS {
|
||||
let storage = cast_storage::<ReturnStack<ValRaw, Return::Lower>>(storage);
|
||||
let ptr =
|
||||
validate_inbounds::<Params>(memory.as_slice(), &storage.assume_init_ref().args)?;
|
||||
let params = Params::load(&memory, &memory.as_slice()[ptr..][..Params::SIZE32])?;
|
||||
let ret = closure(cx.as_context_mut(), params)?;
|
||||
(*flags).set_may_leave(false);
|
||||
flags.set_may_leave(false);
|
||||
ret.lower(&mut cx, &options, map_maybe_uninit!(storage.ret))?;
|
||||
} else {
|
||||
let storage = cast_storage::<ReturnPointer<ValRaw>>(storage).assume_init_ref();
|
||||
@@ -197,12 +199,12 @@ where
|
||||
let ret = closure(cx.as_context_mut(), params)?;
|
||||
let mut memory = MemoryMut::new(cx.as_context_mut(), &options);
|
||||
let ptr = validate_inbounds::<Return>(memory.as_slice_mut(), &storage.retptr)?;
|
||||
(*flags).set_may_leave(false);
|
||||
flags.set_may_leave(false);
|
||||
ret.store(&mut memory, ptr)?;
|
||||
}
|
||||
}
|
||||
|
||||
(*flags).set_may_leave(true);
|
||||
flags.set_may_leave(true);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
@@ -260,7 +262,7 @@ macro_rules! impl_into_component_func {
|
||||
extern "C" fn entrypoint(
|
||||
cx: *mut VMOpaqueContext,
|
||||
data: *mut u8,
|
||||
flags: *mut VMComponentFlags,
|
||||
flags: InstanceFlags,
|
||||
memory: *mut VMMemoryDefinition,
|
||||
realloc: *mut VMCallerCheckedAnyfunc,
|
||||
string_encoding: StringEncoding,
|
||||
@@ -298,7 +300,7 @@ macro_rules! impl_into_component_func {
|
||||
extern "C" fn entrypoint(
|
||||
cx: *mut VMOpaqueContext,
|
||||
data: *mut u8,
|
||||
flags: *mut VMComponentFlags,
|
||||
flags: InstanceFlags,
|
||||
memory: *mut VMMemoryDefinition,
|
||||
realloc: *mut VMCallerCheckedAnyfunc,
|
||||
string_encoding: StringEncoding,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::component::func::{
|
||||
Func, Memory, MemoryMut, Options, MAX_STACK_PARAMS, MAX_STACK_RESULTS,
|
||||
};
|
||||
use crate::component::func::{Func, Memory, MemoryMut, Options};
|
||||
use crate::store::StoreOpaque;
|
||||
use crate::{AsContext, AsContextMut, StoreContext, StoreContextMut, ValRaw};
|
||||
use anyhow::{bail, Context, Result};
|
||||
@@ -9,7 +7,9 @@ use std::fmt;
|
||||
use std::marker;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::str;
|
||||
use wasmtime_environ::component::{ComponentTypes, InterfaceType, StringEncoding};
|
||||
use wasmtime_environ::component::{
|
||||
ComponentTypes, InterfaceType, StringEncoding, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
|
||||
};
|
||||
|
||||
/// A statically-typed version of [`Func`] which takes `Params` as input and
|
||||
/// returns `Return`.
|
||||
@@ -156,8 +156,8 @@ where
|
||||
// space reserved for the params/results is always of the appropriate
|
||||
// size (as the params/results needed differ depending on the "flatten"
|
||||
// count)
|
||||
if Params::flatten_count() <= MAX_STACK_PARAMS {
|
||||
if Return::flatten_count() <= MAX_STACK_RESULTS {
|
||||
if Params::flatten_count() <= MAX_FLAT_PARAMS {
|
||||
if Return::flatten_count() <= MAX_FLAT_RESULTS {
|
||||
self.func.call_raw(
|
||||
store,
|
||||
¶ms,
|
||||
@@ -173,7 +173,7 @@ where
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if Return::flatten_count() <= MAX_STACK_RESULTS {
|
||||
if Return::flatten_count() <= MAX_FLAT_RESULTS {
|
||||
self.func.call_raw(
|
||||
store,
|
||||
¶ms,
|
||||
@@ -203,7 +203,7 @@ where
|
||||
params: &Params,
|
||||
dst: &mut MaybeUninit<Params::Lower>,
|
||||
) -> Result<()> {
|
||||
assert!(Params::flatten_count() <= MAX_STACK_PARAMS);
|
||||
assert!(Params::flatten_count() <= MAX_FLAT_PARAMS);
|
||||
params.lower(store, options, dst)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -211,7 +211,7 @@ where
|
||||
/// Lower parameters onto a heap-allocated location.
|
||||
///
|
||||
/// This is used when the stack space to be used for the arguments is above
|
||||
/// the `MAX_STACK_PARAMS` threshold. Here the wasm's `realloc` function is
|
||||
/// the `MAX_FLAT_PARAMS` threshold. Here the wasm's `realloc` function is
|
||||
/// invoked to allocate space and then parameters are stored at that heap
|
||||
/// pointer location.
|
||||
fn lower_heap_args<T>(
|
||||
@@ -220,7 +220,7 @@ where
|
||||
params: &Params,
|
||||
dst: &mut MaybeUninit<ValRaw>,
|
||||
) -> Result<()> {
|
||||
assert!(Params::flatten_count() > MAX_STACK_PARAMS);
|
||||
assert!(Params::flatten_count() > MAX_FLAT_PARAMS);
|
||||
|
||||
// Memory must exist via validation if the arguments are stored on the
|
||||
// heap, so we can create a `MemoryMut` at this point. Afterwards
|
||||
@@ -257,14 +257,14 @@ where
|
||||
options: &Options,
|
||||
dst: &Return::Lower,
|
||||
) -> Result<Return> {
|
||||
assert!(Return::flatten_count() <= MAX_STACK_RESULTS);
|
||||
assert!(Return::flatten_count() <= MAX_FLAT_RESULTS);
|
||||
Return::lift(store, options, dst)
|
||||
}
|
||||
|
||||
/// Lift the result of a function where the result is stored indirectly on
|
||||
/// the heap.
|
||||
fn lift_heap_result(store: &StoreOpaque, options: &Options, dst: &ValRaw) -> Result<Return> {
|
||||
assert!(Return::flatten_count() > MAX_STACK_RESULTS);
|
||||
assert!(Return::flatten_count() > MAX_FLAT_RESULTS);
|
||||
// FIXME: needs to read an i64 for memory64
|
||||
let ptr = usize::try_from(dst.get_u32())?;
|
||||
if ptr % usize::try_from(Return::ALIGN32)? != 0 {
|
||||
|
||||
@@ -12,7 +12,7 @@ use wasmtime_environ::component::{
|
||||
ExtractPostReturn, ExtractRealloc, GlobalInitializer, InstantiateModule, LowerImport,
|
||||
RuntimeImportIndex, RuntimeInstanceIndex, RuntimeModuleIndex,
|
||||
};
|
||||
use wasmtime_environ::{EntityIndex, PrimaryMap};
|
||||
use wasmtime_environ::{EntityIndex, Global, GlobalInit, PrimaryMap, WasmType};
|
||||
use wasmtime_runtime::component::{ComponentInstance, OwnedComponentInstance};
|
||||
|
||||
/// An instantiated component.
|
||||
@@ -132,6 +132,18 @@ impl InstanceData {
|
||||
anyfunc: self.state.always_trap_anyfunc(*idx),
|
||||
})
|
||||
}
|
||||
CoreDef::InstanceFlags(idx) => {
|
||||
wasmtime_runtime::Export::Global(wasmtime_runtime::ExportGlobal {
|
||||
definition: self.state.instance_flags(*idx).as_raw(),
|
||||
global: Global {
|
||||
wasm_ty: WasmType::I32,
|
||||
mutability: true,
|
||||
initializer: GlobalInit::I32Const(0),
|
||||
},
|
||||
})
|
||||
}
|
||||
// This should have been processed away during compilation.
|
||||
CoreDef::Adapter(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,10 +366,28 @@ impl<'a> Instantiator<'a> {
|
||||
) -> &OwnedImports {
|
||||
self.core_imports.clear();
|
||||
self.core_imports.reserve(module);
|
||||
let mut imports = module.compiled_module().module().imports();
|
||||
|
||||
for arg in args {
|
||||
let export = self.data.lookup_def(store, arg);
|
||||
// The general idea of Wasmtime is that at runtime type-checks for
|
||||
// core wasm instantiations internally within a component are
|
||||
// unnecessary and superfluous. Naturally though mistakes may be
|
||||
// made, so double-check this property of wasmtime in debug mode.
|
||||
if cfg!(debug_assertions) {
|
||||
let export = self.data.lookup_def(store, arg);
|
||||
let (_, _, expected) = imports.next().unwrap();
|
||||
let val = unsafe { crate::Extern::from_wasmtime_export(export, store) };
|
||||
crate::types::matching::MatchCx {
|
||||
store,
|
||||
engine: store.engine(),
|
||||
signatures: module.signatures(),
|
||||
types: module.types(),
|
||||
}
|
||||
.extern_(&expected, &val)
|
||||
.expect("unexpected typecheck failure");
|
||||
}
|
||||
|
||||
let export = self.data.lookup_def(store, arg);
|
||||
// The unsafety here should be ok since the `export` is loaded
|
||||
// directly from an instance which should only give us valid export
|
||||
// items.
|
||||
@@ -365,6 +395,7 @@ impl<'a> Instantiator<'a> {
|
||||
self.core_imports.push_export(&export);
|
||||
}
|
||||
}
|
||||
debug_assert!(imports.next().is_none());
|
||||
|
||||
&self.core_imports
|
||||
}
|
||||
|
||||
@@ -426,6 +426,9 @@ impl<'a> SerializedModule<'a> {
|
||||
// setting just fine (it's just a section in the compiled file and
|
||||
// whether it's present or not)
|
||||
generate_address_map: _,
|
||||
|
||||
// Just a debugging aid, doesn't affect functionality at all.
|
||||
debug_adapter_modules: _,
|
||||
} = self.metadata.tunables;
|
||||
|
||||
Self::check_int(
|
||||
|
||||
Reference in New Issue
Block a user