use crate::ir::component::idx_spaces::Space;
use crate::ir::component::refs::{Depth, GetCompRefs, GetItemRef, GetTypeRefs, IndexedRef};
use crate::ir::component::visitor::utils::{TypeBodyDecls, VisitCtxInner};
use crate::ir::component::visitor::{ResolvedItem, VisitCtx};
use crate::Component;
use wasmparser::{
ComponentAlias, ComponentDefinedType, ComponentExport, ComponentExternalKind,
ComponentFuncType, ComponentInstance, 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),
ResolvedItem::CompInst(_, ComponentInstance::FromExports(exports)) => {
concretize_from_exports_instance(self, exports)
}
ResolvedItem::CompInst(_, inst @ ComponentInstance::Instantiate { .. }) => {
self.concretize_import(name).or_else(|| {
let comp_ref = inst.get_comp_refs().into_iter().next()?;
match self.resolve(&comp_ref.ref_) {
ResolvedItem::Component(_, nested) => concretize_comp_func_exports(nested),
_ => None,
}
})
}
ResolvedItem::Import(_, imp) => {
let type_ref = imp.get_type_refs().into_iter().next()?;
let ty = match self.resolve(&type_ref.ref_) {
ResolvedItem::CompType(_, ty) => ty,
_ => return None,
};
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);
match ty {
ComponentType::Instance(decls) => inner.push_type_body(TypeBodyDecls::Inst(decls)),
ComponentType::Component(decls) => inner.push_type_body(TypeBodyDecls::Comp(decls)),
_ => {}
}
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,
..
},
) => {
if let Some(nested_comp) = resolve_instantiated_comp(comp, *instance_index) {
match nested_comp.concretize_export(name) {
Some(ConcreteType::Func(ft)) => Some(ft),
_ => None,
}
} else {
resolve_func_from_import_instance(comp, *instance_index, name)
}
}
ResolvedItem::Import(_, imp) => {
let type_ref = imp.get_type_refs().into_iter().next()?;
match comp.resolve(&type_ref.ref_) {
ResolvedItem::CompType(_, ComponentType::Func(ft)) => {
Some(concretize_func_ty(ft, comp, cx))
}
_ => 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 resolve_type_from_import_instance(comp, *instance_index, name);
};
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 concretize_from_exports_instance<'a>(
comp: &'a Component<'a>,
exports: &'a [ComponentExport<'a>],
) -> Option<ConcreteType<'a>> {
let cx = {
let mut inner = VisitCtxInner::new(comp);
inner.push_component(comp);
VisitCtx { inner }
};
let mut funcs = vec![];
for export in exports.iter() {
if export.kind != ComponentExternalKind::Func {
continue; }
let resolved = comp.resolve(&export.get_item_ref().ref_);
if let Some(ft) = resolve_and_concretize_func(resolved, comp, &cx) {
funcs.push((export.name.0, ft));
}
}
Some(ConcreteType::Instance(funcs))
}
fn concretize_comp_func_exports<'a>(comp: &'a Component<'a>) -> Option<ConcreteType<'a>> {
let cx = {
let mut inner = VisitCtxInner::new(comp);
inner.push_component(comp);
VisitCtx { inner }
};
let mut funcs = vec![];
for export in comp.exports.iter() {
if export.kind != ComponentExternalKind::Func {
continue; }
let resolved = comp.resolve(&export.get_item_ref().ref_);
if let Some(ft) = resolve_and_concretize_func(resolved, comp, &cx) {
funcs.push((export.name.0, ft));
}
}
Some(ConcreteType::Instance(funcs))
}
fn resolve_type_from_import_instance<'a>(
comp: &'a Component<'a>,
instance_index: u32,
type_name: &str,
) -> ConcreteValType<'a> {
let inst_ref = IndexedRef {
depth: Depth::default(),
space: Space::CompInst,
index: instance_index,
};
let import = match comp.resolve(&inst_ref) {
ResolvedItem::Import(_, imp) => imp,
_ => return ConcreteValType::Resource,
};
let type_ref = match import.get_type_refs().into_iter().next() {
Some(tr) => tr,
None => return ConcreteValType::Resource,
};
let ty = match comp.resolve(&type_ref.ref_) {
ResolvedItem::CompType(_, ty) => ty,
_ => return ConcreteValType::Resource,
};
let decls = match ty {
ComponentType::Instance(decls) => decls,
_ => return ConcreteValType::Resource,
};
let inner_cx = comp.enter_type_scope(ty);
for decl in decls {
if let InstanceTypeDeclaration::Export { name, .. } = decl {
if name.0 != type_name {
continue;
}
if let Some(tr) = decl.get_type_refs().first() {
let resolved = inner_cx.resolve(&tr.ref_);
return concretize_from_resolved(resolved, comp, &inner_cx);
}
}
}
ConcreteValType::Resource
}
fn resolve_func_from_import_instance<'a>(
comp: &'a Component<'a>,
instance_index: u32,
func_name: &str,
) -> Option<ConcreteFuncType<'a>> {
let inst_ref = IndexedRef {
depth: Depth::default(),
space: Space::CompInst,
index: instance_index,
};
let import = match comp.resolve(&inst_ref) {
ResolvedItem::Import(_, imp) => imp,
_ => return None,
};
let type_ref = import.get_type_refs().into_iter().next()?;
let ty = match comp.resolve(&type_ref.ref_) {
ResolvedItem::CompType(_, ty) => ty,
_ => return None,
};
match concretize_comp_type(comp, ty)? {
ConcreteType::Instance(funcs) => funcs
.into_iter()
.find(|(name, _)| *name == func_name)
.map(|(_, ft)| ft),
_ => None,
}
}
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,
}
}