use crate::ir::component::idx_spaces::Space;
use crate::ir::component::refs::{Depth, GetCompRefs, GetItemRef, GetTypeRefs, IndexedRef};
use crate::ir::component::visitor::utils::VisitCtxInner;
use crate::ir::component::visitor::{ResolvedItem, VisitCtx};
use crate::Component;
use wasmparser::{
ComponentAlias, ComponentDefinedType, ComponentFuncType, ComponentType, ComponentValType,
InstanceTypeDeclaration, PrimitiveValType,
};
#[derive(Debug, Clone)]
pub enum ConcreteType<'a> {
Instance(Vec<(&'a str, ConcreteFuncType<'a>)>),
Func(ConcreteFuncType<'a>),
Resource,
}
#[derive(Debug, Clone)]
pub struct ConcreteFuncType<'a> {
pub params: Vec<(&'a str, ConcreteValType<'a>)>,
pub result: Option<ConcreteValType<'a>>,
}
#[derive(Debug, Clone)]
pub enum ConcreteValType<'a> {
Primitive(PrimitiveValType),
Record(Vec<(&'a str, Box<ConcreteValType<'a>>)>),
Variant(Vec<(&'a str, Option<Box<ConcreteValType<'a>>>)>),
List(Box<ConcreteValType<'a>>),
Tuple(Vec<ConcreteValType<'a>>),
Option(Box<ConcreteValType<'a>>),
Result {
ok: Option<Box<ConcreteValType<'a>>>,
err: Option<Box<ConcreteValType<'a>>>,
},
Flags(Vec<&'a str>),
Enum(Vec<&'a str>),
Map(Box<ConcreteValType<'a>>, Box<ConcreteValType<'a>>),
FixedSizeList(Box<ConcreteValType<'a>>, u32),
Resource,
AsyncHandle,
}
impl<'a> Component<'a> {
pub fn concretize_import(&'a self, name: &str) -> Option<ConcreteType<'a>> {
match self.resolve_named_import(name)? {
ResolvedItem::CompType(_, ty) => concretize_comp_type(self, ty),
_ => None,
}
}
pub fn concretize_export(&'a self, name: &str) -> Option<ConcreteType<'a>> {
match self.resolve_named_export(name)? {
ResolvedItem::CompType(_, ty) => concretize_comp_type(self, ty),
_ => None,
}
}
fn enter_type_scope(&'a self, ty: &'a ComponentType<'a>) -> VisitCtx<'a> {
let mut inner = VisitCtxInner::new(self);
inner.push_component(self);
inner.maybe_enter_scope(ty);
VisitCtx { inner }
}
}
fn concretize_comp_type<'a>(
comp: &'a Component<'a>,
ty: &'a ComponentType<'a>,
) -> Option<ConcreteType<'a>> {
match ty {
ComponentType::Instance(decls) => {
let cx = comp.enter_type_scope(ty);
Some(ConcreteType::Instance(concretize_instance_decls(
comp, decls, &cx,
)))
}
ComponentType::Func(ft) => {
let cx = comp.enter_type_scope(ty);
Some(ConcreteType::Func(concretize_func_ty(ft, comp, &cx)))
}
ComponentType::Resource { .. } => Some(ConcreteType::Resource),
_ => None,
}
}
fn concretize_instance_decls<'a>(
comp: &'a Component<'a>,
decls: &'a [InstanceTypeDeclaration<'a>],
cx: &VisitCtx<'a>,
) -> Vec<(&'a str, ConcreteFuncType<'a>)> {
let mut funcs = vec![];
for decl in decls {
if let InstanceTypeDeclaration::Export { name, .. } = decl {
if let Some(type_ref) = decl.get_type_refs().first() {
let resolved = cx.resolve(&type_ref.ref_);
if let Some(ft) = resolve_and_concretize_func(resolved, comp, cx) {
funcs.push((name.0, ft));
}
}
}
}
funcs
}
fn resolve_and_concretize_func<'a>(
resolved: ResolvedItem<'a, 'a>,
comp: &'a Component<'a>,
cx: &VisitCtx<'a>,
) -> Option<ConcreteFuncType<'a>> {
match resolved {
ResolvedItem::CompType(_, ComponentType::Func(ft)) => {
Some(concretize_func_ty(ft, comp, cx))
}
ResolvedItem::Alias(_, alias @ ComponentAlias::Outer { .. }) => {
resolve_and_concretize_func(cx.resolve(&alias.get_item_ref().ref_), comp, cx)
}
ResolvedItem::Alias(
_,
ComponentAlias::InstanceExport {
instance_index,
name,
..
},
) => {
let nested_comp = resolve_instantiated_comp(comp, *instance_index)?;
match nested_comp.concretize_export(name)? {
ConcreteType::Func(ft) => Some(ft),
_ => None,
}
}
_ => None,
}
}
fn concretize_func_ty<'a>(
ft: &'a ComponentFuncType<'a>,
comp: &'a Component<'a>,
cx: &VisitCtx<'a>,
) -> ConcreteFuncType<'a> {
ConcreteFuncType {
params: ft
.params
.iter()
.map(|(name, ty)| (*name, concretize_val_type(ty, comp, cx)))
.collect(),
result: ft
.result
.as_ref()
.map(|ty| concretize_val_type(ty, comp, cx)),
}
}
fn concretize_val_type<'a>(
ty: &'a ComponentValType,
comp: &'a Component<'a>,
cx: &VisitCtx<'a>,
) -> ConcreteValType<'a> {
match ty {
ComponentValType::Primitive(p) => ConcreteValType::Primitive(*p),
ComponentValType::Type(_) => {
if let Some(type_ref) = ty.get_type_refs().first() {
concretize_from_resolved(cx.resolve(&type_ref.ref_), comp, cx)
} else {
unreachable!("`ComponentValType::Type(idx)` always carries exactly one type ref in a valid binary")
}
}
}
}
fn concretize_from_resolved<'a>(
resolved: ResolvedItem<'a, 'a>,
comp: &'a Component<'a>,
cx: &VisitCtx<'a>,
) -> ConcreteValType<'a> {
match resolved {
ResolvedItem::CompType(_, ty) => concretize_comp_type_to_val(ty, comp, cx),
ResolvedItem::Alias(_, alias @ ComponentAlias::Outer { .. }) => {
concretize_from_resolved(cx.resolve(&alias.get_item_ref().ref_), comp, cx)
}
ResolvedItem::Alias(
_,
ComponentAlias::InstanceExport {
instance_index,
name,
..
},
) => {
let Some(nested_comp) = resolve_instantiated_comp(comp, *instance_index) else {
return ConcreteValType::Resource;
};
match nested_comp.concretize_export(name) {
Some(ConcreteType::Resource) | None => ConcreteValType::Resource,
Some(ConcreteType::Instance(_) | ConcreteType::Func(_)) => {
ConcreteValType::Resource
}
}
}
ResolvedItem::Import(_, import) => {
if let Some(type_ref) = import.get_type_refs().into_iter().next() {
concretize_from_resolved(cx.resolve(&type_ref.ref_), comp, cx)
} else {
ConcreteValType::Resource
}
}
ResolvedItem::InstTyDeclExport(_, decl) => {
if let Some(type_ref) = decl.get_type_refs().into_iter().next() {
concretize_from_resolved(cx.resolve(&type_ref.ref_), comp, cx)
} else {
ConcreteValType::Resource
}
}
_ => ConcreteValType::Resource,
}
}
fn concretize_comp_type_to_val<'a>(
ty: &'a ComponentType<'a>,
comp: &'a Component<'a>,
cx: &VisitCtx<'a>,
) -> ConcreteValType<'a> {
match ty {
ComponentType::Defined(def) => concretize_defined_type(def, comp, cx),
_ => ConcreteValType::Resource,
}
}
fn concretize_defined_type<'a>(
ty: &'a ComponentDefinedType,
comp: &'a Component<'a>,
cx: &VisitCtx<'a>,
) -> ConcreteValType<'a> {
match ty {
ComponentDefinedType::Primitive(p) => ConcreteValType::Primitive(*p),
ComponentDefinedType::Record(fields) => ConcreteValType::Record(
fields
.iter()
.map(|(name, ty)| (*name, Box::new(concretize_val_type(ty, comp, cx))))
.collect(),
),
ComponentDefinedType::Variant(cases) => ConcreteValType::Variant(
cases
.iter()
.map(|c| {
(
c.name,
c.ty.as_ref()
.map(|t| Box::new(concretize_val_type(t, comp, cx))),
)
})
.collect(),
),
ComponentDefinedType::List(ty) => {
ConcreteValType::List(Box::new(concretize_val_type(ty, comp, cx)))
}
ComponentDefinedType::Tuple(types) => ConcreteValType::Tuple(
types
.iter()
.map(|t| concretize_val_type(t, comp, cx))
.collect(),
),
ComponentDefinedType::Option(ty) => {
ConcreteValType::Option(Box::new(concretize_val_type(ty, comp, cx)))
}
ComponentDefinedType::Result { ok, err } => ConcreteValType::Result {
ok: ok
.as_ref()
.map(|t| Box::new(concretize_val_type(t, comp, cx))),
err: err
.as_ref()
.map(|t| Box::new(concretize_val_type(t, comp, cx))),
},
ComponentDefinedType::Flags(names) => ConcreteValType::Flags(names.to_vec()),
ComponentDefinedType::Enum(names) => ConcreteValType::Enum(names.to_vec()),
ComponentDefinedType::Map(key, val) => ConcreteValType::Map(
Box::new(concretize_val_type(key, comp, cx)),
Box::new(concretize_val_type(val, comp, cx)),
),
ComponentDefinedType::FixedSizeList(elem, size) => {
ConcreteValType::FixedSizeList(Box::new(concretize_val_type(elem, comp, cx)), *size)
}
ComponentDefinedType::Own(_) | ComponentDefinedType::Borrow(_) => ConcreteValType::Resource,
ComponentDefinedType::Future(_) | ComponentDefinedType::Stream(_) => {
ConcreteValType::AsyncHandle
}
}
}
fn resolve_instantiated_comp<'a>(
comp: &'a Component<'a>,
instance_index: u32,
) -> Option<&'a Component<'a>> {
let inst_ref = IndexedRef {
depth: Depth::default(),
space: Space::CompInst,
index: instance_index,
};
let inst = match comp.resolve(&inst_ref) {
ResolvedItem::CompInst(_, inst) => inst,
_ => return None,
};
let comp_ref = inst.get_comp_refs().into_iter().next()?;
match comp.resolve(&comp_ref.ref_) {
ResolvedItem::Component(_, nested_comp) => Some(nested_comp),
_ => None,
}
}