use std::collections::HashMap;
use cviz::model::{InterfaceType, TypeArena, ValueType, ValueTypeId};
use wasm_encoder::{
Alias, CanonicalFunctionSection, CanonicalOption, Component, ComponentAliasSection,
ComponentExportKind, ComponentExportSection, ComponentImportSection, ComponentInstanceSection,
ComponentOuterAliasKind, ComponentSectionId, ComponentTypeRef, ComponentTypeSection,
ComponentValType, ExportKind, InstanceSection, InstanceType, ModuleSection, PrimitiveValType,
RawSection,
};
use super::dispatch::{build_dispatch_module, build_mem_module};
use super::encoders::{encode_comp_cv, InstTypeCtx};
use super::mem_layout::MemoryLayoutBuilder;
use crate::adapter::abi::WitBridge;
use crate::adapter::filter::FilteredSections;
use crate::adapter::func::AdapterFunc;
use crate::adapter::indices::ComponentIndices;
use crate::adapter::names;
fn build_handler_inst_type(
ctx: &mut InstTypeCtx,
inst: &mut InstanceType,
funcs: &[AdapterFunc],
arena: &TypeArena,
) -> anyhow::Result<()> {
let mut pp_cvs: Vec<Vec<ComponentValType>> = Vec::new();
let mut pr_cv: Vec<Option<ComponentValType>> = Vec::new();
for func in funcs.iter() {
let mut p_cvs = Vec::new();
for &id in &func.param_type_ids {
p_cvs.push(ctx.encode_cv(id, inst, arena)?);
}
let r_cv = func
.result_type_id
.map(|id| ctx.encode_cv(id, inst, arena))
.transpose()?;
pp_cvs.push(p_cvs);
pr_cv.push(r_cv);
}
for (fi, func) in funcs.iter().enumerate() {
let params: Vec<(&str, ComponentValType)> = func
.param_names
.iter()
.zip(pp_cvs[fi].iter())
.map(|(n, &cv)| (n.as_str(), cv))
.collect();
let fn_ty_local_idx = inst.type_count();
let mut fty = inst.ty().function();
if func.is_async {
fty.async_(true);
}
fty.params(params.iter().copied()).result(pr_cv[fi]);
inst.export(&func.name, ComponentTypeRef::Func(fn_ty_local_idx));
}
Ok(())
}
fn emit_hook_inst_types(
types: &mut ComponentTypeSection,
indices: &mut ComponentIndices,
has_before: bool,
has_after: bool,
has_blocking: bool,
) -> (Option<u32>, Option<u32>, Option<u32>) {
use crate::contract::{TIER1_AFTER_FNS, TIER1_BEFORE_FNS, TIER1_BLOCKING_FNS};
let string_cv = ComponentValType::Primitive(PrimitiveValType::String);
let bool_cv = ComponentValType::Primitive(PrimitiveValType::Bool);
let mut emit_hook_ty = |export_name: &str, result: Option<ComponentValType>| {
let idx = indices.alloc_ty();
let mut inst = InstanceType::new();
inst.ty()
.function()
.async_(true)
.params([("name", string_cv)])
.result(result);
inst.export(export_name, ComponentTypeRef::Func(0));
types.instance(&inst);
idx
};
let before_inst_ty = has_before.then(|| emit_hook_ty(TIER1_BEFORE_FNS[0], None));
let after_inst_ty = has_after.then(|| emit_hook_ty(TIER1_AFTER_FNS[0], None));
let blocking_inst_ty = has_blocking.then(|| emit_hook_ty(TIER1_BLOCKING_FNS[0], Some(bool_cv)));
(before_inst_ty, after_inst_ty, blocking_inst_ty)
}
fn emit_hook_imports(
imports: &mut ComponentImportSection,
indices: &mut ComponentIndices,
before_ty: Option<u32>,
after_ty: Option<u32>,
blocking_ty: Option<u32>,
) -> (Option<u32>, Option<u32>, Option<u32>) {
use crate::contract::{
versioned_interface, TIER1_AFTER, TIER1_BEFORE, TIER1_BLOCKING, TIER1_VERSION,
};
let mut import_hook = |ty_idx: u32, iface: &str| {
let idx = indices.alloc_inst();
imports.import(
&versioned_interface(iface, TIER1_VERSION),
ComponentTypeRef::Instance(ty_idx),
);
idx
};
let before_inst = before_ty.map(|ty| import_hook(ty, TIER1_BEFORE));
let after_inst = after_ty.map(|ty| import_hook(ty, TIER1_AFTER));
let blocking_inst = blocking_ty.map(|ty| import_hook(ty, TIER1_BLOCKING));
(before_inst, after_inst, blocking_inst)
}
fn emit_func_aliases(
aliases: &mut ComponentAliasSection,
indices: &mut ComponentIndices,
funcs: &[AdapterFunc],
handler_inst: u32,
before_inst: Option<u32>,
after_inst: Option<u32>,
blocking_inst: Option<u32>,
) -> (u32, Option<u32>, Option<u32>, Option<u32>) {
let handler_func_base = indices.func;
let mut alias_func = |inst_idx: u32, name: &str| {
let idx = indices.alloc_func();
aliases.alias(Alias::InstanceExport {
instance: inst_idx,
kind: ComponentExportKind::Func,
name,
});
idx
};
for func in funcs {
alias_func(handler_inst, &func.name);
}
use crate::contract::{TIER1_AFTER_FNS, TIER1_BEFORE_FNS, TIER1_BLOCKING_FNS};
let before_comp_func = before_inst.map(|i| alias_func(i, TIER1_BEFORE_FNS[0]));
let after_comp_func = after_inst.map(|i| alias_func(i, TIER1_AFTER_FNS[0]));
let blocking_comp_func = blocking_inst.map(|i| alias_func(i, TIER1_BLOCKING_FNS[0]));
(
handler_func_base,
before_comp_func,
after_comp_func,
blocking_comp_func,
)
}
#[allow(clippy::too_many_arguments)]
fn emit_imports_from_split(
component: &mut Component,
target_interface: &str,
funcs: &[AdapterFunc],
has_before: bool,
has_after: bool,
has_blocking: bool,
arena: &TypeArena,
iface_ty: &InterfaceType,
split: &FilteredSections,
indices: &mut ComponentIndices,
) -> anyhow::Result<ImportsOutcome> {
for (section_kind, data) in &split.raw_sections {
component.section(&RawSection {
id: *section_kind as u8,
data,
});
}
indices.ty = split.type_count;
indices.inst = split.instance_count;
if let Some(handler_idx) = split
.import_names
.iter()
.position(|n| n == target_interface)
{
emit_imports_consumer_split(
component,
funcs,
has_before,
has_after,
has_blocking,
arena,
iface_ty,
split,
indices,
handler_idx as u32,
)
} else {
emit_imports_provider_split(
component,
target_interface,
funcs,
has_before,
has_after,
has_blocking,
arena,
iface_ty,
split,
indices,
)
}
}
#[allow(clippy::too_many_arguments)]
fn emit_imports_consumer_split(
component: &mut Component,
funcs: &[AdapterFunc],
has_before: bool,
has_after: bool,
has_blocking: bool,
arena: &TypeArena,
iface_ty: &InterfaceType,
split: &FilteredSections,
indices: &mut ComponentIndices,
handler_inst: u32,
) -> anyhow::Result<ImportsOutcome> {
let mut types = ComponentTypeSection::new();
let (before_inst_ty, after_inst_ty, blocking_inst_ty) =
emit_hook_inst_types(&mut types, indices, has_before, has_after, has_blocking);
component.section(&types);
let mut imports = ComponentImportSection::new();
let (before_inst, after_inst, blocking_inst) = emit_hook_imports(
&mut imports,
indices,
before_inst_ty,
after_inst_ty,
blocking_inst_ty,
);
component.section(&imports);
let mut aliases = ComponentAliasSection::new();
let (handler_func_base, before_comp_func, after_comp_func, blocking_comp_func) =
emit_func_aliases(
&mut aliases,
indices,
funcs,
handler_inst,
before_inst,
after_inst,
blocking_inst,
);
component.section(&aliases);
let mut inst_ctx = InstTypeCtx::new();
{
let mut dummy_inst = InstanceType::new();
build_handler_inst_type(&mut inst_ctx, &mut dummy_inst, funcs, arena)?;
}
let mut comp_aliased_types: HashMap<ValueTypeId, u32> = HashMap::new();
let mut comp_resource_indices: Vec<u32> = Vec::new();
let mut export_aliases = ComponentAliasSection::new();
for (_vid, export_name, _res_local, _own_local) in &inst_ctx.resource_exports {
let comp_idx = indices.alloc_ty();
export_aliases.alias(Alias::InstanceExport {
instance: handler_inst,
kind: ComponentExportKind::Type,
name: export_name,
});
comp_resource_indices.push(comp_idx);
}
if let InterfaceType::Instance(inst) = iface_ty {
for (export_name, &vid) in &inst.type_exports {
if !matches!(
arena.lookup_val(vid),
ValueType::Resource(_) | ValueType::AsyncHandle
) {
let comp_idx = indices.alloc_ty();
export_aliases.alias(Alias::InstanceExport {
instance: handler_inst,
kind: ComponentExportKind::Type,
name: export_name,
});
comp_aliased_types.insert(vid, comp_idx);
}
}
}
if indices.ty > split.type_count {
component.section(&export_aliases);
}
Ok(ImportsOutcome {
handler_func_base,
before_comp_func,
after_comp_func,
blocking_comp_func,
inst_ctx,
comp_resource_indices,
comp_aliased_types,
})
}
#[allow(clippy::too_many_arguments)]
fn emit_imports_provider_split(
component: &mut Component,
target_interface: &str,
funcs: &[AdapterFunc],
has_before: bool,
has_after: bool,
has_blocking: bool,
arena: &TypeArena,
iface_ty: &InterfaceType,
split: &FilteredSections,
indices: &mut ComponentIndices,
) -> anyhow::Result<ImportsOutcome> {
let mut outer_aliased: HashMap<ValueTypeId, u32> = HashMap::new();
if let InterfaceType::Instance(inst_iface) = iface_ty {
for (name, &vid) in &inst_iface.type_exports {
if let Some(&comp_idx) = split.aliased_type_exports.get(name) {
outer_aliased.insert(vid, comp_idx);
}
}
}
let handler_inst_ty: u32;
let inst_ctx: InstTypeCtx;
let mut types = ComponentTypeSection::new();
{
let outer_res_map: HashMap<ValueTypeId, u32> = outer_aliased
.iter()
.filter(|(&vid, _)| {
matches!(
arena.lookup_val(vid),
ValueType::Resource(_) | ValueType::AsyncHandle
)
})
.map(|(k, v)| (*k, *v))
.collect();
let mut ctx = if outer_res_map.is_empty() {
InstTypeCtx::new()
} else {
InstTypeCtx::with_outer_resources(outer_res_map)
};
let mut inst = InstanceType::new();
for (&vid, &comp_idx) in &outer_aliased {
let local_idx = inst.type_count();
inst.alias(Alias::Outer {
kind: ComponentOuterAliasKind::Type,
count: 1,
index: comp_idx,
});
ctx.alias_locals.insert(vid, local_idx);
}
build_handler_inst_type(&mut ctx, &mut inst, funcs, arena)?;
handler_inst_ty = indices.alloc_ty();
inst_ctx = ctx;
types.instance(&inst);
}
let (before_inst_ty, after_inst_ty, blocking_inst_ty) =
emit_hook_inst_types(&mut types, indices, has_before, has_after, has_blocking);
component.section(&types);
let mut imports = ComponentImportSection::new();
let handler_inst = indices.alloc_inst();
imports.import(
target_interface,
ComponentTypeRef::Instance(handler_inst_ty),
);
let (before_inst, after_inst, blocking_inst) = emit_hook_imports(
&mut imports,
indices,
before_inst_ty,
after_inst_ty,
blocking_inst_ty,
);
component.section(&imports);
let mut aliases = ComponentAliasSection::new();
let (handler_func_base, before_comp_func, after_comp_func, blocking_comp_func) =
emit_func_aliases(
&mut aliases,
indices,
funcs,
handler_inst,
before_inst,
after_inst,
blocking_inst,
);
let mut comp_resource_indices: Vec<u32> = Vec::new();
for (vid, export_name, _res_local, _own_local) in &inst_ctx.resource_exports {
if let Some(&comp_idx) = outer_aliased.get(vid) {
comp_resource_indices.push(comp_idx);
} else {
let comp_res_idx = indices.alloc_ty();
aliases.alias(Alias::InstanceExport {
instance: handler_inst,
kind: ComponentExportKind::Type,
name: export_name,
});
comp_resource_indices.push(comp_res_idx);
}
}
let mut comp_aliased_types: HashMap<ValueTypeId, u32> = HashMap::new();
if let InterfaceType::Instance(inst_iface) = iface_ty {
for &vid in inst_iface.type_exports.values() {
if !matches!(
arena.lookup_val(vid),
ValueType::Resource(_) | ValueType::AsyncHandle
) {
if let Some(&comp_idx) = outer_aliased.get(&vid) {
comp_aliased_types.insert(vid, comp_idx);
}
}
}
}
component.section(&aliases);
Ok(ImportsOutcome {
handler_func_base,
before_comp_func,
after_comp_func,
blocking_comp_func,
inst_ctx,
comp_resource_indices,
comp_aliased_types,
})
}
struct ImportsOutcome {
handler_func_base: u32,
before_comp_func: Option<u32>,
after_comp_func: Option<u32>,
blocking_comp_func: Option<u32>,
inst_ctx: InstTypeCtx,
comp_resource_indices: Vec<u32>,
comp_aliased_types: HashMap<ValueTypeId, u32>,
}
struct MemoryLayout {
event_ptr: u32,
block_result_ptr: Option<u32>,
needs_realloc: bool,
bump_start: u32,
has_async_machinery: bool,
}
fn compute_memory_layout(
funcs: &[AdapterFunc],
mut layout: MemoryLayoutBuilder,
has_before: bool,
has_after: bool,
has_blocking: bool,
) -> MemoryLayout {
let has_async = funcs.iter().any(|f| f.is_async);
let has_async_machinery = has_async || has_before || has_after || has_blocking;
let event_ptr = layout.alloc_event_slot();
let block_result_ptr = has_blocking.then(|| layout.alloc_block_result());
let needs_realloc = funcs.iter().any(|f| f.canon_needs_realloc());
let bump_start = layout.finish_as_bump_start();
MemoryLayout {
event_ptr,
block_result_ptr,
needs_realloc,
bump_start,
has_async_machinery,
}
}
struct MemoryProviderOutcome {
mem_core_mem: u32,
mem_core_realloc: Option<u32>,
}
fn emit_memory_provider(
component: &mut Component,
indices: &mut ComponentIndices,
needs_realloc: bool,
bump_start: u32,
) -> MemoryProviderOutcome {
let core_memory_count: u32 = 0;
{
let mem_module = build_mem_module(needs_realloc, bump_start);
component.section(&ModuleSection(&mem_module));
}
let mem_core_inst: u32;
{
let mut instances = InstanceSection::new();
mem_core_inst = indices.alloc_core_inst();
instances.instantiate::<[(&str, wasm_encoder::ModuleArg); 0], &str>(0u32, []);
component.section(&instances);
}
let mem_core_mem: u32;
let mem_core_realloc: Option<u32>;
{
let mut aliases = ComponentAliasSection::new();
mem_core_mem = core_memory_count;
aliases.alias(Alias::CoreInstanceExport {
instance: mem_core_inst,
kind: ExportKind::Memory,
name: names::ENV_MEMORY,
});
mem_core_realloc = if needs_realloc {
let idx = indices.alloc_core_func();
aliases.alias(Alias::CoreInstanceExport {
instance: mem_core_inst,
kind: ExportKind::Func,
name: names::ENV_REALLOC,
});
Some(idx)
} else {
None
};
component.section(&aliases);
}
MemoryProviderOutcome {
mem_core_mem,
mem_core_realloc,
}
}
struct CanonLowerOutcome {
core_before_func: Option<u32>,
core_after_func: Option<u32>,
core_blocking_func: Option<u32>,
core_handler_func_base: u32,
core_waitable_new: Option<u32>,
core_waitable_join: Option<u32>,
core_waitable_wait: Option<u32>,
core_waitable_drop: Option<u32>,
core_subtask_drop: Option<u32>,
core_task_return_funcs: Vec<Option<u32>>,
}
#[allow(clippy::too_many_arguments)]
fn emit_canon_lower(
component: &mut Component,
indices: &mut ComponentIndices,
funcs: &[AdapterFunc],
handler_func_base: u32,
before_comp_func: Option<u32>,
after_comp_func: Option<u32>,
blocking_comp_func: Option<u32>,
mem_core_mem: u32,
mem_core_realloc: Option<u32>,
has_async_machinery: bool,
comp_result_cvs: &[Option<ComponentValType>],
) -> CanonLowerOutcome {
let core_waitable_new: Option<u32>;
let core_waitable_join: Option<u32>;
let core_waitable_wait: Option<u32>;
let core_waitable_drop: Option<u32>;
let core_subtask_drop: Option<u32>;
let mut canons = CanonicalFunctionSection::new();
let mut lower_hook = |comp_f: u32| {
let idx = indices.alloc_core_func();
canons.lower(
comp_f,
[
CanonicalOption::Async,
CanonicalOption::Memory(mem_core_mem),
CanonicalOption::UTF8,
],
);
idx
};
let core_before_func = before_comp_func.map(&mut lower_hook);
let core_after_func = after_comp_func.map(&mut lower_hook);
let core_blocking_func = blocking_comp_func.map(&mut lower_hook);
let core_handler_func_base = indices.core_func;
for (i, func) in funcs.iter().enumerate() {
indices.core_func += 1;
let mut opts: Vec<CanonicalOption> = Vec::new();
if func.is_async {
opts.push(CanonicalOption::Async);
}
if func.canon_needs_memory() {
opts.push(CanonicalOption::Memory(mem_core_mem));
}
if func.canon_needs_utf8() {
opts.push(CanonicalOption::UTF8);
}
if func.canon_needs_realloc() {
if let Some(ra) = mem_core_realloc {
opts.push(CanonicalOption::Realloc(ra));
}
}
canons.lower(handler_func_base + i as u32, opts);
}
if has_async_machinery {
core_waitable_new = Some(indices.alloc_core_func());
canons.waitable_set_new();
core_waitable_join = Some(indices.alloc_core_func());
canons.waitable_join();
core_waitable_wait = Some(indices.alloc_core_func());
canons.waitable_set_wait(false, mem_core_mem);
core_waitable_drop = Some(indices.alloc_core_func());
canons.waitable_set_drop();
core_subtask_drop = Some(indices.alloc_core_func());
canons.subtask_drop();
} else {
core_waitable_new = None;
core_waitable_join = None;
core_waitable_wait = None;
core_waitable_drop = None;
core_subtask_drop = None;
}
let core_task_return_funcs: Vec<Option<u32>> = funcs
.iter()
.enumerate()
.map(|(fi, func)| {
if func.is_async {
let idx = indices.alloc_core_func();
let tr_result_cv = comp_result_cvs[fi];
let mut opts: Vec<CanonicalOption> = Vec::new();
if func.canon_needs_memory() {
opts.push(CanonicalOption::Memory(mem_core_mem));
}
if func.canon_needs_utf8() {
opts.push(CanonicalOption::UTF8);
}
canons.task_return(tr_result_cv, opts);
Some(idx)
} else {
None
}
})
.collect();
component.section(&canons);
CanonLowerOutcome {
core_before_func,
core_after_func,
core_blocking_func,
core_handler_func_base,
core_waitable_new,
core_waitable_join,
core_waitable_wait,
core_waitable_drop,
core_subtask_drop,
core_task_return_funcs,
}
}
struct DispatchPhaseOutcome {
dispatch_core_inst: u32,
}
fn build_env_exports(
canon_lower: &CanonLowerOutcome,
funcs: &[AdapterFunc],
mem_core_mem: u32,
) -> Vec<(String, ExportKind, u32)> {
use crate::contract::{
TIER1_AFTER_ENV_SLOTS, TIER1_BEFORE_ENV_SLOTS, TIER1_BLOCKING_ENV_SLOTS,
};
let mut env_exports: Vec<(String, ExportKind, u32)> = Vec::new();
env_exports.push((
names::ENV_MEMORY.to_string(),
ExportKind::Memory,
mem_core_mem,
));
if let Some(idx) = canon_lower.core_before_func {
env_exports.push((TIER1_BEFORE_ENV_SLOTS[0].to_string(), ExportKind::Func, idx));
}
if let Some(idx) = canon_lower.core_after_func {
env_exports.push((TIER1_AFTER_ENV_SLOTS[0].to_string(), ExportKind::Func, idx));
}
if let Some(idx) = canon_lower.core_blocking_func {
env_exports.push((
TIER1_BLOCKING_ENV_SLOTS[0].to_string(),
ExportKind::Func,
idx,
));
}
for (i, _) in funcs.iter().enumerate() {
env_exports.push((
names::env_handler_fn(i),
ExportKind::Func,
canon_lower.core_handler_func_base + i as u32,
));
}
if let Some(idx) = canon_lower.core_waitable_new {
env_exports.push((names::ENV_WAITABLE_NEW.to_string(), ExportKind::Func, idx));
}
if let Some(idx) = canon_lower.core_waitable_join {
env_exports.push((names::ENV_WAITABLE_JOIN.to_string(), ExportKind::Func, idx));
}
if let Some(idx) = canon_lower.core_waitable_wait {
env_exports.push((names::ENV_WAITABLE_WAIT.to_string(), ExportKind::Func, idx));
}
if let Some(idx) = canon_lower.core_waitable_drop {
env_exports.push((names::ENV_WAITABLE_DROP.to_string(), ExportKind::Func, idx));
}
if let Some(idx) = canon_lower.core_subtask_drop {
env_exports.push((names::ENV_SUBTASK_DROP.to_string(), ExportKind::Func, idx));
}
for (i, tr_idx) in canon_lower.core_task_return_funcs.iter().enumerate() {
if let Some(idx) = tr_idx {
env_exports.push((names::env_task_return_fn(i), ExportKind::Func, *idx));
}
}
env_exports
}
#[allow(clippy::too_many_arguments)]
fn emit_dispatch_phase(
component: &mut Component,
indices: &mut ComponentIndices,
funcs: &[AdapterFunc],
has_before: bool,
has_after: bool,
has_blocking: bool,
layout: &MemoryLayout,
mem_core_mem: u32,
canon_lower: &CanonLowerOutcome,
bridge: &WitBridge,
) -> anyhow::Result<DispatchPhaseOutcome> {
{
let dispatch_bytes = build_dispatch_module(
funcs,
has_before,
has_after,
has_blocking,
layout.event_ptr,
layout.block_result_ptr,
bridge,
)?;
component.section(&RawSection {
id: ComponentSectionId::CoreModule as u8,
data: &dispatch_bytes,
});
}
let dispatch_core_inst: u32;
{
let mut instances = InstanceSection::new();
let env_inst = indices.alloc_core_inst();
let env_exports = build_env_exports(canon_lower, funcs, mem_core_mem);
instances.export_items(
env_exports
.iter()
.map(|(n, k, i)| (n.as_str(), *k, *i))
.collect::<Vec<_>>(),
);
dispatch_core_inst = indices.alloc_core_inst();
instances.instantiate(
1u32,
[(
names::ENV_INSTANCE,
wasm_encoder::ModuleArg::Instance(env_inst),
)],
);
component.section(&instances);
}
Ok(DispatchPhaseOutcome { dispatch_core_inst })
}
struct CanonLiftOutcome {
wrapped_func_base: u32,
}
#[allow(clippy::too_many_arguments)]
fn emit_canon_lift_phase(
component: &mut Component,
indices: &ComponentIndices,
funcs: &[AdapterFunc],
dispatch_core_inst: u32,
target_func_ty_base: u32,
mem_core_mem: u32,
mem_core_realloc: Option<u32>,
needs_realloc: bool,
) -> CanonLiftOutcome {
let core_wrapper_func_base: u32;
{
let mut aliases = ComponentAliasSection::new();
core_wrapper_func_base = indices.core_func;
for func in funcs {
aliases.alias(Alias::CoreInstanceExport {
instance: dispatch_core_inst,
kind: ExportKind::Func,
name: &func.name,
});
}
component.section(&aliases);
}
let wrapped_func_base: u32;
{
let mut canons = CanonicalFunctionSection::new();
wrapped_func_base = indices.func;
for (i, func) in funcs.iter().enumerate() {
let mut opts: Vec<CanonicalOption> = if func.is_async {
vec![CanonicalOption::Async]
} else {
vec![]
};
if func.canon_needs_memory() {
opts.push(CanonicalOption::Memory(mem_core_mem));
}
if func.canon_needs_utf8() {
opts.push(CanonicalOption::UTF8);
}
if needs_realloc && func.canon_needs_realloc() {
if let Some(ra) = mem_core_realloc {
opts.push(CanonicalOption::Realloc(ra));
}
}
canons.lift(
core_wrapper_func_base + i as u32,
target_func_ty_base + i as u32,
opts,
);
}
component.section(&canons);
}
CanonLiftOutcome { wrapped_func_base }
}
#[allow(clippy::too_many_arguments)]
fn emit_export_phase(
component: &mut Component,
indices: &ComponentIndices,
target_interface: &str,
funcs: &[AdapterFunc],
iface_ty: &InterfaceType,
split: &FilteredSections,
inst_ctx: &InstTypeCtx,
comp_resource_indices: &[u32],
comp_aliased_types: &HashMap<ValueTypeId, u32>,
wrapped_func_base: u32,
) {
let export_inst: u32;
{
let mut comp_instances = ComponentInstanceSection::new();
export_inst = indices.inst;
let mut export_items: Vec<(&str, ComponentExportKind, u32)> = Vec::new();
let handler_from_split = split.import_names.iter().any(|n| n == target_interface);
if handler_from_split {
for (i, (_vid, export_name, _res_local, _own_local)) in
inst_ctx.resource_exports.iter().enumerate()
{
export_items.push((
export_name,
ComponentExportKind::Type,
comp_resource_indices[i],
));
}
if let InterfaceType::Instance(inst) = iface_ty {
for (export_name, &vid) in &inst.type_exports {
if let Some(&comp_idx) = comp_aliased_types.get(&vid) {
export_items.push((export_name, ComponentExportKind::Type, comp_idx));
}
}
}
}
for (i, func) in funcs.iter().enumerate() {
export_items.push((
func.name.as_str(),
ComponentExportKind::Func,
wrapped_func_base + i as u32,
));
}
comp_instances.export_items(export_items);
component.section(&comp_instances);
}
{
let mut exports = ComponentExportSection::new();
exports.export(
target_interface,
ComponentExportKind::Instance,
export_inst,
None,
);
component.section(&exports);
}
}
struct HandlerTypesOutcome {
target_func_ty_base: u32,
comp_result_cvs: Vec<Option<ComponentValType>>,
}
struct FuncSig {
params: Vec<(String, ComponentValType)>,
result: Option<ComponentValType>,
}
impl FuncSig {
fn new(params: Vec<(String, ComponentValType)>, result: Option<ComponentValType>) -> FuncSig {
FuncSig { params, result }
}
}
fn emit_handler_resource_types(
component: &mut Component,
funcs: &[AdapterFunc],
arena: &TypeArena,
inst_ctx: &InstTypeCtx,
comp_resource_indices: &[u32],
comp_aliased_types: &HashMap<ValueTypeId, u32>,
indices: &mut ComponentIndices,
) -> anyhow::Result<HandlerTypesOutcome> {
let comp_own_by_vid: HashMap<ValueTypeId, u32>;
{
let mut own_types = ComponentTypeSection::new();
let mut own_map: HashMap<ValueTypeId, u32> = HashMap::new();
for (i, (vid, _export_name, _res_local, _own_local)) in
inst_ctx.resource_exports.iter().enumerate()
{
let comp_res_idx = comp_resource_indices[i];
let own_idx = indices.alloc_ty();
own_types.defined_type().own(comp_res_idx);
own_map.insert(*vid, own_idx);
}
comp_own_by_vid = own_map;
if !inst_ctx.resource_exports.is_empty() {
component.section(&own_types);
}
}
let target_func_ty_base: u32;
let comp_result_cvs: Vec<Option<ComponentValType>>;
{
let mut func_types = ComponentTypeSection::new();
let mut comp_cv_cache: HashMap<ValueTypeId, u32> = HashMap::new();
for (&vid, &comp_idx) in comp_aliased_types {
if !matches!(
arena.lookup_val(vid),
ValueType::Resource(_) | ValueType::AsyncHandle
) {
comp_cv_cache.insert(vid, comp_idx);
}
}
let mut pre_encoded: Vec<FuncSig> = Vec::new();
for func in funcs.iter() {
let mut params: Vec<(String, ComponentValType)> = Vec::new();
for (n, &id) in func.param_names.iter().zip(func.param_type_ids.iter()) {
let cv = encode_comp_cv(
id,
arena,
&mut func_types,
&mut indices.ty,
&comp_own_by_vid,
&mut comp_cv_cache,
)?;
params.push((n.clone(), cv));
}
let result_cv = func
.result_type_id
.map(|id| {
encode_comp_cv(
id,
arena,
&mut func_types,
&mut indices.ty,
&comp_own_by_vid,
&mut comp_cv_cache,
)
})
.transpose()?;
pre_encoded.push(FuncSig::new(params, result_cv));
}
target_func_ty_base = indices.ty;
let mut result_cvs: Vec<Option<ComponentValType>> = Vec::new();
for (func, FuncSig { params, result }) in funcs.iter().zip(pre_encoded) {
indices.ty += 1;
result_cvs.push(result);
let mut fty = func_types.function();
if func.is_async {
fty.async_(true);
}
fty.params(params.iter().map(|(n, cv)| (n.as_str(), *cv)))
.result(result);
}
comp_result_cvs = result_cvs;
component.section(&func_types);
}
let _ = comp_own_by_vid;
Ok(HandlerTypesOutcome {
target_func_ty_base,
comp_result_cvs,
})
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn build_adapter_bytes(
target_interface: &str,
funcs: &[AdapterFunc],
has_before: bool,
has_after: bool,
has_blocking: bool,
arena: &TypeArena,
iface_ty: &InterfaceType,
split: &FilteredSections,
layout: MemoryLayoutBuilder,
bridge: &WitBridge,
) -> anyhow::Result<Vec<u8>> {
let mut component = Component::new();
let mut indices = ComponentIndices::default();
let ImportsOutcome {
handler_func_base,
before_comp_func,
after_comp_func,
blocking_comp_func,
inst_ctx,
comp_resource_indices,
comp_aliased_types,
} = emit_imports_from_split(
&mut component,
target_interface,
funcs,
has_before,
has_after,
has_blocking,
arena,
iface_ty,
split,
&mut indices,
)?;
let HandlerTypesOutcome {
target_func_ty_base,
comp_result_cvs,
} = emit_handler_resource_types(
&mut component,
funcs,
arena,
&inst_ctx,
&comp_resource_indices,
&comp_aliased_types,
&mut indices,
)?;
let layout = compute_memory_layout(funcs, layout, has_before, has_after, has_blocking);
let MemoryProviderOutcome {
mem_core_mem,
mem_core_realloc,
} = emit_memory_provider(
&mut component,
&mut indices,
layout.needs_realloc,
layout.bump_start,
);
let canon_lower = emit_canon_lower(
&mut component,
&mut indices,
funcs,
handler_func_base,
before_comp_func,
after_comp_func,
blocking_comp_func,
mem_core_mem,
mem_core_realloc,
layout.has_async_machinery,
&comp_result_cvs,
);
let DispatchPhaseOutcome { dispatch_core_inst } = emit_dispatch_phase(
&mut component,
&mut indices,
funcs,
has_before,
has_after,
has_blocking,
&layout,
mem_core_mem,
&canon_lower,
bridge,
)?;
let CanonLiftOutcome { wrapped_func_base } = emit_canon_lift_phase(
&mut component,
&indices,
funcs,
dispatch_core_inst,
target_func_ty_base,
mem_core_mem,
mem_core_realloc,
layout.needs_realloc,
);
emit_export_phase(
&mut component,
&indices,
target_interface,
funcs,
iface_ty,
split,
&inst_ctx,
&comp_resource_indices,
&comp_aliased_types,
wrapped_func_base,
);
Ok(component.finish())
}