use crate::component::translate::adapt::{Adapter, AdapterOptions};
use crate::component::translate::*;
use crate::{EntityType, PrimaryMap};
use indexmap::IndexMap;
pub(super) fn run(
types: &ComponentTypesBuilder,
result: &Translation<'_>,
nested_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
nested_components: &PrimaryMap<StaticComponentIndex, Translation<'_>>,
) -> Result<dfg::ComponentDfg> {
let mut inliner = Inliner {
types,
nested_modules,
nested_components,
result: Default::default(),
import_path_interner: Default::default(),
runtime_instances: PrimaryMap::default(),
};
let mut args = HashMap::with_capacity(result.exports.len());
for init in result.initializers.iter() {
let (name, ty) = match *init {
LocalInitializer::Import(name, ty) => (name, ty),
_ => continue,
};
let index = inliner.result.import_types.push((name.to_string(), ty));
let path = ImportPath::root(index);
args.insert(name, ComponentItemDef::from_import(path, ty)?);
}
let index = RuntimeComponentInstanceIndex::from_u32(0);
inliner.result.num_runtime_component_instances += 1;
let mut frames = vec![InlinerFrame::new(
index,
result,
ComponentClosure::default(),
args,
)];
let exports = inliner.run(&mut frames)?;
assert!(frames.is_empty());
let mut export_map = Default::default();
for (name, def) in exports {
inliner.record_export(name, def, &mut export_map)?;
}
inliner.result.exports = export_map;
Ok(inliner.result)
}
struct Inliner<'a> {
types: &'a ComponentTypesBuilder,
nested_modules: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
nested_components: &'a PrimaryMap<StaticComponentIndex, Translation<'a>>,
result: dfg::ComponentDfg,
import_path_interner: HashMap<ImportPath<'a>, RuntimeImportIndex>,
runtime_instances: PrimaryMap<dfg::InstanceId, InstanceModule>,
}
struct InlinerFrame<'a> {
instance: RuntimeComponentInstanceIndex,
initializers: std::slice::Iter<'a, LocalInitializer<'a>>,
translation: &'a Translation<'a>,
closure: ComponentClosure<'a>,
args: HashMap<&'a str, ComponentItemDef<'a>>,
funcs: PrimaryMap<FuncIndex, dfg::CoreDef>,
memories: PrimaryMap<MemoryIndex, dfg::CoreExport<EntityIndex>>,
tables: PrimaryMap<TableIndex, dfg::CoreExport<EntityIndex>>,
globals: PrimaryMap<GlobalIndex, dfg::CoreExport<EntityIndex>>,
modules: PrimaryMap<ModuleIndex, ModuleDef<'a>>,
component_funcs: PrimaryMap<ComponentFuncIndex, ComponentFuncDef<'a>>,
module_instances: PrimaryMap<ModuleInstanceIndex, ModuleInstanceDef<'a>>,
component_instances: PrimaryMap<ComponentInstanceIndex, ComponentInstanceDef<'a>>,
components: PrimaryMap<ComponentIndex, ComponentDef<'a>>,
}
#[derive(Default, Clone)]
struct ComponentClosure<'a> {
modules: PrimaryMap<ModuleUpvarIndex, ModuleDef<'a>>,
components: PrimaryMap<ComponentUpvarIndex, ComponentDef<'a>>,
}
#[derive(Clone, PartialEq, Hash, Eq)]
struct ImportPath<'a> {
index: ImportIndex,
path: Vec<&'a str>,
}
#[derive(Clone)]
enum ComponentItemDef<'a> {
Component(ComponentDef<'a>),
Instance(ComponentInstanceDef<'a>),
Func(ComponentFuncDef<'a>),
Module(ModuleDef<'a>),
Type(TypeDef),
}
#[derive(Clone)]
enum ModuleDef<'a> {
Static(StaticModuleIndex),
Import(ImportPath<'a>, TypeModuleIndex),
}
enum ModuleInstanceDef<'a> {
Instantiated(dfg::InstanceId, ModuleIndex),
Synthetic(&'a HashMap<&'a str, EntityIndex>),
}
#[derive(Clone)]
enum ComponentFuncDef<'a> {
Import(ImportPath<'a>),
Lifted {
ty: TypeFuncIndex,
func: dfg::CoreDef,
options: AdapterOptions,
},
}
#[derive(Clone)]
enum ComponentInstanceDef<'a> {
Import(ImportPath<'a>, TypeComponentInstanceIndex),
Items(IndexMap<&'a str, ComponentItemDef<'a>>),
}
#[derive(Clone)]
struct ComponentDef<'a> {
index: StaticComponentIndex,
closure: ComponentClosure<'a>,
}
impl<'a> Inliner<'a> {
fn run(
&mut self,
frames: &mut Vec<InlinerFrame<'a>>,
) -> Result<IndexMap<&'a str, ComponentItemDef<'a>>> {
loop {
let frame = frames.last_mut().unwrap();
match frame.initializers.next() {
Some(init) => match self.initializer(frame, init)? {
Some(new_frame) => frames.push(new_frame),
None => {}
},
None => {
let exports = frame
.translation
.exports
.iter()
.map(|(name, item)| (*name, frame.item(*item)))
.collect();
frames.pop();
match frames.last_mut() {
Some(parent) => {
parent
.component_instances
.push(ComponentInstanceDef::Items(exports));
}
None => break Ok(exports),
}
}
}
}
}
fn initializer(
&mut self,
frame: &mut InlinerFrame<'a>,
initializer: &'a LocalInitializer,
) -> Result<Option<InlinerFrame<'a>>> {
use LocalInitializer::*;
match initializer {
Import(name, _ty) => match &frame.args[name] {
ComponentItemDef::Module(i) => {
frame.modules.push(i.clone());
}
ComponentItemDef::Component(i) => {
frame.components.push(i.clone());
}
ComponentItemDef::Instance(i) => {
frame.component_instances.push(i.clone());
}
ComponentItemDef::Func(i) => {
frame.component_funcs.push(i.clone());
}
ComponentItemDef::Type(_ty) => {}
},
Lower(func, options) => {
let canonical_abi = frame.translation.funcs[frame.funcs.next_key()];
let lower_ty = frame.translation.component_funcs[*func];
let options_lower = self.adapter_options(frame, options);
let func = match &frame.component_funcs[*func] {
ComponentFuncDef::Import(path) => {
let import = self.runtime_import(path);
let options = self.canonical_options(options_lower);
let index = self.result.lowerings.push_uniq(dfg::LowerImport {
canonical_abi,
import,
options,
});
dfg::CoreDef::Lowered(index)
}
ComponentFuncDef::Lifted {
options: options_lift,
..
} if options_lift.instance == options_lower.instance => {
let index = self.result.always_trap.push_uniq(canonical_abi);
dfg::CoreDef::AlwaysTrap(index)
}
ComponentFuncDef::Lifted {
ty: lift_ty,
func,
options: options_lift,
} => {
let adapter_idx = self.result.adapters.push_uniq(Adapter {
lift_ty: *lift_ty,
lift_options: options_lift.clone(),
lower_ty,
lower_options: options_lower,
func: func.clone(),
});
dfg::CoreDef::Adapter(adapter_idx)
}
};
frame.funcs.push(func);
}
Lift(ty, func, options) => {
let options = self.adapter_options(frame, options);
frame.component_funcs.push(ComponentFuncDef::Lifted {
ty: *ty,
func: frame.funcs[*func].clone(),
options,
});
}
ModuleStatic(idx) => {
frame.modules.push(ModuleDef::Static(*idx));
}
ModuleInstantiate(module, args) => {
let instance_module;
let init = match &frame.modules[*module] {
ModuleDef::Static(idx) => {
let mut defs = Vec::new();
for (module, name, _ty) in self.nested_modules[*idx].module.imports() {
let instance = args[module];
defs.push(
self.core_def_of_module_instance_export(frame, instance, name),
);
}
instance_module = InstanceModule::Static(*idx);
dfg::Instance::Static(*idx, defs.into())
}
ModuleDef::Import(path, ty) => {
let mut defs = IndexMap::new();
for ((module, name), _) in self.types[*ty].imports.iter() {
let instance = args[module.as_str()];
let def =
self.core_def_of_module_instance_export(frame, instance, name);
defs.entry(module.to_string())
.or_insert(IndexMap::new())
.insert(name.to_string(), def);
}
let index = self.runtime_import(path);
instance_module = InstanceModule::Import(*ty);
dfg::Instance::Import(index, defs)
}
};
let idx = self.result.instances.push(init);
let idx2 = self.runtime_instances.push(instance_module);
assert_eq!(idx, idx2);
frame
.module_instances
.push(ModuleInstanceDef::Instantiated(idx, *module));
}
ModuleSynthetic(map) => {
frame
.module_instances
.push(ModuleInstanceDef::Synthetic(map));
}
ComponentStatic(index, vars) => {
frame.components.push(ComponentDef {
index: *index,
closure: ComponentClosure {
modules: vars
.modules
.iter()
.map(|(_, m)| frame.closed_over_module(m))
.collect(),
components: vars
.components
.iter()
.map(|(_, m)| frame.closed_over_component(m))
.collect(),
},
});
}
ComponentInstantiate(component, args) => {
let component: &ComponentDef<'a> = &frame.components[*component];
let index = RuntimeComponentInstanceIndex::from_u32(
self.result.num_runtime_component_instances,
);
self.result.num_runtime_component_instances += 1;
let frame = InlinerFrame::new(
index,
&self.nested_components[component.index],
component.closure.clone(),
args.iter()
.map(|(name, item)| (*name, frame.item(*item)))
.collect(),
);
return Ok(Some(frame));
}
ComponentSynthetic(map) => {
let items = map
.iter()
.map(|(name, index)| (*name, frame.item(*index)))
.collect();
frame
.component_instances
.push(ComponentInstanceDef::Items(items));
}
AliasExportFunc(instance, name) => {
frame
.funcs
.push(self.core_def_of_module_instance_export(frame, *instance, *name));
}
AliasExportTable(instance, name) => {
frame.tables.push(
match self.core_def_of_module_instance_export(frame, *instance, *name) {
dfg::CoreDef::Export(e) => e,
_ => unreachable!(),
},
);
}
AliasExportGlobal(instance, name) => {
frame.globals.push(
match self.core_def_of_module_instance_export(frame, *instance, *name) {
dfg::CoreDef::Export(e) => e,
_ => unreachable!(),
},
);
}
AliasExportMemory(instance, name) => {
frame.memories.push(
match self.core_def_of_module_instance_export(frame, *instance, *name) {
dfg::CoreDef::Export(e) => e,
_ => unreachable!(),
},
);
}
AliasComponentExport(instance, name) => {
match &frame.component_instances[*instance] {
ComponentInstanceDef::Import(path, ty) => {
let mut path = path.clone();
path.path.push(name);
match self.types[*ty].exports[*name] {
TypeDef::ComponentFunc(_) => {
frame.component_funcs.push(ComponentFuncDef::Import(path));
}
TypeDef::ComponentInstance(ty) => {
frame
.component_instances
.push(ComponentInstanceDef::Import(path, ty));
}
TypeDef::Module(ty) => {
frame.modules.push(ModuleDef::Import(path, ty));
}
TypeDef::Component(_) => {
unimplemented!("aliasing component export of component import")
}
TypeDef::Interface(_) => {
unimplemented!("aliasing type export of component import")
}
TypeDef::CoreFunc(_) => unreachable!(),
}
}
ComponentInstanceDef::Items(map) => match &map[*name] {
ComponentItemDef::Func(i) => {
frame.component_funcs.push(i.clone());
}
ComponentItemDef::Module(i) => {
frame.modules.push(i.clone());
}
ComponentItemDef::Component(i) => {
frame.components.push(i.clone());
}
ComponentItemDef::Instance(i) => {
let instance = i.clone();
frame.component_instances.push(instance);
}
ComponentItemDef::Type(_ty) => {}
},
}
}
AliasModule(idx) => {
frame.modules.push(frame.closed_over_module(idx));
}
AliasComponent(idx) => {
frame.components.push(frame.closed_over_component(idx));
}
}
Ok(None)
}
fn runtime_import(&mut self, path: &ImportPath<'a>) -> RuntimeImportIndex {
*self
.import_path_interner
.entry(path.clone())
.or_insert_with(|| {
self.result.imports.push((
path.index,
path.path.iter().map(|s| s.to_string()).collect(),
))
})
}
fn core_def_of_module_instance_export(
&self,
frame: &InlinerFrame<'a>,
instance: ModuleInstanceIndex,
name: &'a str,
) -> dfg::CoreDef {
match &frame.module_instances[instance] {
ModuleInstanceDef::Instantiated(instance, module) => {
let item = match frame.modules[*module] {
ModuleDef::Static(idx) => {
let entity = self.nested_modules[idx].module.exports[name];
ExportItem::Index(entity)
}
ModuleDef::Import(..) => ExportItem::Name(name.to_string()),
};
dfg::CoreExport {
instance: *instance,
item,
}
.into()
}
ModuleInstanceDef::Synthetic(instance) => match instance[name] {
EntityIndex::Function(i) => frame.funcs[i].clone(),
EntityIndex::Table(i) => frame.tables[i].clone().into(),
EntityIndex::Global(i) => frame.globals[i].clone().into(),
EntityIndex::Memory(i) => frame.memories[i].clone().into(),
},
}
}
fn adapter_options(
&mut self,
frame: &InlinerFrame<'a>,
options: &LocalCanonicalOptions,
) -> AdapterOptions {
let memory = options.memory.map(|i| {
frame.memories[i].clone().map_index(|i| match i {
EntityIndex::Memory(i) => i,
_ => unreachable!(),
})
});
let memory64 = match &memory {
Some(memory) => match &self.runtime_instances[memory.instance] {
InstanceModule::Static(idx) => match &memory.item {
ExportItem::Index(i) => {
let plan = &self.nested_modules[*idx].module.memory_plans[*i];
plan.memory.memory64
}
ExportItem::Name(_) => unreachable!(),
},
InstanceModule::Import(ty) => match &memory.item {
ExportItem::Name(name) => match self.types[*ty].exports[name] {
EntityType::Memory(m) => m.memory64,
_ => unreachable!(),
},
ExportItem::Index(_) => unreachable!(),
},
},
None => false,
};
let realloc = options.realloc.map(|i| frame.funcs[i].clone());
let post_return = options.post_return.map(|i| frame.funcs[i].clone());
AdapterOptions {
instance: frame.instance,
string_encoding: options.string_encoding,
memory,
memory64,
realloc,
post_return,
}
}
fn canonical_options(&mut self, options: AdapterOptions) -> dfg::CanonicalOptions {
let memory = options
.memory
.map(|export| self.result.memories.push_uniq(export));
let realloc = options
.realloc
.map(|def| self.result.reallocs.push_uniq(def));
let post_return = options
.post_return
.map(|def| self.result.post_returns.push_uniq(def));
dfg::CanonicalOptions {
instance: options.instance,
string_encoding: options.string_encoding,
memory,
realloc,
post_return,
}
}
fn record_export(
&mut self,
name: &str,
def: ComponentItemDef<'a>,
map: &mut IndexMap<String, dfg::Export>,
) -> Result<()> {
let export = match def {
ComponentItemDef::Module(module) => match module {
ModuleDef::Static(idx) => dfg::Export::ModuleStatic(idx),
ModuleDef::Import(path, _) => dfg::Export::ModuleImport(self.runtime_import(&path)),
},
ComponentItemDef::Func(func) => match func {
ComponentFuncDef::Lifted { ty, func, options } => {
let options = self.canonical_options(options);
dfg::Export::LiftedFunction { ty, func, options }
}
ComponentFuncDef::Import(_) => {
bail!("component export `{name}` is a reexport of an imported function which is not implemented")
}
},
ComponentItemDef::Instance(instance) => {
let mut result = IndexMap::new();
match instance {
ComponentInstanceDef::Import(path, ty) => {
for (name, ty) in self.types[ty].exports.iter() {
let mut path = path.clone();
path.path.push(name);
let def = ComponentItemDef::from_import(path, *ty)?;
self.record_export(name, def, &mut result)?;
}
}
ComponentInstanceDef::Items(map) => {
for (name, def) in map {
self.record_export(name, def, &mut result)?;
}
}
}
dfg::Export::Instance(result)
}
ComponentItemDef::Component(_) => {
bail!("exporting a component from the root component is not supported")
}
ComponentItemDef::Type(def) => dfg::Export::Type(def),
};
map.insert(name.to_string(), export);
Ok(())
}
}
impl<'a> InlinerFrame<'a> {
fn new(
instance: RuntimeComponentInstanceIndex,
translation: &'a Translation<'a>,
closure: ComponentClosure<'a>,
args: HashMap<&'a str, ComponentItemDef<'a>>,
) -> Self {
InlinerFrame {
instance,
translation,
closure,
args,
initializers: translation.initializers.iter(),
funcs: Default::default(),
memories: Default::default(),
tables: Default::default(),
globals: Default::default(),
component_instances: Default::default(),
component_funcs: Default::default(),
module_instances: Default::default(),
components: Default::default(),
modules: Default::default(),
}
}
fn item(&self, index: ComponentItem) -> ComponentItemDef<'a> {
match index {
ComponentItem::Func(i) => ComponentItemDef::Func(self.component_funcs[i].clone()),
ComponentItem::Component(i) => ComponentItemDef::Component(self.components[i].clone()),
ComponentItem::ComponentInstance(i) => {
ComponentItemDef::Instance(self.component_instances[i].clone())
}
ComponentItem::Module(i) => ComponentItemDef::Module(self.modules[i].clone()),
ComponentItem::Type(t) => ComponentItemDef::Type(t),
}
}
fn closed_over_module(&self, index: &ClosedOverModule) -> ModuleDef<'a> {
match *index {
ClosedOverModule::Local(i) => self.modules[i].clone(),
ClosedOverModule::Upvar(i) => self.closure.modules[i].clone(),
}
}
fn closed_over_component(&self, index: &ClosedOverComponent) -> ComponentDef<'a> {
match *index {
ClosedOverComponent::Local(i) => self.components[i].clone(),
ClosedOverComponent::Upvar(i) => self.closure.components[i].clone(),
}
}
}
impl<'a> ImportPath<'a> {
fn root(index: ImportIndex) -> ImportPath<'a> {
ImportPath {
index,
path: Vec::new(),
}
}
}
impl<'a> ComponentItemDef<'a> {
fn from_import(path: ImportPath<'a>, ty: TypeDef) -> Result<ComponentItemDef<'a>> {
let item = match ty {
TypeDef::Module(ty) => ComponentItemDef::Module(ModuleDef::Import(path, ty)),
TypeDef::ComponentInstance(ty) => {
ComponentItemDef::Instance(ComponentInstanceDef::Import(path, ty))
}
TypeDef::ComponentFunc(_ty) => ComponentItemDef::Func(ComponentFuncDef::Import(path)),
TypeDef::Component(_ty) => bail!("root-level component imports are not supported"),
TypeDef::Interface(_ty) => unimplemented!("import of a type"),
TypeDef::CoreFunc(_ty) => unreachable!(),
};
Ok(item)
}
}
enum InstanceModule {
Static(StaticModuleIndex),
Import(TypeModuleIndex),
}