use crate::ir::component::idx_spaces::{IndexSpaceOf, ScopeId, Space, SpaceSubtype, StoreHandle};
use crate::ir::component::refs::{Depth, IndexedRef, RefKind};
use crate::ir::component::scopes::{
build_component_store, ComponentStore, GetScopeKind, RegistryHandle, ScopeOwnerKind,
};
use crate::ir::component::section::ComponentSection;
use crate::ir::component::visitor::driver::VisitEvent;
use crate::ir::component::visitor::{ItemKind, ResolvedItem};
use crate::ir::id::ComponentId;
use crate::Component;
use wasmparser::{ComponentTypeDeclaration, InstanceTypeDeclaration, ModuleTypeDeclaration};
#[derive(Clone)]
pub(crate) enum TypeBodyDecls<'a> {
Inst(&'a [InstanceTypeDeclaration<'a>]),
Comp(&'a [ComponentTypeDeclaration<'a>]),
Module(&'a [ModuleTypeDeclaration<'a>]),
}
#[derive(Clone)]
pub struct VisitCtxInner<'a> {
pub(crate) registry: RegistryHandle,
pub(crate) component_stack: Vec<ComponentId>, pub(crate) scope_stack: ScopeStack,
pub(crate) node_has_nested_scope: Vec<bool>,
pub(crate) type_scope_nesting: usize,
pub(crate) type_body_stack: Vec<TypeBodyDecls<'a>>,
pub(crate) store: StoreHandle,
pub(crate) comp_store: ComponentStore<'a>,
section_tracker_stack: Vec<SectionTracker>,
pub(crate) current_section_idx: Option<usize>,
}
impl<'a> VisitCtxInner<'a> {
pub fn new(root: &'a Component<'a>) -> Self {
let comp_store = build_component_store(root);
Self {
registry: root.scope_registry.clone(),
component_stack: Vec::new(),
section_tracker_stack: Vec::new(),
scope_stack: ScopeStack::new(),
node_has_nested_scope: Vec::new(),
type_scope_nesting: 0,
type_body_stack: Vec::new(),
store: root.index_store.clone(),
comp_store,
current_section_idx: None,
}
}
pub fn visit_section(&mut self, section: &ComponentSection, num: usize) -> usize {
self.section_tracker_stack
.last_mut()
.unwrap()
.visit_section(section, num)
}
pub fn push_comp_section_tracker(&mut self) {
self.section_tracker_stack.push(SectionTracker::default());
}
pub fn pop_comp_section_tracker(&mut self) {
self.section_tracker_stack.pop();
}
pub fn push_component(&mut self, component: &Component) {
let id = component.id;
self.component_stack.push(id);
self.push_comp_section_tracker();
self.enter_comp_scope(id);
}
pub fn pop_component(&mut self) {
let id = self.component_stack.pop().unwrap();
self.pop_comp_section_tracker();
self.exit_comp_scope(id);
}
pub fn curr_component(&self) -> &Component<'_> {
let id = self.comp_at(Depth::default());
self.comp_store.get(id)
}
pub fn maybe_enter_scope<T: GetScopeKind>(&mut self, node: &T) {
let mut nested = false;
if let Some(scope_entry) = self.registry.borrow().scope_entry(node) {
nested = true;
self.scope_stack.enter_scope(scope_entry.space);
if matches!(
scope_entry.kind,
ScopeOwnerKind::ComponentTypeInstance
| ScopeOwnerKind::ComponentTypeComponent
| ScopeOwnerKind::CoreTypeModule
) {
self.type_scope_nesting += 1;
}
}
self.node_has_nested_scope.push(nested);
}
pub fn maybe_exit_scope<T: GetScopeKind>(&mut self, node: &T) {
let nested = self.node_has_nested_scope.pop().unwrap();
if let Some(scope_entry) = self.registry.borrow().scope_entry(node) {
let exited_from = self.scope_stack.exit_scope();
if matches!(
scope_entry.kind,
ScopeOwnerKind::ComponentTypeInstance
| ScopeOwnerKind::ComponentTypeComponent
| ScopeOwnerKind::CoreTypeModule
) {
self.type_scope_nesting -= 1;
}
debug_assert!(nested);
debug_assert_eq!(scope_entry.space, exited_from);
} else {
debug_assert!(!nested);
}
}
pub(crate) fn enter_comp_scope(&mut self, comp_id: ComponentId) {
let scope_id = self
.registry
.borrow()
.scope_of_comp(comp_id)
.expect("Internal error: no scope found for component");
self.node_has_nested_scope
.push(!self.scope_stack.stack.is_empty());
self.scope_stack.enter_scope(scope_id);
}
pub(crate) fn exit_comp_scope(&mut self, comp_id: ComponentId) {
let scope_id = self
.registry
.borrow()
.scope_of_comp(comp_id)
.unwrap_or_else(|| panic!("Internal error: no scope found for component {comp_id:?}"));
let exited_from = self.scope_stack.exit_scope();
debug_assert_eq!(scope_id, exited_from);
}
pub(crate) fn comp_at(&self, depth: Depth) -> &ComponentId {
let comp_depth = depth.val().saturating_sub(self.type_scope_nesting);
let idx = self.component_stack.len() - comp_depth - 1;
self.component_stack.get(idx).unwrap_or_else(|| {
panic!(
"Internal error: couldn't find component at depth {} \
(adjusted from scope depth {}, type_scope_nesting={}); stack: {:?}",
comp_depth,
depth.val(),
self.type_scope_nesting,
self.component_stack
)
})
}
pub(crate) fn push_type_body(&mut self, decls: TypeBodyDecls<'a>) {
self.type_body_stack.push(decls);
}
pub(crate) fn pop_type_body(&mut self) {
self.type_body_stack.pop();
}
}
impl<'a> VisitCtxInner<'a> {
pub(crate) fn lookup_id_for(
&self,
space: &Space,
section: &ComponentSection,
vec_idx: usize,
) -> u32 {
let nested = self.node_has_nested_scope.last().unwrap_or(&false);
let scope_id = if *nested {
self.scope_stack.scope_at_depth(&Depth::parent())
} else {
self.scope_stack.curr_scope_id()
};
self.store
.borrow()
.scopes
.get(&scope_id)
.unwrap()
.lookup_assumed_id(space, section, vec_idx) as u32
}
pub(crate) fn lookup_id_with_subvec_for(
&self,
space: &Space,
section: &ComponentSection,
vec_idx: usize,
subvec_idx: usize,
) -> u32 {
let nested = self.node_has_nested_scope.last().unwrap_or(&false);
let scope_id = if *nested {
self.scope_stack.scope_at_depth(&Depth::parent())
} else {
self.scope_stack.curr_scope_id()
};
self.store
.borrow()
.scopes
.get(&scope_id)
.unwrap()
.lookup_assumed_id_with_subvec(space, section, vec_idx, subvec_idx) as u32
}
pub(crate) fn index_from_assumed_id_no_cache(
&self,
r: &IndexedRef,
) -> (SpaceSubtype, usize, Option<usize>) {
let scope_id = self.scope_stack.scope_at_depth(&r.depth);
self.store
.borrow()
.scopes
.get(&scope_id)
.unwrap()
.index_from_assumed_id_no_cache(r)
}
pub(crate) fn index_from_assumed_id(
&mut self,
r: &IndexedRef,
) -> (SpaceSubtype, usize, Option<usize>) {
let scope_id = self.scope_stack.scope_at_depth(&r.depth);
self.store
.borrow_mut()
.scopes
.get_mut(&scope_id)
.unwrap()
.index_from_assumed_id(r)
}
}
impl<'a> VisitCtxInner<'a> {
pub fn lookup_root_comp_name(&self) -> Option<&str> {
self.curr_component().component_name.as_deref()
}
pub fn lookup_comp_name(&self, id: u32) -> Option<&str> {
self.curr_component().components_names.get(id)
}
pub fn lookup_comp_inst_name(&self, id: u32) -> Option<&str> {
self.curr_component().instance_names.get(id)
}
pub fn lookup_comp_type_name(&self, id: u32) -> Option<&str> {
self.curr_component().type_names.get(id)
}
pub fn lookup_comp_func_name(&self, id: u32) -> Option<&str> {
self.curr_component().func_names.get(id)
}
pub fn lookup_module_name(&self, id: u32) -> Option<&str> {
self.curr_component().module_names.get(id)
}
pub fn lookup_core_inst_name(&self, id: u32) -> Option<&str> {
self.curr_component().core_instances_names.get(id)
}
pub fn lookup_core_type_name(&self, id: u32) -> Option<&str> {
self.curr_component().core_type_names.get(id)
}
pub fn lookup_core_func_name(&self, id: u32) -> Option<&str> {
self.curr_component().core_func_names.get(id)
}
pub fn lookup_global_name(&self, id: u32) -> Option<&str> {
self.curr_component().global_names.get(id)
}
pub fn lookup_memory_name(&self, id: u32) -> Option<&str> {
self.curr_component().memory_names.get(id)
}
pub fn lookup_tag_name(&self, id: u32) -> Option<&str> {
self.curr_component().tag_names.get(id)
}
pub fn lookup_table_name(&self, id: u32) -> Option<&str> {
self.curr_component().table_names.get(id)
}
pub fn lookup_value_name(&self, id: u32) -> Option<&str> {
self.curr_component().value_names.get(id)
}
pub fn resolve_all(&self, refs: &[RefKind]) -> Vec<ResolvedItem<'a, 'a>> {
let mut items = vec![];
for r in refs.iter() {
items.push(self.resolve(&r.ref_));
}
items
}
pub fn resolve(&self, r: &IndexedRef) -> ResolvedItem<'a, 'a> {
if r.depth.is_curr() {
match self.type_body_stack.last() {
Some(TypeBodyDecls::Inst(decls)) => {
return self.resolve_maybe_from_subvec(r, decls)
}
Some(TypeBodyDecls::Comp(decls)) => {
return self.resolve_maybe_from_subvec(r, decls)
}
Some(TypeBodyDecls::Module(decls)) => {
return self.resolve_maybe_from_subvec(r, decls)
}
None => {} }
}
let (subtype, idx, subidx) = self.index_from_assumed_id_no_cache(r);
if r.space != Space::CoreType {
assert!(
subidx.is_none(),
"only core types (with rec groups) should ever have subvec indices!"
);
}
let comp_id = self.comp_at(r.depth);
let referenced_comp = self.comp_store.get(comp_id);
referenced_comp.item_at(r, subtype, idx)
}
pub fn resolve_maybe_from_subvec<T>(
&self,
ref_: &IndexedRef,
subvec: &'a [T],
) -> ResolvedItem<'a, 'a>
where
T: AsResolvedItem<'a>,
{
if !ref_.depth.is_curr() {
return self.resolve(ref_);
}
let (vec, idx, ..) = self.index_from_assumed_id_no_cache(ref_);
assert_eq!(vec, SpaceSubtype::Main);
subvec[idx].as_resolved_item(ref_.index)
}
}
pub(crate) trait AsResolvedItem<'a> {
fn as_resolved_item(&'a self, index: u32) -> ResolvedItem<'a, 'a>;
}
impl<'a> AsResolvedItem<'a> for InstanceTypeDeclaration<'a> {
fn as_resolved_item(&'a self, index: u32) -> ResolvedItem<'a, 'a> {
match self {
InstanceTypeDeclaration::CoreType(ty) => ResolvedItem::CoreType(index, ty),
InstanceTypeDeclaration::Type(ty) => ResolvedItem::CompType(index, ty),
InstanceTypeDeclaration::Alias(alias) => ResolvedItem::Alias(index, alias),
InstanceTypeDeclaration::Export { .. } => ResolvedItem::InstTyDeclExport(index, self),
}
}
}
impl<'a> AsResolvedItem<'a> for ComponentTypeDeclaration<'a> {
fn as_resolved_item(&'a self, index: u32) -> ResolvedItem<'a, 'a> {
match self {
ComponentTypeDeclaration::CoreType(ty) => ResolvedItem::CoreType(index, ty),
ComponentTypeDeclaration::Type(ty) => ResolvedItem::CompType(index, ty),
ComponentTypeDeclaration::Alias(alias) => ResolvedItem::Alias(index, alias),
ComponentTypeDeclaration::Import(imp) => ResolvedItem::Import(index, imp),
ComponentTypeDeclaration::Export { .. } => ResolvedItem::CompTyDeclExport(index, self),
}
}
}
impl<'a> AsResolvedItem<'a> for ModuleTypeDeclaration<'a> {
fn as_resolved_item(&'a self, index: u32) -> ResolvedItem<'a, 'a> {
ResolvedItem::ModuleTyDecl(index, self)
}
}
#[derive(Clone)]
pub(crate) struct ScopeStack {
pub(crate) stack: Vec<ScopeId>,
}
impl ScopeStack {
fn new() -> Self {
Self { stack: vec![] }
}
pub(crate) fn curr_scope_id(&self) -> ScopeId {
self.stack.last().cloned().unwrap()
}
pub(crate) fn scope_at_depth(&self, depth: &Depth) -> ScopeId {
*self
.stack
.get(self.stack.len() - depth.val() - 1)
.unwrap_or_else(|| {
panic!(
"couldn't find scope at depth {}; this is the current scope stack: {:?}",
depth.val(),
self.stack
)
})
}
pub fn enter_scope(&mut self, id: ScopeId) {
self.stack.push(id)
}
pub fn exit_scope(&mut self) -> ScopeId {
self.stack.pop().unwrap()
}
}
#[derive(Clone, Default)]
struct SectionTracker {
last_processed_module: usize,
last_processed_alias: usize,
last_processed_core_ty: usize,
last_processed_comp_ty: usize,
last_processed_imp: usize,
last_processed_exp: usize,
last_processed_core_inst: usize,
last_processed_comp_inst: usize,
last_processed_canon: usize,
last_processed_component: usize,
last_processed_start: usize,
last_processed_custom: usize,
}
impl SectionTracker {
pub fn visit_section(&mut self, section: &ComponentSection, num: usize) -> usize {
let tracker = match section {
ComponentSection::Component => &mut self.last_processed_component,
ComponentSection::Module => &mut self.last_processed_module,
ComponentSection::Alias => &mut self.last_processed_alias,
ComponentSection::CoreType => &mut self.last_processed_core_ty,
ComponentSection::ComponentType => &mut self.last_processed_comp_ty,
ComponentSection::ComponentImport => &mut self.last_processed_imp,
ComponentSection::ComponentExport => &mut self.last_processed_exp,
ComponentSection::CoreInstance => &mut self.last_processed_core_inst,
ComponentSection::ComponentInstance => &mut self.last_processed_comp_inst,
ComponentSection::Canon => &mut self.last_processed_canon,
ComponentSection::CustomSection => &mut self.last_processed_custom,
ComponentSection::ComponentStartSection => &mut self.last_processed_start,
};
let curr = *tracker;
*tracker += num;
curr
}
}
pub fn for_each_indexed<'ir, T>(
slice: &'ir [T],
start: usize,
count: usize,
mut f: impl FnMut(usize, &'ir T),
) {
debug_assert!(start + count <= slice.len());
for i in 0..count {
let idx = start + i;
f(idx, &slice[idx]);
}
}
pub fn emit_indexed<'ir, T: IndexSpaceOf>(
out: &mut Vec<VisitEvent<'ir>>,
section_idx: usize,
idx: usize,
item: &'ir T,
make: fn(usize, ItemKind, usize, &'ir T) -> VisitEvent<'ir>,
) {
out.push(make(section_idx, item.index_space_of().into(), idx, item));
}