diff options
Diffstat (limited to 'third_party/rust/wast/src/resolve/aliases.rs')
-rw-r--r-- | third_party/rust/wast/src/resolve/aliases.rs | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/third_party/rust/wast/src/resolve/aliases.rs b/third_party/rust/wast/src/resolve/aliases.rs new file mode 100644 index 0000000000..91d386517b --- /dev/null +++ b/third_party/rust/wast/src/resolve/aliases.rs @@ -0,0 +1,286 @@ +use crate::ast::*; +use crate::resolve::gensym; +use std::collections::{hash_map::Entry, HashMap}; + +pub fn run(fields: &mut Vec<ModuleField>) { + let mut cur = 0; + let mut cx = Expander::default(); + + // Note that insertion here is somewhat tricky. We're injecting aliases + // which will affect the index spaces for each kind of item being aliased. + // In the final binary aliases will come before all locally defined items, + // notably via the sorting in binary emission of this crate. To account for + // this index space behavior we need to ensure that aliases all appear at + // the right place in the module. + // + // The general algorithm here is that aliases discovered in the "header" of + // the module, e.g. imports/aliases/types/etc, are all inserted preceding + // the field that the alias is found within. After the header, however, the + // position of the header is recorded and all future aliases will be + // inserted at that location. + // + // This ends up meaning that aliases found in globals/functions/tables/etc + // will precede all of those definitions, being positioned at a point that + // should come after all the instances that are defined as well. Overall + // this isn't the cleanest algorithm and probably isn't the final form of + // those. It's hoped that discussion on WebAssembly/module-linking#25 might + // lead to a good solution. + let mut insertion_point = None; + while cur < fields.len() { + let field = &mut fields[cur]; + match field { + ModuleField::Alias(_) + | ModuleField::Type(_) + | ModuleField::Import(_) + | ModuleField::NestedModule(_) + | ModuleField::Instance(_) => {} + _ if insertion_point.is_none() => insertion_point = Some(cur), + _ => {} + } + cx.process(field); + if insertion_point.is_none() { + for item in cx.to_prepend.drain(..) { + fields.insert(cur, item); + cur += 1; + } + } + cur += 1; + } + if let Some(mut i) = insertion_point { + for item in cx.to_prepend.drain(..) { + fields.insert(i, item); + i += 1; + } + } + assert!(cx.to_prepend.is_empty()); +} + +#[derive(Default)] +struct Expander<'a> { + to_prepend: Vec<ModuleField<'a>>, + instances: HashMap<(Index<'a>, &'a str, ExportKind), Index<'a>>, + parents: HashMap<(Index<'a>, Index<'a>, ExportKind), Index<'a>>, +} + +impl<'a> Expander<'a> { + fn process(&mut self, field: &mut ModuleField<'a>) { + match field { + ModuleField::Alias(a) => { + let id = gensym::fill(a.span, &mut a.id); + match &mut a.kind { + AliasKind::InstanceExport { + instance, + export, + kind, + } => { + self.expand(instance); + self.instances + .insert((*instance.unwrap_index(), export, *kind), id.into()); + } + AliasKind::Outer { + module, + index, + kind, + } => { + self.parents.insert((*module, *index, *kind), id.into()); + } + } + } + + ModuleField::Instance(i) => { + if let InstanceKind::Inline { module, args } = &mut i.kind { + self.expand(module); + for arg in args { + self.expand(&mut arg.index); + } + } + } + + ModuleField::Elem(e) => { + if let ElemKind::Active { table, .. } = &mut e.kind { + self.expand(table); + } + match &mut e.payload { + ElemPayload::Indices(funcs) => { + for func in funcs { + self.expand(func); + } + } + ElemPayload::Exprs { exprs, .. } => { + for func in exprs { + if let Some(func) = func { + self.expand(func); + } + } + } + } + } + + ModuleField::Data(e) => { + if let DataKind::Active { memory, .. } = &mut e.kind { + self.expand(memory); + } + } + + ModuleField::Export(e) => self.expand(&mut e.index), + + ModuleField::Func(f) => { + self.expand_type_use(&mut f.ty); + if let FuncKind::Inline { expression, .. } = &mut f.kind { + self.expand_expr(expression); + } + } + + ModuleField::Import(i) => self.expand_item_sig(&mut i.item), + + ModuleField::Global(g) => { + if let GlobalKind::Inline(expr) = &mut g.kind { + self.expand_expr(expr); + } + } + + ModuleField::Start(s) => self.expand(s), + + ModuleField::Event(e) => match &mut e.ty { + EventType::Exception(t) => self.expand_type_use(t), + }, + + ModuleField::NestedModule(m) => match &mut m.kind { + NestedModuleKind::Import { ty, .. } => self.expand_type_use(ty), + NestedModuleKind::Inline { fields } => run(fields), + }, + + ModuleField::Custom(_) + | ModuleField::Memory(_) + | ModuleField::Table(_) + | ModuleField::Type(_) => {} + } + } + + fn expand_item_sig(&mut self, sig: &mut ItemSig<'a>) { + match &mut sig.kind { + ItemKind::Func(t) => self.expand_type_use(t), + ItemKind::Module(t) => self.expand_type_use(t), + ItemKind::Instance(t) => self.expand_type_use(t), + ItemKind::Table(_) => {} + ItemKind::Memory(_) => {} + ItemKind::Global(_) => {} + ItemKind::Event(_) => {} + } + } + + fn expand_type_use<T>(&mut self, ty: &mut TypeUse<'a, T>) { + if let Some(index) = &mut ty.index { + self.expand(index); + } + } + + fn expand_expr(&mut self, expr: &mut Expression<'a>) { + for instr in expr.instrs.iter_mut() { + self.expand_instr(instr); + } + } + + fn expand_instr(&mut self, instr: &mut Instruction<'a>) { + use Instruction::*; + + if let Some(m) = instr.memarg_mut() { + self.expand(&mut m.memory); + } + + match instr { + Call(i) | ReturnCall(i) | RefFunc(i) => self.expand(&mut i.0), + CallIndirect(i) | ReturnCallIndirect(i) => { + self.expand(&mut i.table); + self.expand_type_use(&mut i.ty); + } + TableInit(i) => self.expand(&mut i.table), + MemoryInit(i) => self.expand(&mut i.mem), + TableCopy(i) => { + self.expand(&mut i.src); + self.expand(&mut i.dst); + } + MemoryCopy(i) => { + self.expand(&mut i.src); + self.expand(&mut i.dst); + } + GlobalSet(g) | GlobalGet(g) => self.expand(&mut g.0), + TableGet(t) | TableSet(t) | TableFill(t) | TableSize(t) | TableGrow(t) => { + self.expand(&mut t.dst) + } + + MemorySize(m) | MemoryGrow(m) | MemoryFill(m) => self.expand(&mut m.mem), + + _ => {} + } + } + + fn expand<T>(&mut self, item: &mut ItemRef<'a, T>) + where + T: Into<ExportKind> + Copy, + { + match item { + ItemRef::Outer { kind, module, idx } => { + let key = (*module, *idx, (*kind).into()); + let idx = match self.parents.entry(key) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(v) => { + let span = idx.span(); + let id = gensym::gen(span); + self.to_prepend.push(ModuleField::Alias(Alias { + span, + id: Some(id), + name: None, + kind: AliasKind::Outer { + module: *module, + index: *idx, + kind: (*kind).into(), + }, + })); + *v.insert(Index::Id(id)) + } + }; + *item = ItemRef::Item { + kind: *kind, + idx, + exports: Vec::new(), + }; + } + ItemRef::Item { kind, idx, exports } => { + let mut cur = *idx; + let len = exports.len(); + for (i, export) in exports.drain(..).enumerate() { + let kind = if i < len - 1 { + ExportKind::Instance + } else { + (*kind).into() + }; + let key = (cur, export, kind); + cur = match self.instances.entry(key) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(v) => { + let span = idx.span(); + let id = gensym::gen(span); + self.to_prepend.push(ModuleField::Alias(Alias { + span, + id: Some(id), + name: None, + kind: AliasKind::InstanceExport { + kind, + instance: ItemRef::Item { + kind: kw::instance(span), + idx: cur, + exports: Vec::new(), + }, + export, + }, + })); + *v.insert(Index::Id(id)) + } + }; + } + *idx = cur; + } + } + } +} |