use crate::builder::ComponentBuilder;
use crate::metadata::{self, Bindgen, ModuleMetadata};
use crate::validation::{ValidatedModule, BARE_FUNC_MODULE_NAME, MAIN_MODULE_IMPORT_NAME};
use crate::StringEncoding;
use anyhow::{anyhow, bail, Context, Result};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::hash::Hash;
use wasm_encoder::*;
use wasmparser::{Validator, WasmFeatures};
use wit_parser::{
abi::{AbiVariant, WasmSignature, WasmType},
Function, InterfaceId, Resolve, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldItem,
};
const INDIRECT_TABLE_NAME: &str = "$imports";
mod wit;
pub use wit::encode;
mod types;
use types::{InstanceTypeEncoder, RootTypeEncoder, ValtypeEncoder};
mod world;
use world::{ComponentWorld, ImportedInterface};
fn to_val_type(ty: &WasmType) -> ValType {
match ty {
WasmType::I32 => ValType::I32,
WasmType::I64 => ValType::I64,
WasmType::F32 => ValType::F32,
WasmType::F64 => ValType::F64,
}
}
bitflags::bitflags! {
pub struct RequiredOptions: u8 {
const MEMORY = 1 << 0;
const REALLOC = 1 << 1;
const STRING_ENCODING = 1 << 2;
}
}
impl RequiredOptions {
fn for_import(resolve: &Resolve, func: &Function) -> RequiredOptions {
let sig = resolve.wasm_signature(AbiVariant::GuestImport, func);
let mut ret = RequiredOptions::empty();
ret.add_lift(TypeContents::for_types(
resolve,
func.params.iter().map(|(_, t)| t),
));
ret.add_lower(TypeContents::for_types(resolve, func.results.iter_types()));
if sig.retptr || sig.indirect_params {
ret |= RequiredOptions::MEMORY;
}
ret
}
fn for_export(resolve: &Resolve, func: &Function) -> RequiredOptions {
let sig = resolve.wasm_signature(AbiVariant::GuestExport, func);
let mut ret = RequiredOptions::empty();
ret.add_lower(TypeContents::for_types(
resolve,
func.params.iter().map(|(_, t)| t),
));
ret.add_lift(TypeContents::for_types(resolve, func.results.iter_types()));
if sig.retptr || sig.indirect_params {
ret |= RequiredOptions::MEMORY;
if sig.indirect_params {
ret |= RequiredOptions::REALLOC;
}
}
ret
}
fn add_lower(&mut self, types: TypeContents) {
if types.contains(TypeContents::LIST) {
*self |= RequiredOptions::MEMORY | RequiredOptions::REALLOC;
}
if types.contains(TypeContents::STRING) {
*self |= RequiredOptions::MEMORY
| RequiredOptions::STRING_ENCODING
| RequiredOptions::REALLOC;
}
}
fn add_lift(&mut self, types: TypeContents) {
if types.contains(TypeContents::LIST) {
*self |= RequiredOptions::MEMORY;
}
if types.contains(TypeContents::STRING) {
*self |= RequiredOptions::MEMORY | RequiredOptions::STRING_ENCODING;
}
}
fn into_iter(
self,
encoding: StringEncoding,
memory_index: Option<u32>,
realloc_index: Option<u32>,
) -> Result<impl ExactSizeIterator<Item = CanonicalOption>> {
#[derive(Default)]
struct Iter {
options: [Option<CanonicalOption>; 3],
current: usize,
count: usize,
}
impl Iter {
fn push(&mut self, option: CanonicalOption) {
assert!(self.count < self.options.len());
self.options[self.count] = Some(option);
self.count += 1;
}
}
impl Iterator for Iter {
type Item = CanonicalOption;
fn next(&mut self) -> Option<Self::Item> {
if self.current == self.count {
return None;
}
let option = self.options[self.current];
self.current += 1;
option
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.count - self.current, Some(self.count - self.current))
}
}
impl ExactSizeIterator for Iter {}
let mut iter = Iter::default();
if self.contains(RequiredOptions::MEMORY) {
iter.push(CanonicalOption::Memory(memory_index.ok_or_else(|| {
anyhow!("module does not export a memory named `memory`")
})?));
}
if self.contains(RequiredOptions::REALLOC) {
iter.push(CanonicalOption::Realloc(realloc_index.ok_or_else(
|| anyhow!("module does not export a function named `cabi_realloc`"),
)?));
}
if self.contains(RequiredOptions::STRING_ENCODING) {
iter.push(encoding.into());
}
Ok(iter)
}
}
bitflags::bitflags! {
struct TypeContents: u8 {
const STRING = 1 << 0;
const LIST = 1 << 1;
}
}
impl TypeContents {
fn for_types<'a>(resolve: &Resolve, types: impl Iterator<Item = &'a Type>) -> Self {
let mut cur = TypeContents::empty();
for ty in types {
cur |= Self::for_type(resolve, ty);
}
cur
}
fn for_optional_types<'a>(
resolve: &Resolve,
types: impl Iterator<Item = Option<&'a Type>>,
) -> Self {
Self::for_types(resolve, types.flatten())
}
fn for_optional_type(resolve: &Resolve, ty: Option<&Type>) -> Self {
match ty {
Some(ty) => Self::for_type(resolve, ty),
None => Self::empty(),
}
}
fn for_type(resolve: &Resolve, ty: &Type) -> Self {
match ty {
Type::Id(id) => match &resolve.types[*id].kind {
TypeDefKind::Record(r) => Self::for_types(resolve, r.fields.iter().map(|f| &f.ty)),
TypeDefKind::Tuple(t) => Self::for_types(resolve, t.types.iter()),
TypeDefKind::Flags(_) => Self::empty(),
TypeDefKind::Option(t) => Self::for_type(resolve, t),
TypeDefKind::Result(r) => {
Self::for_optional_type(resolve, r.ok.as_ref())
| Self::for_optional_type(resolve, r.err.as_ref())
}
TypeDefKind::Variant(v) => {
Self::for_optional_types(resolve, v.cases.iter().map(|c| c.ty.as_ref()))
}
TypeDefKind::Union(v) => Self::for_types(resolve, v.cases.iter().map(|c| &c.ty)),
TypeDefKind::Enum(_) => Self::empty(),
TypeDefKind::List(t) => Self::for_type(resolve, t) | Self::LIST,
TypeDefKind::Type(t) => Self::for_type(resolve, t),
TypeDefKind::Future(_) => todo!("encoding for future"),
TypeDefKind::Stream(_) => todo!("encoding for stream"),
TypeDefKind::Unknown => unreachable!(),
},
Type::String => Self::STRING,
_ => Self::empty(),
}
}
}
pub struct EncodingState<'a> {
component: ComponentBuilder,
module_index: Option<u32>,
instance_index: Option<u32>,
memory_index: Option<u32>,
realloc_index: Option<u32>,
shim_instance_index: Option<u32>,
fixups_module_index: Option<u32>,
adapter_modules: IndexMap<&'a str, u32>,
adapter_instances: IndexMap<&'a str, u32>,
adapter_import_reallocs: IndexMap<&'a str, Option<u32>>,
adapter_export_reallocs: IndexMap<&'a str, Option<u32>>,
imported_instances: IndexMap<InterfaceId, u32>,
imported_funcs: IndexMap<&'a str, u32>,
exported_instances: IndexMap<InterfaceId, u32>,
type_map: HashMap<TypeId, u32>,
func_type_map: HashMap<types::FunctionKey<'a>, u32>,
info: &'a ComponentWorld<'a>,
}
impl<'a> EncodingState<'a> {
fn encode_core_modules(&mut self) {
assert!(self.module_index.is_none());
let idx = self.component.core_module_raw(&self.info.encoder.module);
self.module_index = Some(idx);
for (name, (_, wasm)) in self.info.adapters.iter() {
let idx = self.component.core_module_raw(wasm);
let prev = self.adapter_modules.insert(name, idx);
assert!(prev.is_none());
}
}
fn root_type_encoder(&mut self, interface: Option<InterfaceId>) -> RootTypeEncoder<'_, 'a> {
RootTypeEncoder {
state: self,
type_exports: Vec::new(),
interface,
type_map: Default::default(),
func_type_map: Default::default(),
}
}
fn instance_type_encoder(&mut self, interface: InterfaceId) -> InstanceTypeEncoder<'_, 'a> {
InstanceTypeEncoder {
state: self,
interface,
type_map: Default::default(),
func_type_map: Default::default(),
ty: Default::default(),
}
}
fn encode_imports(&mut self) -> Result<()> {
for (name, info) in self.info.import_map.iter() {
match name {
Some(name) => self.encode_interface_import(name, info)?,
None => self.encode_root_import_funcs(info)?,
}
}
Ok(())
}
fn encode_interface_import(&mut self, name: &str, info: &ImportedInterface) -> Result<()> {
let resolve = &self.info.encoder.metadata.resolve;
let (interface_id, url) = info.interface.as_ref().unwrap();
let interface_id = *interface_id;
let interface = &resolve.interfaces[interface_id];
log::trace!("encoding imports for `{name}` as {:?}", interface_id);
let mut encoder = self.instance_type_encoder(interface_id);
for (_, func) in interface.functions.iter() {
if !info.required.contains(func.name.as_str()) {
continue;
}
log::trace!("encoding function type for `{}`", func.name);
let idx = encoder.encode_func_type(resolve, func)?;
encoder
.ty
.export(&func.name, "", ComponentTypeRef::Func(idx));
}
if let Some(live) = encoder.state.info.live_types.get(&interface_id) {
for ty in live {
log::trace!("encoding extra type {ty:?}");
encoder.encode_valtype(resolve, &Type::Id(*ty))?;
}
}
let ty = encoder.ty;
if ty.is_empty() {
return Ok(());
}
let instance_type_idx = self.component.instance_type(&ty);
let instance_idx =
self.component
.import(name, url, ComponentTypeRef::Instance(instance_type_idx));
let prev = self.imported_instances.insert(interface_id, instance_idx);
assert!(prev.is_none());
Ok(())
}
fn encode_root_import_funcs(&mut self, info: &ImportedInterface) -> Result<()> {
let resolve = &self.info.encoder.metadata.resolve;
let world = self.info.encoder.metadata.world;
for (name, item) in resolve.worlds[world].imports.iter() {
let func = match item {
WorldItem::Function(f) => f,
WorldItem::Interface(_) | WorldItem::Type(_) => continue,
};
if !info.required.contains(name.as_str()) {
continue;
}
log::trace!("encoding function type for `{}`", func.name);
let mut encoder = self.root_type_encoder(None);
let idx = encoder.encode_func_type(resolve, func)?;
assert!(encoder.type_exports.is_empty());
let func_idx = self.component.import(name, "", ComponentTypeRef::Func(idx));
let prev = self.imported_funcs.insert(name, func_idx);
assert!(prev.is_none());
}
Ok(())
}
fn index_of_type_export(&mut self, id: TypeId) -> u32 {
let ty = &self.info.encoder.metadata.resolve.types[id];
let interface = match ty.owner {
TypeOwner::Interface(id) => id,
_ => panic!("cannot import anonymous type across interfaces"),
};
let name = ty
.name
.as_ref()
.expect("cannot import anonymous type across interfaces");
let instance = self
.exported_instances
.get(&interface)
.copied()
.unwrap_or_else(|| self.imported_instances[&interface]);
self.component.alias_type_export(instance, name)
}
fn encode_core_instantiation(&mut self) -> Result<()> {
let info = &self.info.info;
let shims = self.encode_shim_instantiation();
let mut args = Vec::new();
for core_wasm_name in info.required_imports.keys() {
let index = self.import_instance_to_lowered_core_instance(
CustomModule::Main,
*core_wasm_name,
&shims,
info.metadata,
);
args.push((*core_wasm_name, ModuleArg::Instance(index)));
}
for (adapter, funcs) in info.adapters_required.iter() {
let shim_instance = self
.shim_instance_index
.expect("shim should be instantiated");
let mut exports = Vec::new();
for (func, _ty) in funcs {
let index = self.component.alias_core_item(
shim_instance,
ExportKind::Func,
&shims.shim_names[&ShimKind::Adapter { adapter, func }],
);
exports.push((*func, ExportKind::Func, index));
}
let index = self.component.instantiate_core_exports(exports);
args.push((*adapter, ModuleArg::Instance(index)));
}
self.instantiate_core_module(args, info);
self.instantiate_adapter_modules(&shims);
self.encode_indirect_lowerings(shims)
}
fn import_instance_to_lowered_core_instance(
&mut self,
for_module: CustomModule<'_>,
core_wasm_name: &str,
shims: &Shims<'_>,
metadata: &ModuleMetadata,
) -> u32 {
let interface = if core_wasm_name == BARE_FUNC_MODULE_NAME {
None
} else {
Some(core_wasm_name)
};
let import = &self.info.import_map[&interface];
let mut exports = Vec::with_capacity(import.direct.len() + import.indirect.len());
for (i, lowering) in import.indirect.iter().enumerate() {
let encoding =
metadata.import_encodings[&(core_wasm_name.to_string(), lowering.name.to_string())];
let index = self.component.alias_core_item(
self.shim_instance_index
.expect("shim should be instantiated"),
ExportKind::Func,
&shims.shim_names[&ShimKind::IndirectLowering {
interface,
indirect_index: i,
realloc: for_module,
encoding,
}],
);
exports.push((lowering.name, ExportKind::Func, index));
}
for lowering in &import.direct {
let func_index = match &import.interface {
Some((interface, _url)) => {
let instance_index = self.imported_instances[interface];
self.component.alias_func(instance_index, lowering.name)
}
None => self.imported_funcs[lowering.name],
};
let core_func_index = self.component.lower_func(func_index, []);
exports.push((lowering.name, ExportKind::Func, core_func_index));
}
self.component.instantiate_core_exports(exports)
}
fn encode_exports(&mut self, opts: &'a ComponentEncoder, module: CustomModule) -> Result<()> {
let resolve = &opts.metadata.resolve;
let world = match module {
CustomModule::Main => opts.metadata.world,
CustomModule::Adapter(name) => opts.adapters[name].2,
};
let world = &resolve.worlds[world];
for (export_name, export) in world.exports.iter() {
match export {
WorldItem::Type(ty) => {
let mut enc = self.root_type_encoder(None);
enc.encode_valtype(resolve, &Type::Id(*ty))?;
assert!(enc.type_exports.is_empty());
}
WorldItem::Function(func) => {
let mut enc = self.root_type_encoder(None);
let ty = enc.encode_func_type(resolve, func)?;
for (idx, name) in enc.type_exports {
self.component
.export(name, "", ComponentExportKind::Type, idx);
}
let core_name = func.core_export_name(None);
let idx = self.encode_lift(opts, module, &core_name, func, ty)?;
self.component
.export(export_name, "", ComponentExportKind::Func, idx);
}
WorldItem::Interface(export) => {
let mut interface_exports = Vec::new();
let mut enc = self.root_type_encoder(Some(*export));
for (_, func) in &resolve.interfaces[*export].functions {
let core_name = func.core_export_name(Some(export_name));
let ty = enc.encode_func_type(resolve, func)?;
let func_index =
enc.state.encode_lift(opts, module, &core_name, func, ty)?;
interface_exports.push((
func.name.as_str(),
ComponentExportKind::Func,
func_index,
));
}
if let Some(live) = enc.state.info.live_types.get(export) {
for ty in live {
enc.encode_valtype(resolve, &Type::Id(*ty))?;
}
}
interface_exports.splice(
0..0,
enc.type_exports
.into_iter()
.map(|(idx, name)| (name, ComponentExportKind::Type, idx)),
);
if export_name.is_empty() {
bail!("cannot export an unnamed interface");
}
let instance_index = self.component.instantiate_exports(interface_exports);
let url = resolve.url_of(*export).unwrap_or(String::new());
let idx = self.component.export(
export_name,
&url,
ComponentExportKind::Instance,
instance_index,
);
let prev = self.exported_instances.insert(*export, idx);
assert!(prev.is_none());
}
}
}
Ok(())
}
fn encode_lift(
&mut self,
opts: &'a ComponentEncoder,
module: CustomModule<'_>,
core_name: &str,
func: &Function,
ty: u32,
) -> Result<u32> {
let resolve = &opts.metadata.resolve;
let metadata = match module {
CustomModule::Main => &opts.metadata.metadata,
CustomModule::Adapter(name) => &opts.adapters[name].1,
};
let instance_index = match module {
CustomModule::Main => self.instance_index.expect("instantiated by now"),
CustomModule::Adapter(name) => self.adapter_instances[name],
};
let core_func_index =
self.component
.alias_core_item(instance_index, ExportKind::Func, core_name);
let options = RequiredOptions::for_export(resolve, func);
let encoding = metadata.export_encodings[core_name];
let realloc_index = match module {
CustomModule::Main => self.realloc_index,
CustomModule::Adapter(name) => self.adapter_export_reallocs[name],
};
let mut options = options
.into_iter(encoding, self.memory_index, realloc_index)?
.collect::<Vec<_>>();
if resolve.guest_export_needs_post_return(func) {
let post_return = self.component.alias_core_item(
instance_index,
ExportKind::Func,
&format!("cabi_post_{core_name}"),
);
options.push(CanonicalOption::PostReturn(post_return));
}
let func_index = self.component.lift_func(core_func_index, ty, options);
Ok(func_index)
}
fn encode_shim_instantiation(&mut self) -> Shims<'a> {
let mut signatures = Vec::new();
let mut ret = Shims::default();
let info = &self.info.info;
for core_wasm_name in info.required_imports.keys() {
let import_name = if *core_wasm_name == BARE_FUNC_MODULE_NAME {
None
} else {
Some(*core_wasm_name)
};
let import = &self.info.import_map[&import_name];
ret.append_indirect(
core_wasm_name,
CustomModule::Main,
import,
info.metadata,
&mut signatures,
);
}
for (adapter, (info, _wasm)) in self.info.adapters.iter() {
for (name, _) in info.required_imports.iter() {
let import = &self.info.import_map[&Some(*name)];
ret.append_indirect(
name,
CustomModule::Adapter(adapter),
import,
info.metadata,
&mut signatures,
);
}
let funcs = match self.info.info.adapters_required.get(adapter) {
Some(funcs) => funcs,
None => continue,
};
for (func, ty) in funcs {
let name = ret.list.len().to_string();
log::debug!("shim {name} is adapter `{adapter}::{func}`");
signatures.push(WasmSignature {
params: ty.params().iter().map(to_wasm_type).collect(),
results: ty.results().iter().map(to_wasm_type).collect(),
indirect_params: false,
retptr: false,
});
ret.list.push(Shim {
name,
debug_name: format!("adapt-{adapter}-{func}"),
options: RequiredOptions::MEMORY,
kind: ShimKind::Adapter { adapter, func },
});
}
}
if ret.list.is_empty() {
return ret;
}
for shim in ret.list.iter() {
ret.shim_names.insert(shim.kind, shim.name.clone());
}
assert!(self.shim_instance_index.is_none());
assert!(self.fixups_module_index.is_none());
let mut types = TypeSection::new();
let mut tables = TableSection::new();
let mut functions = FunctionSection::new();
let mut exports = ExportSection::new();
let mut code = CodeSection::new();
let mut sigs = IndexMap::new();
let mut imports_section = ImportSection::new();
let mut elements = ElementSection::new();
let mut func_indexes = Vec::new();
let mut func_names = NameMap::new();
for (i, (sig, shim)) in signatures.iter().zip(&ret.list).enumerate() {
let i = i as u32;
let type_index = *sigs.entry(sig).or_insert_with(|| {
let index = types.len();
types.function(
sig.params.iter().map(to_val_type),
sig.results.iter().map(to_val_type),
);
index
});
functions.function(type_index);
Self::encode_shim_function(type_index, i, &mut code, sig.params.len() as u32);
exports.export(&shim.name, ExportKind::Func, i);
imports_section.import("", &shim.name, EntityType::Function(type_index));
func_indexes.push(i);
func_names.append(i, &shim.debug_name);
}
let mut names = NameSection::new();
names.functions(&func_names);
let table_type = TableType {
element_type: ValType::FuncRef,
minimum: signatures.len() as u32,
maximum: Some(signatures.len() as u32),
};
tables.table(table_type);
exports.export(INDIRECT_TABLE_NAME, ExportKind::Table, 0);
imports_section.import("", INDIRECT_TABLE_NAME, table_type);
elements.active(
None,
&ConstExpr::i32_const(0),
ValType::FuncRef,
Elements::Functions(&func_indexes),
);
let mut shim = Module::new();
shim.section(&types);
shim.section(&functions);
shim.section(&tables);
shim.section(&exports);
shim.section(&code);
shim.section(&names);
let mut fixups = Module::default();
fixups.section(&types);
fixups.section(&imports_section);
fixups.section(&elements);
let shim_module_index = self.component.core_module(&shim);
self.fixups_module_index = Some(self.component.core_module(&fixups));
self.shim_instance_index = Some(self.component.instantiate(shim_module_index, []));
return ret;
fn to_wasm_type(ty: &wasmparser::ValType) -> WasmType {
match ty {
wasmparser::ValType::I32 => WasmType::I32,
wasmparser::ValType::I64 => WasmType::I64,
wasmparser::ValType::F32 => WasmType::F32,
wasmparser::ValType::F64 => WasmType::F64,
_ => unreachable!(),
}
}
}
fn encode_shim_function(
type_index: u32,
func_index: u32,
code: &mut CodeSection,
param_count: u32,
) {
let mut func = wasm_encoder::Function::new(std::iter::empty());
for i in 0..param_count {
func.instruction(&Instruction::LocalGet(i));
}
func.instruction(&Instruction::I32Const(func_index as i32));
func.instruction(&Instruction::CallIndirect {
ty: type_index,
table: 0,
});
func.instruction(&Instruction::End);
code.function(&func);
}
fn encode_indirect_lowerings(&mut self, shims: Shims<'_>) -> Result<()> {
if shims.list.is_empty() {
return Ok(());
}
let shim_instance_index = self
.shim_instance_index
.expect("must have an instantiated shim");
let table_index = self.component.alias_core_item(
shim_instance_index,
ExportKind::Table,
INDIRECT_TABLE_NAME,
);
let mut exports = Vec::new();
exports.push((INDIRECT_TABLE_NAME, ExportKind::Table, table_index));
for shim in shims.list.iter() {
let core_func_index = match &shim.kind {
ShimKind::IndirectLowering {
interface,
indirect_index,
realloc,
encoding,
} => {
let interface = &self.info.import_map[interface];
let name = interface.indirect[*indirect_index].name;
let func_index = match &interface.interface {
Some((interface_id, _url)) => {
let instance_index = self.imported_instances[interface_id];
self.component.alias_func(instance_index, name)
}
None => self.imported_funcs[name],
};
let realloc = match realloc {
CustomModule::Main => self.realloc_index,
CustomModule::Adapter(name) => self.adapter_import_reallocs[name],
};
self.component.lower_func(
func_index,
shim.options
.into_iter(*encoding, self.memory_index, realloc)?,
)
}
ShimKind::Adapter { adapter, func } => self.component.alias_core_item(
self.adapter_instances[adapter],
ExportKind::Func,
func,
),
};
exports.push((shim.name.as_str(), ExportKind::Func, core_func_index));
}
let instance_index = self.component.instantiate_core_exports(exports);
self.component.instantiate(
self.fixups_module_index.expect("must have fixup module"),
[("", ModuleArg::Instance(instance_index))],
);
Ok(())
}
fn instantiate_core_module<'b, A>(&mut self, args: A, info: &ValidatedModule<'_>)
where
A: IntoIterator<Item = (&'b str, ModuleArg)>,
A::IntoIter: ExactSizeIterator,
{
assert!(self.instance_index.is_none());
let instance_index = self
.component
.instantiate(self.module_index.expect("core module encoded"), args);
if info.has_memory {
self.memory_index = Some(self.component.alias_core_item(
instance_index,
ExportKind::Memory,
"memory",
));
}
if let Some(name) = &info.realloc {
self.realloc_index = Some(self.component.alias_core_item(
instance_index,
ExportKind::Func,
name,
));
}
self.instance_index = Some(instance_index);
}
fn instantiate_adapter_modules(&mut self, shims: &Shims<'_>) {
for (name, (info, _wasm)) in self.info.adapters.iter() {
let mut args = Vec::new();
let mut core_exports = Vec::new();
for export_name in info.needs_core_exports.iter() {
let index = self.component.alias_core_item(
self.instance_index
.expect("adaptee index set at this point"),
ExportKind::Func,
export_name,
);
core_exports.push((export_name.as_str(), ExportKind::Func, index));
}
if !core_exports.is_empty() {
let instance = self.component.instantiate_core_exports(core_exports);
args.push((MAIN_MODULE_IMPORT_NAME, ModuleArg::Instance(instance)));
}
if let Some((module, name)) = &info.needs_memory {
for (import_name, _) in info.required_imports.iter() {
assert!(module != import_name);
}
assert!(module != name);
let memory = self.memory_index.unwrap();
let instance = self.component.instantiate_core_exports([(
name.as_str(),
ExportKind::Memory,
memory,
)]);
args.push((module.as_str(), ModuleArg::Instance(instance)));
}
for (import_name, _) in info.required_imports.iter() {
let instance = self.import_instance_to_lowered_core_instance(
CustomModule::Adapter(name),
import_name,
shims,
info.metadata,
);
args.push((import_name, ModuleArg::Instance(instance)));
}
let instance = self.component.instantiate(self.adapter_modules[name], args);
self.adapter_instances.insert(name, instance);
let realloc = info.export_realloc.as_ref().map(|name| {
self.component
.alias_core_item(instance, ExportKind::Func, name)
});
self.adapter_export_reallocs.insert(name, realloc);
let realloc = info.import_realloc.as_ref().map(|name| {
self.component
.alias_core_item(instance, ExportKind::Func, name)
});
self.adapter_import_reallocs.insert(name, realloc);
}
}
}
#[derive(Default)]
struct Shims<'a> {
list: Vec<Shim<'a>>,
shim_names: IndexMap<ShimKind<'a>, String>,
}
struct Shim<'a> {
options: RequiredOptions,
name: String,
debug_name: String,
kind: ShimKind<'a>,
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
enum ShimKind<'a> {
IndirectLowering {
interface: Option<&'a str>,
indirect_index: usize,
realloc: CustomModule<'a>,
encoding: StringEncoding,
},
Adapter {
adapter: &'a str,
func: &'a str,
},
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
enum CustomModule<'a> {
Main,
Adapter(&'a str),
}
impl<'a> Shims<'a> {
fn append_indirect(
&mut self,
core_wasm_module: &'a str,
for_module: CustomModule<'a>,
import: &ImportedInterface<'a>,
metadata: &ModuleMetadata,
sigs: &mut Vec<WasmSignature>,
) {
let interface = if core_wasm_module == BARE_FUNC_MODULE_NAME {
None
} else {
Some(core_wasm_module)
};
for (indirect_index, lowering) in import.indirect.iter().enumerate() {
let shim_name = self.list.len().to_string();
log::debug!(
"shim {shim_name} is import `{core_wasm_module}` lowering {indirect_index} `{}`",
lowering.name
);
sigs.push(lowering.sig.clone());
let encoding = metadata.import_encodings
[&(core_wasm_module.to_string(), lowering.name.to_string())];
self.list.push(Shim {
name: shim_name,
debug_name: format!("indirect-{core_wasm_module}-{}", lowering.name),
options: lowering.options,
kind: ShimKind::IndirectLowering {
interface,
indirect_index,
realloc: for_module,
encoding,
},
});
}
}
}
#[derive(Default)]
pub struct ComponentEncoder {
module: Vec<u8>,
metadata: Bindgen,
validate: bool,
adapters: IndexMap<String, (Vec<u8>, ModuleMetadata, WorldId)>,
}
impl ComponentEncoder {
pub fn module(mut self, module: &[u8]) -> Result<Self> {
let (wasm, metadata) = metadata::decode(module)?;
self.module = wasm;
self.metadata.merge(metadata)?;
Ok(self)
}
pub fn validate(mut self, validate: bool) -> Self {
self.validate = validate;
self
}
pub fn adapter(mut self, name: &str, bytes: &[u8]) -> Result<Self> {
let (wasm, metadata) = metadata::decode(bytes)?;
let world = self.metadata.resolve.merge(metadata.resolve).worlds[metadata.world.index()];
self.adapters
.insert(name.to_string(), (wasm, metadata.metadata, world));
Ok(self)
}
pub fn encode(&self) -> Result<Vec<u8>> {
if self.module.is_empty() {
bail!("a module is required when encoding a component");
}
let world = ComponentWorld::new(self)?;
let mut state = EncodingState {
component: ComponentBuilder::default(),
module_index: None,
instance_index: None,
memory_index: None,
realloc_index: None,
shim_instance_index: None,
fixups_module_index: None,
adapter_modules: IndexMap::new(),
adapter_instances: IndexMap::new(),
adapter_import_reallocs: IndexMap::new(),
adapter_export_reallocs: IndexMap::new(),
type_map: HashMap::new(),
func_type_map: HashMap::new(),
imported_instances: Default::default(),
imported_funcs: Default::default(),
exported_instances: Default::default(),
info: &world,
};
state.encode_imports()?;
state.encode_core_modules();
state.encode_core_instantiation()?;
state.encode_exports(self, CustomModule::Main)?;
for name in self.adapters.keys() {
state.encode_exports(self, CustomModule::Adapter(name))?;
}
let bytes = state.component.finish();
if self.validate {
let mut validator = Validator::new_with_features(WasmFeatures {
component_model: true,
..Default::default()
});
validator
.validate_all(&bytes)
.context("failed to validate component output")?;
}
Ok(bytes)
}
}