summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wast/src/resolve/aliases.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/wast/src/resolve/aliases.rs')
-rw-r--r--third_party/rust/wast/src/resolve/aliases.rs286
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;
+ }
+ }
+ }
+}