Implement module imports into components (#4208)

* Implement module imports into components

As a step towards implementing function imports into a component this
commit implements importing modules into a component. This fills out
missing pieces of functionality such as exporting modules as well. The
previous translation code had initial support for translating imported
modules but some of the AST type information was restructured with
feedback from this implementation, namely splitting the
`InstantiateModule` initializer into separate upvar/import variants to
clarify that the item orderings for imports are resolved differently at
runtime.

Much of this commit is also adding infrastructure for any imports at all
into a component. For example a `Linker` type (analagous to
`wasmtime::Linker`) was added here as well. For now this type is quite
limited due to the inability to define host functions (it can only work
with instances and instances-of-modules) but it's enough to start
writing `*.wast` tests which exercise lots of module-related functionality.

* Fix a warning
This commit is contained in:
Alex Crichton
2022-06-03 09:33:18 -05:00
committed by GitHub
parent 816aae6aca
commit b49c5c878e
12 changed files with 1254 additions and 250 deletions

View File

@@ -4,6 +4,7 @@ use crate::{
Tunables,
};
use anyhow::{bail, Result};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::mem;
use wasmparser::{Chunk, Encoding, Parser, Payload, Validator};
@@ -483,7 +484,7 @@ impl<'a, 'data> Translator<'a, 'data> {
fn module_instance(
&mut self,
module_idx: ModuleIndex,
module: ModuleIndex,
args: &[wasmparser::ModuleArg<'data>],
) -> InstanceDef<'data> {
// Map the flat list of `args` to instead a name-to-instance index.
@@ -496,7 +497,7 @@ impl<'a, 'data> Translator<'a, 'data> {
}
}
let (imports, module) = match self.result.modules[module_idx].clone() {
let instantiate = match self.result.modules[module].clone() {
// A module defined within this component is being instantiated
// which means we statically know the structure of the module. The
// list of imports required is ordered by the actual list of imports
@@ -507,8 +508,13 @@ impl<'a, 'data> Translator<'a, 'data> {
.module
.imports()
.map(|(m, n, _)| (m.to_string(), n.to_string()))
.collect::<Vec<_>>();
(args, ModuleToInstantiate::Upvar(upvar_idx))
.collect::<Vec<_>>()
.iter()
.map(|(module, name)| {
self.lookup_core_def(instance_by_name[module.as_str()], name)
})
.collect();
InstantiateModule::Upvar(upvar_idx, args)
}
// For imported modules the list of arguments is built to match the
@@ -518,36 +524,27 @@ impl<'a, 'data> Translator<'a, 'data> {
// imports in a different order.
ModuleDef::Import { ty, import } => {
let import = self.runtime_import_index(import);
let args = self.types[ty].imports.keys().cloned().collect();
(args, ModuleToInstantiate::Import(import))
let mut args = IndexMap::new();
let imports = self.types[ty].imports.keys().cloned().collect::<Vec<_>>();
for (module, name) in imports {
let def = self.lookup_core_def(instance_by_name[module.as_str()], &name);
let prev = args
.entry(module)
.or_insert(IndexMap::new())
.insert(name, def);
assert!(prev.is_none());
}
InstantiateModule::Import(import, args)
}
};
// Translate the desired order of import strings to a `CoreDef` used to
// instantiate each module. Of the two-level namespace the `module` name
// is indicated by the `args` argument to this function and the `name`
// is the export of the instance found that's used.
let args = imports
.iter()
.map(|(module, name)| self.lookup_core_def(instance_by_name[module.as_str()], name))
.collect();
// Record initializer information related to this instantiation now that
// we've figure out all the arguments.
let instance = RuntimeInstanceIndex::from_u32(self.result.component.num_runtime_instances);
self.result.component.num_runtime_instances += 1;
self.result
.component
.initializers
.push(Initializer::InstantiateModule {
instance,
module,
args,
});
InstanceDef::Module {
instance,
module: module_idx,
}
.push(Initializer::InstantiateModule(instantiate));
let instance = RuntimeInstanceIndex::from_u32(self.result.component.num_runtime_instances);
self.result.component.num_runtime_instances += 1;
InstanceDef::Module { instance, module }
}
/// Calculate the `CoreDef`, a definition of a core wasm item, corresponding
@@ -693,8 +690,17 @@ impl<'a, 'data> Translator<'a, 'data> {
let export = match export.kind {
wasmparser::ComponentExportKind::Module(i) => {
let idx = ModuleIndex::from_u32(i);
drop(idx);
unimplemented!("exporting a module");
let init = match self.result.modules[idx].clone() {
ModuleDef::Upvar(idx) => Initializer::SaveModuleUpvar(idx),
ModuleDef::Import { import, .. } => {
Initializer::SaveModuleImport(self.runtime_import_index(import))
}
};
self.result.component.initializers.push(init);
let runtime_index =
RuntimeModuleIndex::from_u32(self.result.component.num_runtime_modules);
self.result.component.num_runtime_modules += 1;
Export::Module(runtime_index)
}
wasmparser::ComponentExportKind::Component(i) => {
let idx = ComponentIndex::from_u32(i);
@@ -1021,7 +1027,7 @@ impl<'a, 'data> Translator<'a, 'data> {
self.result
.component
.initializers
.push(Initializer::ExtractMemory { index, export });
.push(Initializer::ExtractMemory(export));
index
}
@@ -1035,7 +1041,7 @@ impl<'a, 'data> Translator<'a, 'data> {
self.result
.component
.initializers
.push(Initializer::ExtractRealloc { index, def });
.push(Initializer::ExtractRealloc(def));
index
}
}