summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wast/src/ast
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/wast/src/ast
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--third_party/rust/wast/src/ast/alias.rs76
-rw-r--r--third_party/rust/wast/src/ast/assert_expr.rs154
-rw-r--r--third_party/rust/wast/src/ast/custom.rs165
-rw-r--r--third_party/rust/wast/src/ast/event.rs44
-rw-r--r--third_party/rust/wast/src/ast/export.rs141
-rw-r--r--third_party/rust/wast/src/ast/expr.rs1767
-rw-r--r--third_party/rust/wast/src/ast/func.rs115
-rw-r--r--third_party/rust/wast/src/ast/global.rs53
-rw-r--r--third_party/rust/wast/src/ast/import.rs176
-rw-r--r--third_party/rust/wast/src/ast/instance.rs86
-rw-r--r--third_party/rust/wast/src/ast/memory.rs250
-rw-r--r--third_party/rust/wast/src/ast/mod.rs430
-rw-r--r--third_party/rust/wast/src/ast/module.rs239
-rw-r--r--third_party/rust/wast/src/ast/nested_module.rs115
-rw-r--r--third_party/rust/wast/src/ast/table.rs234
-rw-r--r--third_party/rust/wast/src/ast/token.rs757
-rw-r--r--third_party/rust/wast/src/ast/types.rs807
-rw-r--r--third_party/rust/wast/src/ast/wast.rs356
18 files changed, 5965 insertions, 0 deletions
diff --git a/third_party/rust/wast/src/ast/alias.rs b/third_party/rust/wast/src/ast/alias.rs
new file mode 100644
index 0000000000..9bcb4969fb
--- /dev/null
+++ b/third_party/rust/wast/src/ast/alias.rs
@@ -0,0 +1,76 @@
+use crate::ast::{self, kw};
+use crate::parser::{Parse, Parser, Result};
+
+/// An `alias` statement used to juggle indices with nested modules.
+#[derive(Debug)]
+pub struct Alias<'a> {
+ /// Where this `alias` was defined.
+ pub span: ast::Span,
+ /// An identifier that this alias is resolved with (optionally) for name
+ /// resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// An optional name for this alias stored in the custom `name` section.
+ pub name: Option<ast::NameAnnotation<'a>>,
+ /// The item in the parent instance that we're aliasing.
+ pub kind: AliasKind<'a>,
+}
+
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub enum AliasKind<'a> {
+ InstanceExport {
+ instance: ast::ItemRef<'a, kw::instance>,
+ export: &'a str,
+ kind: ast::ExportKind,
+ },
+ Outer {
+ /// The index of the module that this reference is referring to.
+ module: ast::Index<'a>,
+ /// The index of the item within `module` that this alias is referering
+ /// to.
+ index: ast::Index<'a>,
+ /// The kind of item that's being aliased.
+ kind: ast::ExportKind,
+ },
+}
+
+impl Alias<'_> {
+ /// Returns the kind of item defined by this alias.
+ pub fn item_kind(&self) -> ast::ExportKind {
+ match self.kind {
+ AliasKind::InstanceExport { kind, .. } => kind,
+ AliasKind::Outer { kind, .. } => kind,
+ }
+ }
+}
+
+impl<'a> Parse<'a> for Alias<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::alias>()?.0;
+ let id = parser.parse()?;
+ let name = parser.parse()?;
+ let kind = parser.parens(|p| {
+ let kind = p.parse()?;
+ Ok(if parser.parse::<Option<kw::outer>>()?.is_some() {
+ AliasKind::Outer {
+ module: parser.parse()?,
+ index: parser.parse()?,
+ kind,
+ }
+ } else {
+ AliasKind::InstanceExport {
+ instance: parser.parse::<ast::IndexOrRef<_>>()?.0,
+ export: parser.parse()?,
+ kind,
+ }
+ })
+ })?;
+
+ Ok(Alias {
+ span,
+ id,
+ name,
+ kind,
+ })
+ }
+}
diff --git a/third_party/rust/wast/src/ast/assert_expr.rs b/third_party/rust/wast/src/ast/assert_expr.rs
new file mode 100644
index 0000000000..f6fcc9cd12
--- /dev/null
+++ b/third_party/rust/wast/src/ast/assert_expr.rs
@@ -0,0 +1,154 @@
+use crate::ast::{kw, Float32, Float64, Index, HeapType};
+use crate::parser::{Parse, Parser, Result};
+
+/// An expression that is valid inside an `assert_return` directive.
+///
+/// As of https://github.com/WebAssembly/spec/pull/1104, spec tests may include `assert_return`
+/// directives that allow NaN patterns (`"nan:canonical"`, `"nan:arithmetic"`). Parsing an
+/// `AssertExpression` means that:
+/// - only constant values (e.g. `i32.const 4`) are used in the `assert_return` directive
+/// - the NaN patterns are allowed (they are not allowed in regular `Expression`s).
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub enum AssertExpression<'a> {
+ I32(i32),
+ I64(i64),
+ F32(NanPattern<Float32>),
+ F64(NanPattern<Float64>),
+ V128(V128Pattern),
+
+ RefNull(Option<HeapType<'a>>),
+ RefExtern(u32),
+ RefFunc(Option<Index<'a>>),
+
+ // Either matches an f32 or f64 for an arithmetic nan pattern
+ LegacyArithmeticNaN,
+ // Either matches an f32 or f64 for a canonical nan pattern
+ LegacyCanonicalNaN,
+}
+
+impl<'a> Parse<'a> for AssertExpression<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let keyword = parser.step(|c| match c.keyword() {
+ Some(pair) => Ok(pair),
+ None => Err(c.error("expected a keyword")),
+ })?;
+
+ match keyword {
+ "i32.const" => Ok(AssertExpression::I32(parser.parse()?)),
+ "i64.const" => Ok(AssertExpression::I64(parser.parse()?)),
+ "f32.const" => Ok(AssertExpression::F32(parser.parse()?)),
+ "f64.const" => Ok(AssertExpression::F64(parser.parse()?)),
+ "v128.const" => Ok(AssertExpression::V128(parser.parse()?)),
+ "ref.null" => Ok(AssertExpression::RefNull(parser.parse()?)),
+ "ref.extern" => Ok(AssertExpression::RefExtern(parser.parse()?)),
+ "ref.func" => Ok(AssertExpression::RefFunc(parser.parse()?)),
+ _ => Err(parser.error("expected a [type].const expression")),
+ }
+ }
+}
+
+/// Either a NaN pattern (`nan:canonical`, `nan:arithmetic`) or a value of type `T`.
+#[derive(Debug, PartialEq)]
+#[allow(missing_docs)]
+pub enum NanPattern<T> {
+ CanonicalNan,
+ ArithmeticNan,
+ Value(T),
+}
+
+impl<'a, T> Parse<'a> for NanPattern<T>
+where
+ T: Parse<'a>,
+{
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if parser.peek::<kw::nan_canonical>() {
+ parser.parse::<kw::nan_canonical>()?;
+ Ok(NanPattern::CanonicalNan)
+ } else if parser.peek::<kw::nan_arithmetic>() {
+ parser.parse::<kw::nan_arithmetic>()?;
+ Ok(NanPattern::ArithmeticNan)
+ } else {
+ let val = parser.parse()?;
+ Ok(NanPattern::Value(val))
+ }
+ }
+}
+
+/// A version of `V128Const` that allows `NanPattern`s.
+///
+/// This implementation is necessary because only float types can include NaN patterns; otherwise
+/// it is largely similar to the implementation of `V128Const`.
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub enum V128Pattern {
+ I8x16([i8; 16]),
+ I16x8([i16; 8]),
+ I32x4([i32; 4]),
+ I64x2([i64; 2]),
+ F32x4([NanPattern<Float32>; 4]),
+ F64x2([NanPattern<Float64>; 2]),
+}
+
+impl<'a> Parse<'a> for V128Pattern {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::i8x16>() {
+ parser.parse::<kw::i8x16>()?;
+ Ok(V128Pattern::I8x16([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::i16x8>() {
+ parser.parse::<kw::i16x8>()?;
+ Ok(V128Pattern::I16x8([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::i32x4>() {
+ parser.parse::<kw::i32x4>()?;
+ Ok(V128Pattern::I32x4([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::i64x2>() {
+ parser.parse::<kw::i64x2>()?;
+ Ok(V128Pattern::I64x2([parser.parse()?, parser.parse()?]))
+ } else if l.peek::<kw::f32x4>() {
+ parser.parse::<kw::f32x4>()?;
+ Ok(V128Pattern::F32x4([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::f64x2>() {
+ parser.parse::<kw::f64x2>()?;
+ Ok(V128Pattern::F64x2([parser.parse()?, parser.parse()?]))
+ } else {
+ Err(l.error())
+ }
+ }
+}
diff --git a/third_party/rust/wast/src/ast/custom.rs b/third_party/rust/wast/src/ast/custom.rs
new file mode 100644
index 0000000000..52d8aa60df
--- /dev/null
+++ b/third_party/rust/wast/src/ast/custom.rs
@@ -0,0 +1,165 @@
+use crate::ast::{self, annotation, kw};
+use crate::parser::{Parse, Parser, Result};
+
+/// A wasm custom section within a module.
+#[derive(Debug)]
+pub struct Custom<'a> {
+ /// Where this `@custom` was defined.
+ pub span: ast::Span,
+
+ /// Name of the custom section.
+ pub name: &'a str,
+
+ /// Where the custom section is being placed,
+ pub place: CustomPlace,
+
+ /// Payload of this custom section.
+ pub data: Vec<&'a [u8]>,
+}
+
+/// Possible locations to place a custom section within a module.
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub enum CustomPlace {
+ /// This custom section will appear before the first section in the module.
+ BeforeFirst,
+ /// This custom section will be placed just before a known section.
+ Before(CustomPlaceAnchor),
+ /// This custom section will be placed just after a known section.
+ After(CustomPlaceAnchor),
+ /// This custom section will appear after the last section in the module.
+ AfterLast,
+}
+
+/// Known sections that custom sections can be placed relative to.
+#[derive(Debug, PartialEq, Copy, Clone)]
+#[allow(missing_docs)]
+pub enum CustomPlaceAnchor {
+ Type,
+ Import,
+ Module,
+ Instance,
+ Alias,
+ Func,
+ Table,
+ Memory,
+ Global,
+ Export,
+ Start,
+ Elem,
+ Code,
+ Data,
+ Event,
+}
+
+impl<'a> Parse<'a> for Custom<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<annotation::custom>()?.0;
+ let name = parser.parse()?;
+ let place = if parser.peek::<ast::LParen>() {
+ parser.parens(|p| p.parse())?
+ } else {
+ CustomPlace::AfterLast
+ };
+ let mut data = Vec::new();
+ while !parser.is_empty() {
+ data.push(parser.parse()?);
+ }
+ Ok(Custom {
+ span,
+ name,
+ place,
+ data,
+ })
+ }
+}
+
+impl<'a> Parse<'a> for CustomPlace {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ let ctor = if l.peek::<kw::before>() {
+ parser.parse::<kw::before>()?;
+ if l.peek::<kw::first>() {
+ parser.parse::<kw::first>()?;
+ return Ok(CustomPlace::BeforeFirst);
+ }
+ CustomPlace::Before as fn(CustomPlaceAnchor) -> _
+ } else if l.peek::<kw::after>() {
+ parser.parse::<kw::after>()?;
+ if l.peek::<kw::last>() {
+ parser.parse::<kw::last>()?;
+ return Ok(CustomPlace::AfterLast);
+ }
+ CustomPlace::After
+ } else {
+ return Err(l.error());
+ };
+ Ok(ctor(parser.parse()?))
+ }
+}
+
+impl<'a> Parse<'a> for CustomPlaceAnchor {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if parser.peek::<kw::r#type>() {
+ parser.parse::<kw::r#type>()?;
+ return Ok(CustomPlaceAnchor::Type);
+ }
+ if parser.peek::<kw::import>() {
+ parser.parse::<kw::import>()?;
+ return Ok(CustomPlaceAnchor::Import);
+ }
+ if parser.peek::<kw::func>() {
+ parser.parse::<kw::func>()?;
+ return Ok(CustomPlaceAnchor::Func);
+ }
+ if parser.peek::<kw::table>() {
+ parser.parse::<kw::table>()?;
+ return Ok(CustomPlaceAnchor::Table);
+ }
+ if parser.peek::<kw::memory>() {
+ parser.parse::<kw::memory>()?;
+ return Ok(CustomPlaceAnchor::Memory);
+ }
+ if parser.peek::<kw::global>() {
+ parser.parse::<kw::global>()?;
+ return Ok(CustomPlaceAnchor::Global);
+ }
+ if parser.peek::<kw::export>() {
+ parser.parse::<kw::export>()?;
+ return Ok(CustomPlaceAnchor::Export);
+ }
+ if parser.peek::<kw::start>() {
+ parser.parse::<kw::start>()?;
+ return Ok(CustomPlaceAnchor::Start);
+ }
+ if parser.peek::<kw::elem>() {
+ parser.parse::<kw::elem>()?;
+ return Ok(CustomPlaceAnchor::Elem);
+ }
+ if parser.peek::<kw::code>() {
+ parser.parse::<kw::code>()?;
+ return Ok(CustomPlaceAnchor::Code);
+ }
+ if parser.peek::<kw::data>() {
+ parser.parse::<kw::data>()?;
+ return Ok(CustomPlaceAnchor::Data);
+ }
+ if parser.peek::<kw::event>() {
+ parser.parse::<kw::event>()?;
+ return Ok(CustomPlaceAnchor::Event);
+ }
+ if parser.peek::<kw::instance>() {
+ parser.parse::<kw::instance>()?;
+ return Ok(CustomPlaceAnchor::Instance);
+ }
+ if parser.peek::<kw::module>() {
+ parser.parse::<kw::module>()?;
+ return Ok(CustomPlaceAnchor::Module);
+ }
+ if parser.peek::<kw::alias>() {
+ parser.parse::<kw::alias>()?;
+ return Ok(CustomPlaceAnchor::Alias);
+ }
+
+ Err(parser.error("expected a valid section name"))
+ }
+}
diff --git a/third_party/rust/wast/src/ast/event.rs b/third_party/rust/wast/src/ast/event.rs
new file mode 100644
index 0000000000..0746afc4c2
--- /dev/null
+++ b/third_party/rust/wast/src/ast/event.rs
@@ -0,0 +1,44 @@
+use crate::ast::{self, kw};
+use crate::parser::{Parse, Parser, Result};
+
+/// A WebAssembly event directive, part of the exception handling proposal.
+#[derive(Debug)]
+pub struct Event<'a> {
+ /// Where this event was defined
+ pub span: ast::Span,
+ /// An optional name by which to refer to this event in name resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// Optional export directives for this event.
+ pub exports: ast::InlineExport<'a>,
+ /// The type of event that is defined.
+ pub ty: EventType<'a>,
+}
+
+/// Listing of various types of events that can be defined in a wasm module.
+#[derive(Clone, Debug)]
+pub enum EventType<'a> {
+ /// An exception event, where the payload is the type signature of the event
+ /// (constructor parameters, etc).
+ Exception(ast::TypeUse<'a, ast::FunctionType<'a>>),
+}
+
+impl<'a> Parse<'a> for Event<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::event>()?.0;
+ let id = parser.parse()?;
+ let exports = parser.parse()?;
+ let ty = parser.parse()?;
+ Ok(Event {
+ span,
+ id,
+ exports,
+ ty,
+ })
+ }
+}
+
+impl<'a> Parse<'a> for EventType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(EventType::Exception(parser.parse()?))
+ }
+}
diff --git a/third_party/rust/wast/src/ast/export.rs b/third_party/rust/wast/src/ast/export.rs
new file mode 100644
index 0000000000..eadb0b5cc3
--- /dev/null
+++ b/third_party/rust/wast/src/ast/export.rs
@@ -0,0 +1,141 @@
+use crate::ast::{self, kw};
+use crate::parser::{Cursor, Parse, Parser, Peek, Result};
+
+/// A entry in a WebAssembly module's export section.
+#[derive(Debug)]
+pub struct Export<'a> {
+ /// Where this export was defined.
+ pub span: ast::Span,
+ /// The name of this export from the module.
+ pub name: &'a str,
+ /// What's being exported from the module.
+ pub index: ast::ItemRef<'a, ExportKind>,
+}
+
+/// Different kinds of elements that can be exported from a WebAssembly module,
+/// contained in an [`Export`].
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+#[allow(missing_docs)]
+pub enum ExportKind {
+ Func,
+ Table,
+ Memory,
+ Global,
+ Event,
+ Module,
+ Instance,
+ Type,
+}
+
+impl<'a> Parse<'a> for Export<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(Export {
+ span: parser.parse::<kw::export>()?.0,
+ name: parser.parse()?,
+ index: parser.parse()?,
+ })
+ }
+}
+
+impl<'a> Parse<'a> for ExportKind {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::func>() {
+ parser.parse::<kw::func>()?;
+ Ok(ExportKind::Func)
+ } else if l.peek::<kw::table>() {
+ parser.parse::<kw::table>()?;
+ Ok(ExportKind::Table)
+ } else if l.peek::<kw::memory>() {
+ parser.parse::<kw::memory>()?;
+ Ok(ExportKind::Memory)
+ } else if l.peek::<kw::global>() {
+ parser.parse::<kw::global>()?;
+ Ok(ExportKind::Global)
+ } else if l.peek::<kw::event>() {
+ parser.parse::<kw::event>()?;
+ Ok(ExportKind::Event)
+ } else if l.peek::<kw::module>() {
+ parser.parse::<kw::module>()?;
+ Ok(ExportKind::Module)
+ } else if l.peek::<kw::instance>() {
+ parser.parse::<kw::instance>()?;
+ Ok(ExportKind::Instance)
+ } else if l.peek::<kw::r#type>() {
+ parser.parse::<kw::r#type>()?;
+ Ok(ExportKind::Type)
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+macro_rules! kw_conversions {
+ ($($kw:ident => $kind:ident)*) => ($(
+ impl From<kw::$kw> for ExportKind {
+ fn from(_: kw::$kw) -> ExportKind {
+ ExportKind::$kind
+ }
+ }
+
+ impl Default for kw::$kw {
+ fn default() -> kw::$kw {
+ kw::$kw(ast::Span::from_offset(0))
+ }
+ }
+ )*);
+}
+
+kw_conversions! {
+ instance => Instance
+ module => Module
+ func => Func
+ table => Table
+ global => Global
+ event => Event
+ memory => Memory
+ r#type => Type
+}
+
+/// A listing of inline `(export "foo")` statements on a WebAssembly item in
+/// its textual format.
+#[derive(Debug)]
+pub struct InlineExport<'a> {
+ /// The extra names to export an item as, if any.
+ pub names: Vec<&'a str>,
+}
+
+impl<'a> Parse<'a> for InlineExport<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut names = Vec::new();
+ while parser.peek::<Self>() {
+ names.push(parser.parens(|p| {
+ p.parse::<kw::export>()?;
+ p.parse::<&str>()
+ })?);
+ }
+ Ok(InlineExport { names })
+ }
+}
+
+impl Peek for InlineExport<'_> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ let cursor = match cursor.lparen() {
+ Some(cursor) => cursor,
+ None => return false,
+ };
+ let cursor = match cursor.keyword() {
+ Some(("export", cursor)) => cursor,
+ _ => return false,
+ };
+ let cursor = match cursor.string() {
+ Some((_, cursor)) => cursor,
+ None => return false,
+ };
+ cursor.rparen().is_some()
+ }
+
+ fn display() -> &'static str {
+ "inline export"
+ }
+}
diff --git a/third_party/rust/wast/src/ast/expr.rs b/third_party/rust/wast/src/ast/expr.rs
new file mode 100644
index 0000000000..dc4bfb0027
--- /dev/null
+++ b/third_party/rust/wast/src/ast/expr.rs
@@ -0,0 +1,1767 @@
+use crate::ast::{self, kw, HeapType};
+use crate::parser::{Parse, Parser, Result};
+use std::mem;
+
+/// An expression, or a list of instructions, in the WebAssembly text format.
+///
+/// This expression type will parse s-expression-folded instructions into a flat
+/// list of instructions for emission later on. The implicit `end` instruction
+/// at the end of an expression is not included in the `instrs` field.
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct Expression<'a> {
+ pub instrs: Box<[Instruction<'a>]>,
+}
+
+impl<'a> Parse<'a> for Expression<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ ExpressionParser::default().parse(parser)
+ }
+}
+
+/// Helper struct used to parse an `Expression` with helper methods and such.
+///
+/// The primary purpose of this is to avoid defining expression parsing as a
+/// call-thread-stack recursive function. Since we're parsing user input that
+/// runs the risk of blowing the call stack, so we want to be sure to use a heap
+/// stack structure wherever possible.
+#[derive(Default)]
+struct ExpressionParser<'a> {
+ /// The flat list of instructions that we've parsed so far, and will
+ /// eventually become the final `Expression`.
+ instrs: Vec<Instruction<'a>>,
+
+ /// Descriptor of all our nested s-expr blocks. This only happens when
+ /// instructions themselves are nested.
+ stack: Vec<Level<'a>>,
+}
+
+enum Paren {
+ None,
+ Left,
+ Right,
+}
+
+/// A "kind" of nested block that we can be parsing inside of.
+enum Level<'a> {
+ /// This is a normal `block` or `loop` or similar, where the instruction
+ /// payload here is pushed when the block is exited.
+ EndWith(Instruction<'a>),
+
+ /// This is a pretty special variant which means that we're parsing an `if`
+ /// statement, and the state of the `if` parsing is tracked internally in
+ /// the payload.
+ If(If<'a>),
+
+ /// This means we're either parsing inside of `(then ...)` or `(else ...)`
+ /// which don't correspond to terminating instructions, we're just in a
+ /// nested block.
+ IfArm,
+
+ /// Similar to `If` but for `Try` statements, which has simpler parsing
+ /// state to track.
+ Try(Try<'a>),
+
+ /// Similar to `IfArm` but for `(do ...)` and `(catch ...)` blocks.
+ TryArm,
+}
+
+/// Possible states of "what should be parsed next?" in an `if` expression.
+enum If<'a> {
+ /// Only the `if` has been parsed, next thing to parse is the clause, if
+ /// any, of the `if` instruction.
+ Clause(Instruction<'a>),
+ /// Next thing to parse is the `then` block
+ Then(Instruction<'a>),
+ /// Next thing to parse is the `else` block
+ Else,
+ /// This `if` statement has finished parsing and if anything remains it's a
+ /// syntax error.
+ End,
+}
+
+/// Possible state of "what should be parsed next?" in a `try` expression.
+enum Try<'a> {
+ /// Next thing to parse is the `do` block.
+ Do(Instruction<'a>),
+ /// Next thing to parse is `catch`/`catch_all`, or `unwind`.
+ CatchOrUnwind,
+ /// Next thing to parse is a `catch` block or `catch_all`.
+ Catch,
+ /// This `try` statement has finished parsing and if anything remains it's a
+ /// syntax error.
+ End,
+}
+
+impl<'a> ExpressionParser<'a> {
+ fn parse(mut self, parser: Parser<'a>) -> Result<Expression<'a>> {
+ // Here we parse instructions in a loop, and we do not recursively
+ // invoke this parse function to avoid blowing the stack on
+ // deeply-recursive parses.
+ //
+ // Our loop generally only finishes once there's no more input left int
+ // the `parser`. If there's some unclosed delimiters though (on our
+ // `stack`), then we also keep parsing to generate error messages if
+ // there's no input left.
+ while !parser.is_empty() || !self.stack.is_empty() {
+ // As a small ease-of-life adjustment here, if we're parsing inside
+ // of an `if block then we require that all sub-components are
+ // s-expressions surrounded by `(` and `)`, so verify that here.
+ if let Some(Level::If(_)) | Some(Level::Try(_)) = self.stack.last() {
+ if !parser.is_empty() && !parser.peek::<ast::LParen>() {
+ return Err(parser.error("expected `(`"));
+ }
+ }
+
+ match self.paren(parser)? {
+ // No parenthesis seen? Then we just parse the next instruction
+ // and move on.
+ Paren::None => self.instrs.push(parser.parse()?),
+
+ // If we see a left-parenthesis then things are a little
+ // special. We handle block-like instructions specially
+ // (`block`, `loop`, and `if`), and otherwise all other
+ // instructions simply get appended once we reach the end of the
+ // s-expression.
+ //
+ // In all cases here we push something onto the `stack` to get
+ // popped when the `)` character is seen.
+ Paren::Left => {
+ // First up is handling `if` parsing, which is funky in a
+ // whole bunch of ways. See the method internally for more
+ // information.
+ if self.handle_if_lparen(parser)? {
+ continue;
+ }
+ // Second, we handle `try` parsing, which is simpler than
+ // `if` but more complicated than, e.g., `block`.
+ if self.handle_try_lparen(parser)? {
+ continue;
+ }
+ match parser.parse()? {
+ // If block/loop show up then we just need to be sure to
+ // push an `end` instruction whenever the `)` token is
+ // seen
+ i @ Instruction::Block(_)
+ | i @ Instruction::Loop(_)
+ | i @ Instruction::Let(_) => {
+ self.instrs.push(i);
+ self.stack.push(Level::EndWith(Instruction::End(None)));
+ }
+
+ // Parsing an `if` instruction is super tricky, so we
+ // push an `If` scope and we let all our scope-based
+ // parsing handle the remaining items.
+ i @ Instruction::If(_) => {
+ self.stack.push(Level::If(If::Clause(i)));
+ }
+
+ // Parsing a `try` is easier than `if` but we also push
+ // a `Try` scope to handle the required nested blocks.
+ i @ Instruction::Try(_) => {
+ self.stack.push(Level::Try(Try::Do(i)));
+ }
+
+ // Anything else means that we're parsing a nested form
+ // such as `(i32.add ...)` which means that the
+ // instruction we parsed will be coming at the end.
+ other => self.stack.push(Level::EndWith(other)),
+ }
+ }
+
+ // If we registered a `)` token as being seen, then we're
+ // guaranteed there's an item in the `stack` stack for us to
+ // pop. We peel that off and take a look at what it says to do.
+ Paren::Right => match self.stack.pop().unwrap() {
+ Level::EndWith(i) => self.instrs.push(i),
+ Level::IfArm => {}
+ Level::TryArm => {}
+
+ // If an `if` statement hasn't parsed the clause or `then`
+ // block, then that's an error because there weren't enough
+ // items in the `if` statement. Otherwise we're just careful
+ // to terminate with an `end` instruction.
+ Level::If(If::Clause(_)) => {
+ return Err(parser.error("previous `if` had no clause"));
+ }
+ Level::If(If::Then(_)) => {
+ return Err(parser.error("previous `if` had no `then`"));
+ }
+ Level::If(_) => {
+ self.instrs.push(Instruction::End(None));
+ }
+
+ // Both `do` and `catch` are required in a `try` statement, so
+ // we will signal those errors here. Otherwise, terminate with
+ // an `end` instruction.
+ Level::Try(Try::Do(_)) => {
+ return Err(parser.error("previous `try` had no `do`"));
+ }
+ Level::Try(Try::CatchOrUnwind) => {
+ return Err(
+ parser.error("previous `try` had no `catch`, `catch_all`, or `unwind`")
+ );
+ }
+ Level::Try(_) => {
+ self.instrs.push(Instruction::End(None));
+ }
+ },
+ }
+ }
+
+ Ok(Expression {
+ instrs: self.instrs.into(),
+ })
+ }
+
+ /// Parses either `(`, `)`, or nothing.
+ fn paren(&self, parser: Parser<'a>) -> Result<Paren> {
+ parser.step(|cursor| {
+ Ok(match cursor.lparen() {
+ Some(rest) => (Paren::Left, rest),
+ None if self.stack.is_empty() => (Paren::None, cursor),
+ None => match cursor.rparen() {
+ Some(rest) => (Paren::Right, rest),
+ None => (Paren::None, cursor),
+ },
+ })
+ })
+ }
+
+ /// Handles all parsing of an `if` statement.
+ ///
+ /// The syntactical form of an `if` stament looks like:
+ ///
+ /// ```wat
+ /// (if $clause (then $then) (else $else))
+ /// ```
+ ///
+ /// but it turns out we practically see a few things in the wild:
+ ///
+ /// * inside the `(if ...)` every sub-thing is surrounded by parens
+ /// * The `then` and `else` keywords are optional
+ /// * The `$then` and `$else` blocks don't need to be surrounded by parens
+ ///
+ /// That's all attempted to be handled here. The part about all sub-parts
+ /// being surrounded by `(` and `)` means that we hook into the `LParen`
+ /// parsing above to call this method there unconditionally.
+ ///
+ /// Returns `true` if the rest of the arm above should be skipped, or
+ /// `false` if we should parse the next item as an instruction (because we
+ /// didn't handle the lparen here).
+ fn handle_if_lparen(&mut self, parser: Parser<'a>) -> Result<bool> {
+ // Only execute the code below if there's an `If` listed last.
+ let i = match self.stack.last_mut() {
+ Some(Level::If(i)) => i,
+ _ => return Ok(false),
+ };
+
+ // The first thing parsed in an `if` statement is the clause. If the
+ // clause starts with `then`, however, then we know to skip the clause
+ // and fall through to below.
+ if let If::Clause(if_instr) = i {
+ let instr = mem::replace(if_instr, Instruction::End(None));
+ *i = If::Then(instr);
+ if !parser.peek::<kw::then>() {
+ return Ok(false);
+ }
+ }
+
+ // All `if` statements are required to have a `then`. This is either the
+ // second s-expr (with or without a leading `then`) or the first s-expr
+ // with a leading `then`. The optionality of `then` isn't strictly what
+ // the text spec says but it matches wabt for now.
+ //
+ // Note that when we see the `then`, that's when we actually add the
+ // original `if` instruction to the stream.
+ if let If::Then(if_instr) = i {
+ let instr = mem::replace(if_instr, Instruction::End(None));
+ self.instrs.push(instr);
+ *i = If::Else;
+ if parser.parse::<Option<kw::then>>()?.is_some() {
+ self.stack.push(Level::IfArm);
+ return Ok(true);
+ }
+ return Ok(false);
+ }
+
+ // effectively the same as the `then` parsing above
+ if let If::Else = i {
+ self.instrs.push(Instruction::Else(None));
+ if parser.parse::<Option<kw::r#else>>()?.is_some() {
+ if parser.is_empty() {
+ self.instrs.pop();
+ }
+ self.stack.push(Level::IfArm);
+ return Ok(true);
+ }
+ *i = If::End;
+ return Ok(false);
+ }
+
+ // If we made it this far then we're at `If::End` which means that there
+ // were too many s-expressions inside the `(if)` and we don't want to
+ // parse anything else.
+ Err(parser.error("too many payloads inside of `(if)`"))
+ }
+
+ /// Handles parsing of a `try` statement. A `try` statement is simpler
+ /// than an `if` as the syntactic form is:
+ ///
+ /// ```wat
+ /// (try (do $do) (catch $event $catch))
+ /// ```
+ ///
+ /// where the `do` and `catch` keywords are mandatory, even for an empty
+ /// $do or $catch.
+ ///
+ /// Returns `true` if the rest of the arm above should be skipped, or
+ /// `false` if we should parse the next item as an instruction (because we
+ /// didn't handle the lparen here).
+ fn handle_try_lparen(&mut self, parser: Parser<'a>) -> Result<bool> {
+ // Only execute the code below if there's a `Try` listed last.
+ let i = match self.stack.last_mut() {
+ Some(Level::Try(i)) => i,
+ _ => return Ok(false),
+ };
+
+ // Try statements must start with a `do` block.
+ if let Try::Do(try_instr) = i {
+ let instr = mem::replace(try_instr, Instruction::End(None));
+ self.instrs.push(instr);
+ if parser.parse::<Option<kw::r#do>>()?.is_some() {
+ // The state is advanced here only if the parse succeeds in
+ // order to strictly require the keyword.
+ *i = Try::CatchOrUnwind;
+ self.stack.push(Level::TryArm);
+ return Ok(true);
+ }
+ // We return here and continue parsing instead of raising an error
+ // immediately because the missing keyword will be caught more
+ // generally in the `Paren::Right` case in `parse`.
+ return Ok(false);
+ }
+
+ // After a try's `do`, there are several possible kinds of handlers.
+ if let Try::CatchOrUnwind = i {
+ // `catch` may be followed by more `catch`s or `catch_all`.
+ if parser.parse::<Option<kw::catch>>()?.is_some() {
+ let evt = parser.parse::<ast::Index<'a>>()?;
+ self.instrs.push(Instruction::Catch(evt));
+ *i = Try::Catch;
+ self.stack.push(Level::TryArm);
+ return Ok(true);
+ }
+ // `catch_all` can only come at the end and has no argument.
+ if parser.parse::<Option<kw::catch_all>>()?.is_some() {
+ self.instrs.push(Instruction::CatchAll);
+ *i = Try::End;
+ self.stack.push(Level::TryArm);
+ return Ok(true);
+ }
+ // `unwind` is similar to `catch_all`.
+ if parser.parse::<Option<kw::unwind>>()?.is_some() {
+ self.instrs.push(Instruction::Unwind);
+ *i = Try::End;
+ self.stack.push(Level::TryArm);
+ return Ok(true);
+ }
+ return Ok(false);
+ }
+
+ if let Try::Catch = i {
+ if parser.parse::<Option<kw::catch>>()?.is_some() {
+ let evt = parser.parse::<ast::Index<'a>>()?;
+ self.instrs.push(Instruction::Catch(evt));
+ *i = Try::Catch;
+ self.stack.push(Level::TryArm);
+ return Ok(true);
+ }
+ if parser.parse::<Option<kw::catch_all>>()?.is_some() {
+ self.instrs.push(Instruction::CatchAll);
+ *i = Try::End;
+ self.stack.push(Level::TryArm);
+ return Ok(true);
+ }
+ return Err(parser.error("unexpected items after `catch`"));
+ }
+
+ Err(parser.error("too many payloads inside of `(try)`"))
+ }
+}
+
+// TODO: document this obscenity
+macro_rules! instructions {
+ (pub enum Instruction<'a> {
+ $(
+ $(#[$doc:meta])*
+ $name:ident $(($($arg:tt)*))? : [$($binary:tt)*] : $instr:tt $( | $deprecated:tt )?,
+ )*
+ }) => (
+ /// A listing of all WebAssembly instructions that can be in a module
+ /// that this crate currently parses.
+ #[derive(Debug)]
+ #[allow(missing_docs)]
+ pub enum Instruction<'a> {
+ $(
+ $(#[$doc])*
+ $name $(( instructions!(@ty $($arg)*) ))?,
+ )*
+ }
+
+ #[allow(non_snake_case)]
+ impl<'a> Parse<'a> for Instruction<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ $(
+ fn $name<'a>(_parser: Parser<'a>) -> Result<Instruction<'a>> {
+ Ok(Instruction::$name $((
+ instructions!(@parse _parser $($arg)*)?
+ ))?)
+ }
+ )*
+ let parse_remainder = parser.step(|c| {
+ let (kw, rest) = match c.keyword() {
+ Some(pair) => pair,
+ None => return Err(c.error("expected an instruction")),
+ };
+ match kw {
+ $($instr $( | $deprecated )?=> Ok(($name as fn(_) -> _, rest)),)*
+ _ => return Err(c.error("unknown operator or unexpected token")),
+ }
+ })?;
+ parse_remainder(parser)
+ }
+ }
+
+ impl crate::binary::Encode for Instruction<'_> {
+ #[allow(non_snake_case)]
+ fn encode(&self, v: &mut Vec<u8>) {
+ match self {
+ $(
+ Instruction::$name $((instructions!(@first $($arg)*)))? => {
+ fn encode<'a>($(arg: &instructions!(@ty $($arg)*),)? v: &mut Vec<u8>) {
+ instructions!(@encode v $($binary)*);
+ $(<instructions!(@ty $($arg)*) as crate::binary::Encode>::encode(arg, v);)?
+ }
+ encode($( instructions!(@first $($arg)*), )? v)
+ }
+ )*
+ }
+ }
+ }
+
+ impl<'a> Instruction<'a> {
+ /// Returns the associated [`MemArg`] if one is available for this
+ /// instruction.
+ #[allow(unused_variables, non_snake_case)]
+ pub fn memarg_mut(&mut self) -> Option<&mut MemArg<'a>> {
+ match self {
+ $(
+ Instruction::$name $((instructions!(@memarg_binding a $($arg)*)))? => {
+ instructions!(@get_memarg a $($($arg)*)?)
+ }
+ )*
+ }
+ }
+ }
+ );
+
+ (@ty MemArg<$amt:tt>) => (MemArg<'a>);
+ (@ty LoadOrStoreLane<$amt:tt>) => (LoadOrStoreLane<'a>);
+ (@ty $other:ty) => ($other);
+
+ (@first $first:ident $($t:tt)*) => ($first);
+
+ (@parse $parser:ident MemArg<$amt:tt>) => (MemArg::parse($parser, $amt));
+ (@parse $parser:ident MemArg) => (compile_error!("must specify `MemArg` default"));
+ (@parse $parser:ident LoadOrStoreLane<$amt:tt>) => (LoadOrStoreLane::parse($parser, $amt));
+ (@parse $parser:ident LoadOrStoreLane) => (compile_error!("must specify `LoadOrStoreLane` default"));
+ (@parse $parser:ident $other:ty) => ($parser.parse::<$other>());
+
+ // simd opcodes prefixed with `0xfd` get a varuint32 encoding for their payload
+ (@encode $dst:ident 0xfd, $simd:tt) => ({
+ $dst.push(0xfd);
+ <u32 as crate::binary::Encode>::encode(&$simd, $dst);
+ });
+ (@encode $dst:ident $($bytes:tt)*) => ($dst.extend_from_slice(&[$($bytes)*]););
+
+ (@get_memarg $name:ident MemArg<$amt:tt>) => (Some($name));
+ (@get_memarg $($other:tt)*) => (None);
+
+ (@memarg_binding $name:ident MemArg<$amt:tt>) => ($name);
+ (@memarg_binding $name:ident $other:ty) => (_);
+}
+
+instructions! {
+ pub enum Instruction<'a> {
+ Block(BlockType<'a>) : [0x02] : "block",
+ If(BlockType<'a>) : [0x04] : "if",
+ Else(Option<ast::Id<'a>>) : [0x05] : "else",
+ Loop(BlockType<'a>) : [0x03] : "loop",
+ End(Option<ast::Id<'a>>) : [0x0b] : "end",
+
+ Unreachable : [0x00] : "unreachable",
+ Nop : [0x01] : "nop",
+ Br(ast::Index<'a>) : [0x0c] : "br",
+ BrIf(ast::Index<'a>) : [0x0d] : "br_if",
+ BrTable(BrTableIndices<'a>) : [0x0e] : "br_table",
+ Return : [0x0f] : "return",
+ Call(ast::IndexOrRef<'a, kw::func>) : [0x10] : "call",
+ CallIndirect(CallIndirect<'a>) : [0x11] : "call_indirect",
+
+ // tail-call proposal
+ ReturnCall(ast::IndexOrRef<'a, kw::func>) : [0x12] : "return_call",
+ ReturnCallIndirect(CallIndirect<'a>) : [0x13] : "return_call_indirect",
+
+ // function-references proposal
+ CallRef : [0x14] : "call_ref",
+ ReturnCallRef : [0x15] : "return_call_ref",
+ FuncBind(FuncBindType<'a>) : [0x16] : "func.bind",
+ Let(LetType<'a>) : [0x17] : "let",
+
+ Drop : [0x1a] : "drop",
+ Select(SelectTypes<'a>) : [] : "select",
+ LocalGet(ast::Index<'a>) : [0x20] : "local.get" | "get_local",
+ LocalSet(ast::Index<'a>) : [0x21] : "local.set" | "set_local",
+ LocalTee(ast::Index<'a>) : [0x22] : "local.tee" | "tee_local",
+ GlobalGet(ast::IndexOrRef<'a, kw::global>) : [0x23] : "global.get" | "get_global",
+ GlobalSet(ast::IndexOrRef<'a, kw::global>) : [0x24] : "global.set" | "set_global",
+
+ TableGet(TableArg<'a>) : [0x25] : "table.get",
+ TableSet(TableArg<'a>) : [0x26] : "table.set",
+
+ I32Load(MemArg<4>) : [0x28] : "i32.load",
+ I64Load(MemArg<8>) : [0x29] : "i64.load",
+ F32Load(MemArg<4>) : [0x2a] : "f32.load",
+ F64Load(MemArg<8>) : [0x2b] : "f64.load",
+ I32Load8s(MemArg<1>) : [0x2c] : "i32.load8_s",
+ I32Load8u(MemArg<1>) : [0x2d] : "i32.load8_u",
+ I32Load16s(MemArg<2>) : [0x2e] : "i32.load16_s",
+ I32Load16u(MemArg<2>) : [0x2f] : "i32.load16_u",
+ I64Load8s(MemArg<1>) : [0x30] : "i64.load8_s",
+ I64Load8u(MemArg<1>) : [0x31] : "i64.load8_u",
+ I64Load16s(MemArg<2>) : [0x32] : "i64.load16_s",
+ I64Load16u(MemArg<2>) : [0x33] : "i64.load16_u",
+ I64Load32s(MemArg<4>) : [0x34] : "i64.load32_s",
+ I64Load32u(MemArg<4>) : [0x35] : "i64.load32_u",
+ I32Store(MemArg<4>) : [0x36] : "i32.store",
+ I64Store(MemArg<8>) : [0x37] : "i64.store",
+ F32Store(MemArg<4>) : [0x38] : "f32.store",
+ F64Store(MemArg<8>) : [0x39] : "f64.store",
+ I32Store8(MemArg<1>) : [0x3a] : "i32.store8",
+ I32Store16(MemArg<2>) : [0x3b] : "i32.store16",
+ I64Store8(MemArg<1>) : [0x3c] : "i64.store8",
+ I64Store16(MemArg<2>) : [0x3d] : "i64.store16",
+ I64Store32(MemArg<4>) : [0x3e] : "i64.store32",
+
+ // Lots of bulk memory proposal here as well
+ MemorySize(MemoryArg<'a>) : [0x3f] : "memory.size" | "current_memory",
+ MemoryGrow(MemoryArg<'a>) : [0x40] : "memory.grow" | "grow_memory",
+ MemoryInit(MemoryInit<'a>) : [0xfc, 0x08] : "memory.init",
+ MemoryCopy(MemoryCopy<'a>) : [0xfc, 0x0a] : "memory.copy",
+ MemoryFill(MemoryArg<'a>) : [0xfc, 0x0b] : "memory.fill",
+ DataDrop(ast::Index<'a>) : [0xfc, 0x09] : "data.drop",
+ ElemDrop(ast::Index<'a>) : [0xfc, 0x0d] : "elem.drop",
+ TableInit(TableInit<'a>) : [0xfc, 0x0c] : "table.init",
+ TableCopy(TableCopy<'a>) : [0xfc, 0x0e] : "table.copy",
+ TableFill(TableArg<'a>) : [0xfc, 0x11] : "table.fill",
+ TableSize(TableArg<'a>) : [0xfc, 0x10] : "table.size",
+ TableGrow(TableArg<'a>) : [0xfc, 0x0f] : "table.grow",
+
+ RefNull(HeapType<'a>) : [0xd0] : "ref.null",
+ RefIsNull : [0xd1] : "ref.is_null",
+ RefExtern(u32) : [0xff] : "ref.extern", // only used in test harness
+ RefFunc(ast::IndexOrRef<'a, kw::func>) : [0xd2] : "ref.func",
+
+ // function-references proposal
+ RefAsNonNull : [0xd3] : "ref.as_non_null",
+ BrOnNull(ast::Index<'a>) : [0xd4] : "br_on_null",
+
+ // gc proposal: eqref
+ RefEq : [0xd5] : "ref.eq",
+
+ // gc proposal (moz specific, will be removed)
+ StructNew(ast::Index<'a>) : [0xfb, 0x0] : "struct.new",
+
+ // gc proposal: struct
+ StructNewWithRtt(ast::Index<'a>) : [0xfb, 0x01] : "struct.new_with_rtt",
+ StructNewDefaultWithRtt(ast::Index<'a>) : [0xfb, 0x02] : "struct.new_default_with_rtt",
+ StructGet(StructAccess<'a>) : [0xfb, 0x03] : "struct.get",
+ StructGetS(StructAccess<'a>) : [0xfb, 0x04] : "struct.get_s",
+ StructGetU(StructAccess<'a>) : [0xfb, 0x05] : "struct.get_u",
+ StructSet(StructAccess<'a>) : [0xfb, 0x06] : "struct.set",
+
+ // gc proposal (moz specific, will be removed)
+ StructNarrow(StructNarrow<'a>) : [0xfb, 0x07] : "struct.narrow",
+
+ // gc proposal: array
+ ArrayNewWithRtt(ast::Index<'a>) : [0xfb, 0x11] : "array.new_with_rtt",
+ ArrayNewDefaultWithRtt(ast::Index<'a>) : [0xfb, 0x12] : "array.new_default_with_rtt",
+ ArrayGet(ast::Index<'a>) : [0xfb, 0x13] : "array.get",
+ ArrayGetS(ast::Index<'a>) : [0xfb, 0x14] : "array.get_s",
+ ArrayGetU(ast::Index<'a>) : [0xfb, 0x15] : "array.get_u",
+ ArraySet(ast::Index<'a>) : [0xfb, 0x16] : "array.set",
+ ArrayLen(ast::Index<'a>) : [0xfb, 0x17] : "array.len",
+
+ // gc proposal, i31
+ I31New : [0xfb, 0x20] : "i31.new",
+ I31GetS : [0xfb, 0x21] : "i31.get_s",
+ I31GetU : [0xfb, 0x22] : "i31.get_u",
+
+ // gc proposal, rtt/casting
+ RTTCanon(HeapType<'a>) : [0xfb, 0x30] : "rtt.canon",
+ RTTSub(RTTSub<'a>) : [0xfb, 0x31] : "rtt.sub",
+ RefTest(RefTest<'a>) : [0xfb, 0x40] : "ref.test",
+ RefCast(RefTest<'a>) : [0xfb, 0x41] : "ref.cast",
+ BrOnCast(BrOnCast<'a>) : [0xfb, 0x42] : "br_on_cast",
+
+ I32Const(i32) : [0x41] : "i32.const",
+ I64Const(i64) : [0x42] : "i64.const",
+ F32Const(ast::Float32) : [0x43] : "f32.const",
+ F64Const(ast::Float64) : [0x44] : "f64.const",
+
+ I32Clz : [0x67] : "i32.clz",
+ I32Ctz : [0x68] : "i32.ctz",
+ I32Popcnt : [0x69] : "i32.popcnt",
+ I32Add : [0x6a] : "i32.add",
+ I32Sub : [0x6b] : "i32.sub",
+ I32Mul : [0x6c] : "i32.mul",
+ I32DivS : [0x6d] : "i32.div_s",
+ I32DivU : [0x6e] : "i32.div_u",
+ I32RemS : [0x6f] : "i32.rem_s",
+ I32RemU : [0x70] : "i32.rem_u",
+ I32And : [0x71] : "i32.and",
+ I32Or : [0x72] : "i32.or",
+ I32Xor : [0x73] : "i32.xor",
+ I32Shl : [0x74] : "i32.shl",
+ I32ShrS : [0x75] : "i32.shr_s",
+ I32ShrU : [0x76] : "i32.shr_u",
+ I32Rotl : [0x77] : "i32.rotl",
+ I32Rotr : [0x78] : "i32.rotr",
+
+ I64Clz : [0x79] : "i64.clz",
+ I64Ctz : [0x7a] : "i64.ctz",
+ I64Popcnt : [0x7b] : "i64.popcnt",
+ I64Add : [0x7c] : "i64.add",
+ I64Sub : [0x7d] : "i64.sub",
+ I64Mul : [0x7e] : "i64.mul",
+ I64DivS : [0x7f] : "i64.div_s",
+ I64DivU : [0x80] : "i64.div_u",
+ I64RemS : [0x81] : "i64.rem_s",
+ I64RemU : [0x82] : "i64.rem_u",
+ I64And : [0x83] : "i64.and",
+ I64Or : [0x84] : "i64.or",
+ I64Xor : [0x85] : "i64.xor",
+ I64Shl : [0x86] : "i64.shl",
+ I64ShrS : [0x87] : "i64.shr_s",
+ I64ShrU : [0x88] : "i64.shr_u",
+ I64Rotl : [0x89] : "i64.rotl",
+ I64Rotr : [0x8a] : "i64.rotr",
+
+ F32Abs : [0x8b] : "f32.abs",
+ F32Neg : [0x8c] : "f32.neg",
+ F32Ceil : [0x8d] : "f32.ceil",
+ F32Floor : [0x8e] : "f32.floor",
+ F32Trunc : [0x8f] : "f32.trunc",
+ F32Nearest : [0x90] : "f32.nearest",
+ F32Sqrt : [0x91] : "f32.sqrt",
+ F32Add : [0x92] : "f32.add",
+ F32Sub : [0x93] : "f32.sub",
+ F32Mul : [0x94] : "f32.mul",
+ F32Div : [0x95] : "f32.div",
+ F32Min : [0x96] : "f32.min",
+ F32Max : [0x97] : "f32.max",
+ F32Copysign : [0x98] : "f32.copysign",
+
+ F64Abs : [0x99] : "f64.abs",
+ F64Neg : [0x9a] : "f64.neg",
+ F64Ceil : [0x9b] : "f64.ceil",
+ F64Floor : [0x9c] : "f64.floor",
+ F64Trunc : [0x9d] : "f64.trunc",
+ F64Nearest : [0x9e] : "f64.nearest",
+ F64Sqrt : [0x9f] : "f64.sqrt",
+ F64Add : [0xa0] : "f64.add",
+ F64Sub : [0xa1] : "f64.sub",
+ F64Mul : [0xa2] : "f64.mul",
+ F64Div : [0xa3] : "f64.div",
+ F64Min : [0xa4] : "f64.min",
+ F64Max : [0xa5] : "f64.max",
+ F64Copysign : [0xa6] : "f64.copysign",
+
+ I32Eqz : [0x45] : "i32.eqz",
+ I32Eq : [0x46] : "i32.eq",
+ I32Ne : [0x47] : "i32.ne",
+ I32LtS : [0x48] : "i32.lt_s",
+ I32LtU : [0x49] : "i32.lt_u",
+ I32GtS : [0x4a] : "i32.gt_s",
+ I32GtU : [0x4b] : "i32.gt_u",
+ I32LeS : [0x4c] : "i32.le_s",
+ I32LeU : [0x4d] : "i32.le_u",
+ I32GeS : [0x4e] : "i32.ge_s",
+ I32GeU : [0x4f] : "i32.ge_u",
+
+ I64Eqz : [0x50] : "i64.eqz",
+ I64Eq : [0x51] : "i64.eq",
+ I64Ne : [0x52] : "i64.ne",
+ I64LtS : [0x53] : "i64.lt_s",
+ I64LtU : [0x54] : "i64.lt_u",
+ I64GtS : [0x55] : "i64.gt_s",
+ I64GtU : [0x56] : "i64.gt_u",
+ I64LeS : [0x57] : "i64.le_s",
+ I64LeU : [0x58] : "i64.le_u",
+ I64GeS : [0x59] : "i64.ge_s",
+ I64GeU : [0x5a] : "i64.ge_u",
+
+ F32Eq : [0x5b] : "f32.eq",
+ F32Ne : [0x5c] : "f32.ne",
+ F32Lt : [0x5d] : "f32.lt",
+ F32Gt : [0x5e] : "f32.gt",
+ F32Le : [0x5f] : "f32.le",
+ F32Ge : [0x60] : "f32.ge",
+
+ F64Eq : [0x61] : "f64.eq",
+ F64Ne : [0x62] : "f64.ne",
+ F64Lt : [0x63] : "f64.lt",
+ F64Gt : [0x64] : "f64.gt",
+ F64Le : [0x65] : "f64.le",
+ F64Ge : [0x66] : "f64.ge",
+
+ I32WrapI64 : [0xa7] : "i32.wrap_i64" | "i32.wrap/i64",
+ I32TruncF32S : [0xa8] : "i32.trunc_f32_s" | "i32.trunc_s/f32",
+ I32TruncF32U : [0xa9] : "i32.trunc_f32_u" | "i32.trunc_u/f32",
+ I32TruncF64S : [0xaa] : "i32.trunc_f64_s" | "i32.trunc_s/f64",
+ I32TruncF64U : [0xab] : "i32.trunc_f64_u" | "i32.trunc_u/f64",
+ I64ExtendI32S : [0xac] : "i64.extend_i32_s" | "i64.extend_s/i32",
+ I64ExtendI32U : [0xad] : "i64.extend_i32_u" | "i64.extend_u/i32",
+ I64TruncF32S : [0xae] : "i64.trunc_f32_s" | "i64.trunc_s/f32",
+ I64TruncF32U : [0xaf] : "i64.trunc_f32_u" | "i64.trunc_u/f32",
+ I64TruncF64S : [0xb0] : "i64.trunc_f64_s" | "i64.trunc_s/f64",
+ I64TruncF64U : [0xb1] : "i64.trunc_f64_u" | "i64.trunc_u/f64",
+ F32ConvertI32S : [0xb2] : "f32.convert_i32_s" | "f32.convert_s/i32",
+ F32ConvertI32U : [0xb3] : "f32.convert_i32_u" | "f32.convert_u/i32",
+ F32ConvertI64S : [0xb4] : "f32.convert_i64_s" | "f32.convert_s/i64",
+ F32ConvertI64U : [0xb5] : "f32.convert_i64_u" | "f32.convert_u/i64",
+ F32DemoteF64 : [0xb6] : "f32.demote_f64" | "f32.demote/f64",
+ F64ConvertI32S : [0xb7] : "f64.convert_i32_s" | "f64.convert_s/i32",
+ F64ConvertI32U : [0xb8] : "f64.convert_i32_u" | "f64.convert_u/i32",
+ F64ConvertI64S : [0xb9] : "f64.convert_i64_s" | "f64.convert_s/i64",
+ F64ConvertI64U : [0xba] : "f64.convert_i64_u" | "f64.convert_u/i64",
+ F64PromoteF32 : [0xbb] : "f64.promote_f32" | "f64.promote/f32",
+ I32ReinterpretF32 : [0xbc] : "i32.reinterpret_f32" | "i32.reinterpret/f32",
+ I64ReinterpretF64 : [0xbd] : "i64.reinterpret_f64" | "i64.reinterpret/f64",
+ F32ReinterpretI32 : [0xbe] : "f32.reinterpret_i32" | "f32.reinterpret/i32",
+ F64ReinterpretI64 : [0xbf] : "f64.reinterpret_i64" | "f64.reinterpret/i64",
+
+ // non-trapping float to int
+ I32TruncSatF32S : [0xfc, 0x00] : "i32.trunc_sat_f32_s" | "i32.trunc_s:sat/f32",
+ I32TruncSatF32U : [0xfc, 0x01] : "i32.trunc_sat_f32_u" | "i32.trunc_u:sat/f32",
+ I32TruncSatF64S : [0xfc, 0x02] : "i32.trunc_sat_f64_s" | "i32.trunc_s:sat/f64",
+ I32TruncSatF64U : [0xfc, 0x03] : "i32.trunc_sat_f64_u" | "i32.trunc_u:sat/f64",
+ I64TruncSatF32S : [0xfc, 0x04] : "i64.trunc_sat_f32_s" | "i64.trunc_s:sat/f32",
+ I64TruncSatF32U : [0xfc, 0x05] : "i64.trunc_sat_f32_u" | "i64.trunc_u:sat/f32",
+ I64TruncSatF64S : [0xfc, 0x06] : "i64.trunc_sat_f64_s" | "i64.trunc_s:sat/f64",
+ I64TruncSatF64U : [0xfc, 0x07] : "i64.trunc_sat_f64_u" | "i64.trunc_u:sat/f64",
+
+ // sign extension proposal
+ I32Extend8S : [0xc0] : "i32.extend8_s",
+ I32Extend16S : [0xc1] : "i32.extend16_s",
+ I64Extend8S : [0xc2] : "i64.extend8_s",
+ I64Extend16S : [0xc3] : "i64.extend16_s",
+ I64Extend32S : [0xc4] : "i64.extend32_s",
+
+ // atomics proposal
+ MemoryAtomicNotify(MemArg<4>) : [0xfe, 0x00] : "memory.atomic.notify" | "atomic.notify",
+ MemoryAtomicWait32(MemArg<4>) : [0xfe, 0x01] : "memory.atomic.wait32" | "i32.atomic.wait",
+ MemoryAtomicWait64(MemArg<8>) : [0xfe, 0x02] : "memory.atomic.wait64" | "i64.atomic.wait",
+ AtomicFence : [0xfe, 0x03, 0x00] : "atomic.fence",
+
+ I32AtomicLoad(MemArg<4>) : [0xfe, 0x10] : "i32.atomic.load",
+ I64AtomicLoad(MemArg<8>) : [0xfe, 0x11] : "i64.atomic.load",
+ I32AtomicLoad8u(MemArg<1>) : [0xfe, 0x12] : "i32.atomic.load8_u",
+ I32AtomicLoad16u(MemArg<2>) : [0xfe, 0x13] : "i32.atomic.load16_u",
+ I64AtomicLoad8u(MemArg<1>) : [0xfe, 0x14] : "i64.atomic.load8_u",
+ I64AtomicLoad16u(MemArg<2>) : [0xfe, 0x15] : "i64.atomic.load16_u",
+ I64AtomicLoad32u(MemArg<4>) : [0xfe, 0x16] : "i64.atomic.load32_u",
+ I32AtomicStore(MemArg<4>) : [0xfe, 0x17] : "i32.atomic.store",
+ I64AtomicStore(MemArg<8>) : [0xfe, 0x18] : "i64.atomic.store",
+ I32AtomicStore8(MemArg<1>) : [0xfe, 0x19] : "i32.atomic.store8",
+ I32AtomicStore16(MemArg<2>) : [0xfe, 0x1a] : "i32.atomic.store16",
+ I64AtomicStore8(MemArg<1>) : [0xfe, 0x1b] : "i64.atomic.store8",
+ I64AtomicStore16(MemArg<2>) : [0xfe, 0x1c] : "i64.atomic.store16",
+ I64AtomicStore32(MemArg<4>) : [0xfe, 0x1d] : "i64.atomic.store32",
+
+ I32AtomicRmwAdd(MemArg<4>) : [0xfe, 0x1e] : "i32.atomic.rmw.add",
+ I64AtomicRmwAdd(MemArg<8>) : [0xfe, 0x1f] : "i64.atomic.rmw.add",
+ I32AtomicRmw8AddU(MemArg<1>) : [0xfe, 0x20] : "i32.atomic.rmw8.add_u",
+ I32AtomicRmw16AddU(MemArg<2>) : [0xfe, 0x21] : "i32.atomic.rmw16.add_u",
+ I64AtomicRmw8AddU(MemArg<1>) : [0xfe, 0x22] : "i64.atomic.rmw8.add_u",
+ I64AtomicRmw16AddU(MemArg<2>) : [0xfe, 0x23] : "i64.atomic.rmw16.add_u",
+ I64AtomicRmw32AddU(MemArg<4>) : [0xfe, 0x24] : "i64.atomic.rmw32.add_u",
+
+ I32AtomicRmwSub(MemArg<4>) : [0xfe, 0x25] : "i32.atomic.rmw.sub",
+ I64AtomicRmwSub(MemArg<8>) : [0xfe, 0x26] : "i64.atomic.rmw.sub",
+ I32AtomicRmw8SubU(MemArg<1>) : [0xfe, 0x27] : "i32.atomic.rmw8.sub_u",
+ I32AtomicRmw16SubU(MemArg<2>) : [0xfe, 0x28] : "i32.atomic.rmw16.sub_u",
+ I64AtomicRmw8SubU(MemArg<1>) : [0xfe, 0x29] : "i64.atomic.rmw8.sub_u",
+ I64AtomicRmw16SubU(MemArg<2>) : [0xfe, 0x2a] : "i64.atomic.rmw16.sub_u",
+ I64AtomicRmw32SubU(MemArg<4>) : [0xfe, 0x2b] : "i64.atomic.rmw32.sub_u",
+
+ I32AtomicRmwAnd(MemArg<4>) : [0xfe, 0x2c] : "i32.atomic.rmw.and",
+ I64AtomicRmwAnd(MemArg<8>) : [0xfe, 0x2d] : "i64.atomic.rmw.and",
+ I32AtomicRmw8AndU(MemArg<1>) : [0xfe, 0x2e] : "i32.atomic.rmw8.and_u",
+ I32AtomicRmw16AndU(MemArg<2>) : [0xfe, 0x2f] : "i32.atomic.rmw16.and_u",
+ I64AtomicRmw8AndU(MemArg<1>) : [0xfe, 0x30] : "i64.atomic.rmw8.and_u",
+ I64AtomicRmw16AndU(MemArg<2>) : [0xfe, 0x31] : "i64.atomic.rmw16.and_u",
+ I64AtomicRmw32AndU(MemArg<4>) : [0xfe, 0x32] : "i64.atomic.rmw32.and_u",
+
+ I32AtomicRmwOr(MemArg<4>) : [0xfe, 0x33] : "i32.atomic.rmw.or",
+ I64AtomicRmwOr(MemArg<8>) : [0xfe, 0x34] : "i64.atomic.rmw.or",
+ I32AtomicRmw8OrU(MemArg<1>) : [0xfe, 0x35] : "i32.atomic.rmw8.or_u",
+ I32AtomicRmw16OrU(MemArg<2>) : [0xfe, 0x36] : "i32.atomic.rmw16.or_u",
+ I64AtomicRmw8OrU(MemArg<1>) : [0xfe, 0x37] : "i64.atomic.rmw8.or_u",
+ I64AtomicRmw16OrU(MemArg<2>) : [0xfe, 0x38] : "i64.atomic.rmw16.or_u",
+ I64AtomicRmw32OrU(MemArg<4>) : [0xfe, 0x39] : "i64.atomic.rmw32.or_u",
+
+ I32AtomicRmwXor(MemArg<4>) : [0xfe, 0x3a] : "i32.atomic.rmw.xor",
+ I64AtomicRmwXor(MemArg<8>) : [0xfe, 0x3b] : "i64.atomic.rmw.xor",
+ I32AtomicRmw8XorU(MemArg<1>) : [0xfe, 0x3c] : "i32.atomic.rmw8.xor_u",
+ I32AtomicRmw16XorU(MemArg<2>) : [0xfe, 0x3d] : "i32.atomic.rmw16.xor_u",
+ I64AtomicRmw8XorU(MemArg<1>) : [0xfe, 0x3e] : "i64.atomic.rmw8.xor_u",
+ I64AtomicRmw16XorU(MemArg<2>) : [0xfe, 0x3f] : "i64.atomic.rmw16.xor_u",
+ I64AtomicRmw32XorU(MemArg<4>) : [0xfe, 0x40] : "i64.atomic.rmw32.xor_u",
+
+ I32AtomicRmwXchg(MemArg<4>) : [0xfe, 0x41] : "i32.atomic.rmw.xchg",
+ I64AtomicRmwXchg(MemArg<8>) : [0xfe, 0x42] : "i64.atomic.rmw.xchg",
+ I32AtomicRmw8XchgU(MemArg<1>) : [0xfe, 0x43] : "i32.atomic.rmw8.xchg_u",
+ I32AtomicRmw16XchgU(MemArg<2>) : [0xfe, 0x44] : "i32.atomic.rmw16.xchg_u",
+ I64AtomicRmw8XchgU(MemArg<1>) : [0xfe, 0x45] : "i64.atomic.rmw8.xchg_u",
+ I64AtomicRmw16XchgU(MemArg<2>) : [0xfe, 0x46] : "i64.atomic.rmw16.xchg_u",
+ I64AtomicRmw32XchgU(MemArg<4>) : [0xfe, 0x47] : "i64.atomic.rmw32.xchg_u",
+
+ I32AtomicRmwCmpxchg(MemArg<4>) : [0xfe, 0x48] : "i32.atomic.rmw.cmpxchg",
+ I64AtomicRmwCmpxchg(MemArg<8>) : [0xfe, 0x49] : "i64.atomic.rmw.cmpxchg",
+ I32AtomicRmw8CmpxchgU(MemArg<1>) : [0xfe, 0x4a] : "i32.atomic.rmw8.cmpxchg_u",
+ I32AtomicRmw16CmpxchgU(MemArg<2>) : [0xfe, 0x4b] : "i32.atomic.rmw16.cmpxchg_u",
+ I64AtomicRmw8CmpxchgU(MemArg<1>) : [0xfe, 0x4c] : "i64.atomic.rmw8.cmpxchg_u",
+ I64AtomicRmw16CmpxchgU(MemArg<2>) : [0xfe, 0x4d] : "i64.atomic.rmw16.cmpxchg_u",
+ I64AtomicRmw32CmpxchgU(MemArg<4>) : [0xfe, 0x4e] : "i64.atomic.rmw32.cmpxchg_u",
+
+ // proposal: simd
+ V128Load(MemArg<16>) : [0xfd, 0x00] : "v128.load",
+ V128Load8x8S(MemArg<8>) : [0xfd, 0x01] : "v128.load8x8_s",
+ V128Load8x8U(MemArg<8>) : [0xfd, 0x02] : "v128.load8x8_u",
+ V128Load16x4S(MemArg<8>) : [0xfd, 0x03] : "v128.load16x4_s",
+ V128Load16x4U(MemArg<8>) : [0xfd, 0x04] : "v128.load16x4_u",
+ V128Load32x2S(MemArg<8>) : [0xfd, 0x05] : "v128.load32x2_s",
+ V128Load32x2U(MemArg<8>) : [0xfd, 0x06] : "v128.load32x2_u",
+ V128Load8Splat(MemArg<1>) : [0xfd, 0x07] : "v128.load8_splat",
+ V128Load16Splat(MemArg<2>) : [0xfd, 0x08] : "v128.load16_splat",
+ V128Load32Splat(MemArg<4>) : [0xfd, 0x09] : "v128.load32_splat",
+ V128Load64Splat(MemArg<8>) : [0xfd, 0x0a] : "v128.load64_splat",
+ V128Store(MemArg<16>) : [0xfd, 0x0b] : "v128.store",
+
+ V128Const(V128Const) : [0xfd, 0x0c] : "v128.const",
+ I8x16Shuffle(I8x16Shuffle) : [0xfd, 0x0d] : "i8x16.shuffle",
+ I8x16Swizzle : [0xfd, 0x0e] : "i8x16.swizzle",
+
+ I8x16Splat : [0xfd, 0x0f] : "i8x16.splat",
+ I16x8Splat : [0xfd, 0x10] : "i16x8.splat",
+ I32x4Splat : [0xfd, 0x11] : "i32x4.splat",
+ I64x2Splat : [0xfd, 0x12] : "i64x2.splat",
+ F32x4Splat : [0xfd, 0x13] : "f32x4.splat",
+ F64x2Splat : [0xfd, 0x14] : "f64x2.splat",
+
+ I8x16ExtractLaneS(LaneArg) : [0xfd, 0x15] : "i8x16.extract_lane_s",
+ I8x16ExtractLaneU(LaneArg) : [0xfd, 0x16] : "i8x16.extract_lane_u",
+ I8x16ReplaceLane(LaneArg) : [0xfd, 0x17] : "i8x16.replace_lane",
+ I16x8ExtractLaneS(LaneArg) : [0xfd, 0x18] : "i16x8.extract_lane_s",
+ I16x8ExtractLaneU(LaneArg) : [0xfd, 0x19] : "i16x8.extract_lane_u",
+ I16x8ReplaceLane(LaneArg) : [0xfd, 0x1a] : "i16x8.replace_lane",
+ I32x4ExtractLane(LaneArg) : [0xfd, 0x1b] : "i32x4.extract_lane",
+ I32x4ReplaceLane(LaneArg) : [0xfd, 0x1c] : "i32x4.replace_lane",
+ I64x2ExtractLane(LaneArg) : [0xfd, 0x1d] : "i64x2.extract_lane",
+ I64x2ReplaceLane(LaneArg) : [0xfd, 0x1e] : "i64x2.replace_lane",
+ F32x4ExtractLane(LaneArg) : [0xfd, 0x1f] : "f32x4.extract_lane",
+ F32x4ReplaceLane(LaneArg) : [0xfd, 0x20] : "f32x4.replace_lane",
+ F64x2ExtractLane(LaneArg) : [0xfd, 0x21] : "f64x2.extract_lane",
+ F64x2ReplaceLane(LaneArg) : [0xfd, 0x22] : "f64x2.replace_lane",
+
+ I8x16Eq : [0xfd, 0x23] : "i8x16.eq",
+ I8x16Ne : [0xfd, 0x24] : "i8x16.ne",
+ I8x16LtS : [0xfd, 0x25] : "i8x16.lt_s",
+ I8x16LtU : [0xfd, 0x26] : "i8x16.lt_u",
+ I8x16GtS : [0xfd, 0x27] : "i8x16.gt_s",
+ I8x16GtU : [0xfd, 0x28] : "i8x16.gt_u",
+ I8x16LeS : [0xfd, 0x29] : "i8x16.le_s",
+ I8x16LeU : [0xfd, 0x2a] : "i8x16.le_u",
+ I8x16GeS : [0xfd, 0x2b] : "i8x16.ge_s",
+ I8x16GeU : [0xfd, 0x2c] : "i8x16.ge_u",
+ I16x8Eq : [0xfd, 0x2d] : "i16x8.eq",
+ I16x8Ne : [0xfd, 0x2e] : "i16x8.ne",
+ I16x8LtS : [0xfd, 0x2f] : "i16x8.lt_s",
+ I16x8LtU : [0xfd, 0x30] : "i16x8.lt_u",
+ I16x8GtS : [0xfd, 0x31] : "i16x8.gt_s",
+ I16x8GtU : [0xfd, 0x32] : "i16x8.gt_u",
+ I16x8LeS : [0xfd, 0x33] : "i16x8.le_s",
+ I16x8LeU : [0xfd, 0x34] : "i16x8.le_u",
+ I16x8GeS : [0xfd, 0x35] : "i16x8.ge_s",
+ I16x8GeU : [0xfd, 0x36] : "i16x8.ge_u",
+ I32x4Eq : [0xfd, 0x37] : "i32x4.eq",
+ I32x4Ne : [0xfd, 0x38] : "i32x4.ne",
+ I32x4LtS : [0xfd, 0x39] : "i32x4.lt_s",
+ I32x4LtU : [0xfd, 0x3a] : "i32x4.lt_u",
+ I32x4GtS : [0xfd, 0x3b] : "i32x4.gt_s",
+ I32x4GtU : [0xfd, 0x3c] : "i32x4.gt_u",
+ I32x4LeS : [0xfd, 0x3d] : "i32x4.le_s",
+ I32x4LeU : [0xfd, 0x3e] : "i32x4.le_u",
+ I32x4GeS : [0xfd, 0x3f] : "i32x4.ge_s",
+ I32x4GeU : [0xfd, 0x40] : "i32x4.ge_u",
+
+ F32x4Eq : [0xfd, 0x41] : "f32x4.eq",
+ F32x4Ne : [0xfd, 0x42] : "f32x4.ne",
+ F32x4Lt : [0xfd, 0x43] : "f32x4.lt",
+ F32x4Gt : [0xfd, 0x44] : "f32x4.gt",
+ F32x4Le : [0xfd, 0x45] : "f32x4.le",
+ F32x4Ge : [0xfd, 0x46] : "f32x4.ge",
+ F64x2Eq : [0xfd, 0x47] : "f64x2.eq",
+ F64x2Ne : [0xfd, 0x48] : "f64x2.ne",
+ F64x2Lt : [0xfd, 0x49] : "f64x2.lt",
+ F64x2Gt : [0xfd, 0x4a] : "f64x2.gt",
+ F64x2Le : [0xfd, 0x4b] : "f64x2.le",
+ F64x2Ge : [0xfd, 0x4c] : "f64x2.ge",
+
+ V128Not : [0xfd, 0x4d] : "v128.not",
+ V128And : [0xfd, 0x4e] : "v128.and",
+ V128Andnot : [0xfd, 0x4f] : "v128.andnot",
+ V128Or : [0xfd, 0x50] : "v128.or",
+ V128Xor : [0xfd, 0x51] : "v128.xor",
+ V128Bitselect : [0xfd, 0x52] : "v128.bitselect",
+ V128Load8Lane(LoadOrStoreLane<1>) : [0xfd, 0x58] : "v128.load8_lane",
+ V128Load16Lane(LoadOrStoreLane<2>) : [0xfd, 0x59] : "v128.load16_lane",
+ V128Load32Lane(LoadOrStoreLane<4>) : [0xfd, 0x5a] : "v128.load32_lane",
+ V128Load64Lane(LoadOrStoreLane<8>): [0xfd, 0x5b] : "v128.load64_lane",
+ V128Store8Lane(LoadOrStoreLane<1>) : [0xfd, 0x5c] : "v128.store8_lane",
+ V128Store16Lane(LoadOrStoreLane<2>) : [0xfd, 0x5d] : "v128.store16_lane",
+ V128Store32Lane(LoadOrStoreLane<4>) : [0xfd, 0x5e] : "v128.store32_lane",
+ V128Store64Lane(LoadOrStoreLane<8>) : [0xfd, 0x5f] : "v128.store64_lane",
+
+ I8x16Abs : [0xfd, 0x60] : "i8x16.abs",
+ I8x16Neg : [0xfd, 0x61] : "i8x16.neg",
+ V128AnyTrue : [0xfd, 0x62] : "v128.any_true",
+ I8x16AllTrue : [0xfd, 0x63] : "i8x16.all_true",
+ I8x16Bitmask : [0xfd, 0x64] : "i8x16.bitmask",
+ I8x16NarrowI16x8S : [0xfd, 0x65] : "i8x16.narrow_i16x8_s",
+ I8x16NarrowI16x8U : [0xfd, 0x66] : "i8x16.narrow_i16x8_u",
+ I8x16Shl : [0xfd, 0x6b] : "i8x16.shl",
+ I8x16ShrS : [0xfd, 0x6c] : "i8x16.shr_s",
+ I8x16ShrU : [0xfd, 0x6d] : "i8x16.shr_u",
+ I8x16Add : [0xfd, 0x6e] : "i8x16.add",
+ I8x16AddSatS : [0xfd, 0x6f] : "i8x16.add_sat_s",
+ I8x16AddSatU : [0xfd, 0x70] : "i8x16.add_sat_u",
+ I8x16Sub : [0xfd, 0x71] : "i8x16.sub",
+ I8x16SubSatS : [0xfd, 0x72] : "i8x16.sub_sat_s",
+ I8x16SubSatU : [0xfd, 0x73] : "i8x16.sub_sat_u",
+ I8x16MinS : [0xfd, 0x76] : "i8x16.min_s",
+ I8x16MinU : [0xfd, 0x77] : "i8x16.min_u",
+ I8x16MaxS : [0xfd, 0x78] : "i8x16.max_s",
+ I8x16MaxU : [0xfd, 0x79] : "i8x16.max_u",
+ I8x16AvgrU : [0xfd, 0x7b] : "i8x16.avgr_u",
+
+ I16x8Abs : [0xfd, 0x80] : "i16x8.abs",
+ I16x8Neg : [0xfd, 0x81] : "i16x8.neg",
+ I16x8AllTrue : [0xfd, 0x83] : "i16x8.all_true",
+ I16x8Bitmask : [0xfd, 0x84] : "i16x8.bitmask",
+ I16x8NarrowI32x4S : [0xfd, 0x85] : "i16x8.narrow_i32x4_s",
+ I16x8NarrowI32x4U : [0xfd, 0x86] : "i16x8.narrow_i32x4_u",
+ I16x8WidenLowI8x16S : [0xfd, 0x87] : "i16x8.widen_low_i8x16_s",
+ I16x8WidenHighI8x16S : [0xfd, 0x88] : "i16x8.widen_high_i8x16_s",
+ I16x8WidenLowI8x16U : [0xfd, 0x89] : "i16x8.widen_low_i8x16_u",
+ I16x8WidenHighI8x16u : [0xfd, 0x8a] : "i16x8.widen_high_i8x16_u",
+ I16x8Shl : [0xfd, 0x8b] : "i16x8.shl",
+ I16x8ShrS : [0xfd, 0x8c] : "i16x8.shr_s",
+ I16x8ShrU : [0xfd, 0x8d] : "i16x8.shr_u",
+ I16x8Add : [0xfd, 0x8e] : "i16x8.add",
+ I16x8AddSatS : [0xfd, 0x8f] : "i16x8.add_sat_s",
+ I16x8AddSatU : [0xfd, 0x90] : "i16x8.add_sat_u",
+ I16x8Sub : [0xfd, 0x91] : "i16x8.sub",
+ I16x8SubSatS : [0xfd, 0x92] : "i16x8.sub_sat_s",
+ I16x8SubSatU : [0xfd, 0x93] : "i16x8.sub_sat_u",
+ I16x8Mul : [0xfd, 0x95] : "i16x8.mul",
+ I16x8MinS : [0xfd, 0x96] : "i16x8.min_s",
+ I16x8MinU : [0xfd, 0x97] : "i16x8.min_u",
+ I16x8MaxS : [0xfd, 0x98] : "i16x8.max_s",
+ I16x8MaxU : [0xfd, 0x99] : "i16x8.max_u",
+ I16x8ExtMulLowI8x16S : [0xfd, 0x9a] : "i16x8.extmul_low_i8x16_s",
+ I16x8AvgrU : [0xfd, 0x9b] : "i16x8.avgr_u",
+ I16x8Q15MulrSatS : [0xfd, 0x9c] : "i16x8.q15mulr_sat_s",
+ I16x8ExtMulHighI8x16S : [0xfd, 0x9d] : "i16x8.extmul_high_i8x16_s",
+ I16x8ExtMulLowI8x16U : [0xfd, 0x9e] : "i16x8.extmul_low_i8x16_u",
+ I16x8ExtMulHighI8x16U : [0xfd, 0x9f] : "i16x8.extmul_high_i8x16_u",
+
+ I32x4Abs : [0xfd, 0xa0] : "i32x4.abs",
+ I32x4Neg : [0xfd, 0xa1] : "i32x4.neg",
+ I32x4AllTrue : [0xfd, 0xa3] : "i32x4.all_true",
+ I32x4Bitmask : [0xfd, 0xa4] : "i32x4.bitmask",
+ I32x4WidenLowI16x8S : [0xfd, 0xa7] : "i32x4.widen_low_i16x8_s",
+ I32x4WidenHighI16x8S : [0xfd, 0xa8] : "i32x4.widen_high_i16x8_s",
+ I32x4WidenLowI16x8U : [0xfd, 0xa9] : "i32x4.widen_low_i16x8_u",
+ I32x4WidenHighI16x8U : [0xfd, 0xaa] : "i32x4.widen_high_i16x8_u",
+ I32x4Shl : [0xfd, 0xab] : "i32x4.shl",
+ I32x4ShrS : [0xfd, 0xac] : "i32x4.shr_s",
+ I32x4ShrU : [0xfd, 0xad] : "i32x4.shr_u",
+ I32x4Add : [0xfd, 0xae] : "i32x4.add",
+ I32x4Sub : [0xfd, 0xb1] : "i32x4.sub",
+ I32x4Mul : [0xfd, 0xb5] : "i32x4.mul",
+ I32x4MinS : [0xfd, 0xb6] : "i32x4.min_s",
+ I32x4MinU : [0xfd, 0xb7] : "i32x4.min_u",
+ I32x4MaxS : [0xfd, 0xb8] : "i32x4.max_s",
+ I32x4MaxU : [0xfd, 0xb9] : "i32x4.max_u",
+ I32x4DotI16x8S : [0xfd, 0xba] : "i32x4.dot_i16x8_s",
+ I32x4ExtMulLowI16x8S : [0xfd, 0xbb] : "i32x4.extmul_low_i16x8_s",
+ I32x4ExtMulHighI16x8S : [0xfd, 0xbd] : "i32x4.extmul_high_i16x8_s",
+ I32x4ExtMulLowI16x8U : [0xfd, 0xbe] : "i32x4.extmul_low_i16x8_u",
+ I32x4ExtMulHighI16x8U : [0xfd, 0xbf] : "i32x4.extmul_high_i16x8_u",
+
+ I64x2Neg : [0xfd, 0xc1] : "i64x2.neg",
+ I64x2Shl : [0xfd, 0xcb] : "i64x2.shl",
+ I64x2Bitmask : [0xfd, 0xc4] : "i64x2.bitmask",
+ I64x2WidenLowI32x4S : [0xfd, 0xc7] : "i64x2.widen_low_i32x4_s",
+ I64x2WidenHighI32x4S : [0xfd, 0xc8] : "i64x2.widen_high_i32x4_s",
+ I64x2WidenLowI32x4U : [0xfd, 0xc9] : "i64x2.widen_low_i32x4_u",
+ I64x2WidenHighI32x4U : [0xfd, 0xca] : "i64x2.widen_high_i32x4_u",
+ I64x2ShrS : [0xfd, 0xcc] : "i64x2.shr_s",
+ I64x2ShrU : [0xfd, 0xcd] : "i64x2.shr_u",
+ I64x2Add : [0xfd, 0xce] : "i64x2.add",
+ I64x2Sub : [0xfd, 0xd1] : "i64x2.sub",
+ I64x2ExtMulLowI32x4S : [0xfd, 0xd2] : "i64x2.extmul_low_i32x4_s",
+ I64x2ExtMulHighI32x4S : [0xfd, 0xd3] : "i64x2.extmul_high_i32x4_s",
+ I64x2Mul : [0xfd, 0xd5] : "i64x2.mul",
+ I64x2ExtMulLowI32x4U : [0xfd, 0xd6] : "i64x2.extmul_low_i32x4_u",
+ I64x2ExtMulHighI32x4U : [0xfd, 0xd7] : "i64x2.extmul_high_i32x4_u",
+
+ F32x4Ceil : [0xfd, 0xd8] : "f32x4.ceil",
+ F32x4Floor : [0xfd, 0xd9] : "f32x4.floor",
+ F32x4Trunc : [0xfd, 0xda] : "f32x4.trunc",
+ F32x4Nearest : [0xfd, 0xdb] : "f32x4.nearest",
+ F64x2Ceil : [0xfd, 0xdc] : "f64x2.ceil",
+ F64x2Floor : [0xfd, 0xdd] : "f64x2.floor",
+ F64x2Trunc : [0xfd, 0xde] : "f64x2.trunc",
+ F64x2Nearest : [0xfd, 0xdf] : "f64x2.nearest",
+
+ F32x4Abs : [0xfd, 0xe0] : "f32x4.abs",
+ F32x4Neg : [0xfd, 0xe1] : "f32x4.neg",
+ F32x4Sqrt : [0xfd, 0xe3] : "f32x4.sqrt",
+ F32x4Add : [0xfd, 0xe4] : "f32x4.add",
+ F32x4Sub : [0xfd, 0xe5] : "f32x4.sub",
+ F32x4Mul : [0xfd, 0xe6] : "f32x4.mul",
+ F32x4Div : [0xfd, 0xe7] : "f32x4.div",
+ F32x4Min : [0xfd, 0xe8] : "f32x4.min",
+ F32x4Max : [0xfd, 0xe9] : "f32x4.max",
+ F32x4PMin : [0xfd, 0xea] : "f32x4.pmin",
+ F32x4PMax : [0xfd, 0xeb] : "f32x4.pmax",
+
+ F64x2Abs : [0xfd, 0xec] : "f64x2.abs",
+ F64x2Neg : [0xfd, 0xed] : "f64x2.neg",
+ F64x2Sqrt : [0xfd, 0xef] : "f64x2.sqrt",
+ F64x2Add : [0xfd, 0xf0] : "f64x2.add",
+ F64x2Sub : [0xfd, 0xf1] : "f64x2.sub",
+ F64x2Mul : [0xfd, 0xf2] : "f64x2.mul",
+ F64x2Div : [0xfd, 0xf3] : "f64x2.div",
+ F64x2Min : [0xfd, 0xf4] : "f64x2.min",
+ F64x2Max : [0xfd, 0xf5] : "f64x2.max",
+ F64x2PMin : [0xfd, 0xf6] : "f64x2.pmin",
+ F64x2PMax : [0xfd, 0xf7] : "f64x2.pmax",
+
+ I32x4TruncSatF32x4S : [0xfd, 0xf8] : "i32x4.trunc_sat_f32x4_s",
+ I32x4TruncSatF32x4U : [0xfd, 0xf9] : "i32x4.trunc_sat_f32x4_u",
+ F32x4ConvertI32x4S : [0xfd, 0xfa] : "f32x4.convert_i32x4_s",
+ F32x4ConvertI32x4U : [0xfd, 0xfb] : "f32x4.convert_i32x4_u",
+
+ V128Load32Zero(MemArg<4>) : [0xfd, 0xfc] : "v128.load32_zero",
+ V128Load64Zero(MemArg<8>) : [0xfd, 0xfd] : "v128.load64_zero",
+
+ // Exception handling proposal
+ CatchAll : [0x05] : "catch_all", // Reuses the else opcode.
+ Try(BlockType<'a>) : [0x06] : "try",
+ Catch(ast::Index<'a>) : [0x07] : "catch",
+ Throw(ast::Index<'a>) : [0x08] : "throw",
+ Rethrow(ast::Index<'a>) : [0x09] : "rethrow",
+ Unwind : [0x0a] : "unwind",
+ }
+}
+
+/// Extra information associated with block-related instructions.
+///
+/// This is used to label blocks and also annotate what types are expected for
+/// the block.
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct BlockType<'a> {
+ pub label: Option<ast::Id<'a>>,
+ pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>,
+}
+
+impl<'a> Parse<'a> for BlockType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(BlockType {
+ label: parser.parse()?,
+ ty: parser
+ .parse::<ast::TypeUse<'a, ast::FunctionTypeNoNames<'a>>>()?
+ .into(),
+ })
+ }
+}
+
+/// Extra information associated with the func.bind instruction.
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct FuncBindType<'a> {
+ pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>,
+}
+
+impl<'a> Parse<'a> for FuncBindType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(FuncBindType {
+ ty: parser
+ .parse::<ast::TypeUse<'a, ast::FunctionTypeNoNames<'a>>>()?
+ .into(),
+ })
+ }
+}
+
+/// Extra information associated with the let instruction.
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct LetType<'a> {
+ pub block: BlockType<'a>,
+ pub locals: Vec<ast::Local<'a>>,
+}
+
+impl<'a> Parse<'a> for LetType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(LetType {
+ block: parser.parse()?,
+ locals: ast::Local::parse_remainder(parser)?,
+ })
+ }
+}
+
+/// Extra information associated with the `br_table` instruction.
+#[allow(missing_docs)]
+#[derive(Debug)]
+pub struct BrTableIndices<'a> {
+ pub labels: Vec<ast::Index<'a>>,
+ pub default: ast::Index<'a>,
+}
+
+impl<'a> Parse<'a> for BrTableIndices<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut labels = Vec::new();
+ labels.push(parser.parse()?);
+ while parser.peek::<ast::Index>() {
+ labels.push(parser.parse()?);
+ }
+ let default = labels.pop().unwrap();
+ Ok(BrTableIndices { labels, default })
+ }
+}
+
+/// Payload for lane-related instructions. Unsigned with no + prefix.
+#[derive(Debug)]
+pub struct LaneArg {
+ /// The lane argument.
+ pub lane: u8,
+}
+
+impl<'a> Parse<'a> for LaneArg {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let lane = parser.step(|c| {
+ if let Some((i, rest)) = c.integer() {
+ if i.sign() == None {
+ let (src, radix) = i.val();
+ let val = u8::from_str_radix(src, radix)
+ .map_err(|_| c.error("malformed lane index"))?;
+ Ok((val, rest))
+ } else {
+ Err(c.error("unexpected token"))
+ }
+ } else {
+ Err(c.error("expected a lane index"))
+ }
+ })?;
+ Ok(LaneArg { lane })
+ }
+}
+
+/// Payload for memory-related instructions indicating offset/alignment of
+/// memory accesses.
+#[derive(Debug)]
+pub struct MemArg<'a> {
+ /// The alignment of this access.
+ ///
+ /// This is not stored as a log, this is the actual alignment (e.g. 1, 2, 4,
+ /// 8, etc).
+ pub align: u32,
+ /// The offset, in bytes of this access.
+ pub offset: u32,
+ /// The memory index we're accessing
+ pub memory: ast::ItemRef<'a, kw::memory>,
+}
+
+impl<'a> MemArg<'a> {
+ fn parse(parser: Parser<'a>, default_align: u32) -> Result<Self> {
+ fn parse_field(name: &str, parser: Parser<'_>) -> Result<Option<u32>> {
+ parser.step(|c| {
+ let (kw, rest) = match c.keyword() {
+ Some(p) => p,
+ None => return Ok((None, c)),
+ };
+ if !kw.starts_with(name) {
+ return Ok((None, c));
+ }
+ let kw = &kw[name.len()..];
+ if !kw.starts_with("=") {
+ return Ok((None, c));
+ }
+ let num = &kw[1..];
+ let num = if num.starts_with("0x") {
+ match u32::from_str_radix(&num[2..], 16) {
+ Ok(n) => n,
+ Err(_) => return Err(c.error("i32 constant out of range")),
+ }
+ } else {
+ match num.parse() {
+ Ok(n) => n,
+ Err(_) => return Err(c.error("i32 constant out of range")),
+ }
+ };
+
+ Ok((Some(num), rest))
+ })
+ }
+ let memory = parser
+ .parse::<Option<ast::ItemRef<'a, kw::memory>>>()?
+ .unwrap_or(idx_zero(parser.prev_span(), kw::memory));
+ let offset = parse_field("offset", parser)?.unwrap_or(0);
+ let align = match parse_field("align", parser)? {
+ Some(n) if !n.is_power_of_two() => {
+ return Err(parser.error("alignment must be a power of two"))
+ }
+ n => n.unwrap_or(default_align),
+ };
+
+ Ok(MemArg {
+ offset,
+ align,
+ memory,
+ })
+ }
+}
+
+fn idx_zero<T>(span: ast::Span, mk_kind: fn(ast::Span) -> T) -> ast::ItemRef<'static, T> {
+ ast::ItemRef::Item {
+ kind: mk_kind(span),
+ idx: ast::Index::Num(0, span),
+ exports: Vec::new(),
+ }
+}
+
+/// Extra data associated with the `loadN_lane` and `storeN_lane` instructions.
+#[derive(Debug)]
+pub struct LoadOrStoreLane<'a> {
+ /// The memory argument for this instruction.
+ pub memarg: MemArg<'a>,
+ /// The lane argument for this instruction.
+ pub lane: LaneArg
+}
+
+impl<'a> LoadOrStoreLane<'a> {
+ fn parse(parser: Parser<'a>, default_align: u32) -> Result<Self> {
+ Ok(LoadOrStoreLane {
+ memarg: MemArg::parse(parser, default_align)?,
+ lane: LaneArg::parse(parser)?
+ })
+ }
+}
+
+/// Extra data associated with the `call_indirect` instruction.
+#[derive(Debug)]
+pub struct CallIndirect<'a> {
+ /// The table that this call is going to be indexing.
+ pub table: ast::ItemRef<'a, kw::table>,
+ /// Type type signature that this `call_indirect` instruction is using.
+ pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>,
+}
+
+impl<'a> Parse<'a> for CallIndirect<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let prev_span = parser.prev_span();
+ let mut table: Option<ast::IndexOrRef<_>> = parser.parse()?;
+ let ty = parser.parse::<ast::TypeUse<'a, ast::FunctionTypeNoNames<'a>>>()?;
+ // Turns out the official test suite at this time thinks table
+ // identifiers comes first but wabt's test suites asserts differently
+ // putting them second. Let's just handle both.
+ if table.is_none() {
+ table = parser.parse()?;
+ }
+ Ok(CallIndirect {
+ table: table.map(|i| i.0).unwrap_or(idx_zero(prev_span, kw::table)),
+ ty: ty.into(),
+ })
+ }
+}
+
+/// Extra data associated with the `table.init` instruction
+#[derive(Debug)]
+pub struct TableInit<'a> {
+ /// The index of the table we're copying into.
+ pub table: ast::ItemRef<'a, kw::table>,
+ /// The index of the element segment we're copying into a table.
+ pub elem: ast::Index<'a>,
+}
+
+impl<'a> Parse<'a> for TableInit<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let prev_span = parser.prev_span();
+ let (elem, table) =
+ if parser.peek::<ast::ItemRef<kw::table>>() || parser.peek2::<ast::Index>() {
+ let table = parser.parse::<ast::IndexOrRef<_>>()?.0;
+ (parser.parse()?, table)
+ } else {
+ (parser.parse()?, idx_zero(prev_span, kw::table))
+ };
+ Ok(TableInit { table, elem })
+ }
+}
+
+/// Extra data associated with the `table.copy` instruction.
+#[derive(Debug)]
+pub struct TableCopy<'a> {
+ /// The index of the destination table to copy into.
+ pub dst: ast::ItemRef<'a, kw::table>,
+ /// The index of the source table to copy from.
+ pub src: ast::ItemRef<'a, kw::table>,
+}
+
+impl<'a> Parse<'a> for TableCopy<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let (dst, src) = match parser.parse::<Option<ast::IndexOrRef<_>>>()? {
+ Some(dst) => (dst.0, parser.parse::<ast::IndexOrRef<_>>()?.0),
+ None => (
+ idx_zero(parser.prev_span(), kw::table),
+ idx_zero(parser.prev_span(), kw::table),
+ ),
+ };
+ Ok(TableCopy { dst, src })
+ }
+}
+
+/// Extra data associated with unary table instructions.
+#[derive(Debug)]
+pub struct TableArg<'a> {
+ /// The index of the table argument.
+ pub dst: ast::ItemRef<'a, kw::table>,
+}
+
+impl<'a> Parse<'a> for TableArg<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let dst = if let Some(dst) = parser.parse::<Option<ast::IndexOrRef<_>>>()? {
+ dst.0
+ } else {
+ idx_zero(parser.prev_span(), kw::table)
+ };
+ Ok(TableArg { dst })
+ }
+}
+
+/// Extra data associated with unary memory instructions.
+#[derive(Debug)]
+pub struct MemoryArg<'a> {
+ /// The index of the memory space.
+ pub mem: ast::ItemRef<'a, kw::memory>,
+}
+
+impl<'a> Parse<'a> for MemoryArg<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mem = if let Some(mem) = parser.parse::<Option<ast::IndexOrRef<_>>>()? {
+ mem.0
+ } else {
+ idx_zero(parser.prev_span(), kw::memory)
+ };
+ Ok(MemoryArg { mem })
+ }
+}
+
+/// Extra data associated with the `memory.init` instruction
+#[derive(Debug)]
+pub struct MemoryInit<'a> {
+ /// The index of the data segment we're copying into memory.
+ pub data: ast::Index<'a>,
+ /// The index of the memory we're copying into,
+ pub mem: ast::ItemRef<'a, kw::memory>,
+}
+
+impl<'a> Parse<'a> for MemoryInit<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let data = parser.parse()?;
+ let mem = parser
+ .parse::<Option<ast::IndexOrRef<_>>>()?
+ .map(|i| i.0)
+ .unwrap_or(idx_zero(parser.prev_span(), kw::memory));
+ Ok(MemoryInit { data, mem })
+ }
+}
+
+/// Extra data associated with the `memory.copy` instruction
+#[derive(Debug)]
+pub struct MemoryCopy<'a> {
+ /// The index of the memory we're copying from.
+ pub src: ast::ItemRef<'a, kw::memory>,
+ /// The index of the memory we're copying to.
+ pub dst: ast::ItemRef<'a, kw::memory>,
+}
+
+impl<'a> Parse<'a> for MemoryCopy<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let (src, dst) = match parser.parse::<Option<ast::IndexOrRef<_>>>()? {
+ Some(dst) => (parser.parse::<ast::IndexOrRef<_>>()?.0, dst.0),
+ None => (
+ idx_zero(parser.prev_span(), kw::memory),
+ idx_zero(parser.prev_span(), kw::memory),
+ ),
+ };
+ Ok(MemoryCopy { src, dst })
+ }
+}
+
+/// Extra data associated with the `struct.get/set` instructions
+#[derive(Debug)]
+pub struct StructAccess<'a> {
+ /// The index of the struct type we're accessing.
+ pub r#struct: ast::Index<'a>,
+ /// The index of the field of the struct we're accessing
+ pub field: ast::Index<'a>,
+}
+
+impl<'a> Parse<'a> for StructAccess<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(StructAccess {
+ r#struct: parser.parse()?,
+ field: parser.parse()?,
+ })
+ }
+}
+
+/// Extra data associated with the `struct.narrow` instruction
+#[derive(Debug)]
+pub struct StructNarrow<'a> {
+ /// The type of the struct we're casting from
+ pub from: ast::ValType<'a>,
+ /// The type of the struct we're casting to
+ pub to: ast::ValType<'a>,
+}
+
+impl<'a> Parse<'a> for StructNarrow<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(StructNarrow {
+ from: parser.parse()?,
+ to: parser.parse()?,
+ })
+ }
+}
+
+/// Different ways to specify a `v128.const` instruction
+#[derive(Debug)]
+#[rustfmt::skip]
+#[allow(missing_docs)]
+pub enum V128Const {
+ I8x16([i8; 16]),
+ I16x8([i16; 8]),
+ I32x4([i32; 4]),
+ I64x2([i64; 2]),
+ F32x4([ast::Float32; 4]),
+ F64x2([ast::Float64; 2]),
+}
+
+impl V128Const {
+ /// Returns the raw little-ended byte sequence used to represent this
+ /// `v128` constant`
+ ///
+ /// This is typically suitable for encoding as the payload of the
+ /// `v128.const` instruction.
+ #[rustfmt::skip]
+ pub fn to_le_bytes(&self) -> [u8; 16] {
+ match self {
+ V128Const::I8x16(arr) => [
+ arr[0] as u8,
+ arr[1] as u8,
+ arr[2] as u8,
+ arr[3] as u8,
+ arr[4] as u8,
+ arr[5] as u8,
+ arr[6] as u8,
+ arr[7] as u8,
+ arr[8] as u8,
+ arr[9] as u8,
+ arr[10] as u8,
+ arr[11] as u8,
+ arr[12] as u8,
+ arr[13] as u8,
+ arr[14] as u8,
+ arr[15] as u8,
+ ],
+ V128Const::I16x8(arr) => {
+ let a1 = arr[0].to_le_bytes();
+ let a2 = arr[1].to_le_bytes();
+ let a3 = arr[2].to_le_bytes();
+ let a4 = arr[3].to_le_bytes();
+ let a5 = arr[4].to_le_bytes();
+ let a6 = arr[5].to_le_bytes();
+ let a7 = arr[6].to_le_bytes();
+ let a8 = arr[7].to_le_bytes();
+ [
+ a1[0], a1[1],
+ a2[0], a2[1],
+ a3[0], a3[1],
+ a4[0], a4[1],
+ a5[0], a5[1],
+ a6[0], a6[1],
+ a7[0], a7[1],
+ a8[0], a8[1],
+ ]
+ }
+ V128Const::I32x4(arr) => {
+ let a1 = arr[0].to_le_bytes();
+ let a2 = arr[1].to_le_bytes();
+ let a3 = arr[2].to_le_bytes();
+ let a4 = arr[3].to_le_bytes();
+ [
+ a1[0], a1[1], a1[2], a1[3],
+ a2[0], a2[1], a2[2], a2[3],
+ a3[0], a3[1], a3[2], a3[3],
+ a4[0], a4[1], a4[2], a4[3],
+ ]
+ }
+ V128Const::I64x2(arr) => {
+ let a1 = arr[0].to_le_bytes();
+ let a2 = arr[1].to_le_bytes();
+ [
+ a1[0], a1[1], a1[2], a1[3], a1[4], a1[5], a1[6], a1[7],
+ a2[0], a2[1], a2[2], a2[3], a2[4], a2[5], a2[6], a2[7],
+ ]
+ }
+ V128Const::F32x4(arr) => {
+ let a1 = arr[0].bits.to_le_bytes();
+ let a2 = arr[1].bits.to_le_bytes();
+ let a3 = arr[2].bits.to_le_bytes();
+ let a4 = arr[3].bits.to_le_bytes();
+ [
+ a1[0], a1[1], a1[2], a1[3],
+ a2[0], a2[1], a2[2], a2[3],
+ a3[0], a3[1], a3[2], a3[3],
+ a4[0], a4[1], a4[2], a4[3],
+ ]
+ }
+ V128Const::F64x2(arr) => {
+ let a1 = arr[0].bits.to_le_bytes();
+ let a2 = arr[1].bits.to_le_bytes();
+ [
+ a1[0], a1[1], a1[2], a1[3], a1[4], a1[5], a1[6], a1[7],
+ a2[0], a2[1], a2[2], a2[3], a2[4], a2[5], a2[6], a2[7],
+ ]
+ }
+ }
+ }
+}
+
+impl<'a> Parse<'a> for V128Const {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::i8x16>() {
+ parser.parse::<kw::i8x16>()?;
+ Ok(V128Const::I8x16([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::i16x8>() {
+ parser.parse::<kw::i16x8>()?;
+ Ok(V128Const::I16x8([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::i32x4>() {
+ parser.parse::<kw::i32x4>()?;
+ Ok(V128Const::I32x4([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::i64x2>() {
+ parser.parse::<kw::i64x2>()?;
+ Ok(V128Const::I64x2([parser.parse()?, parser.parse()?]))
+ } else if l.peek::<kw::f32x4>() {
+ parser.parse::<kw::f32x4>()?;
+ Ok(V128Const::F32x4([
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ]))
+ } else if l.peek::<kw::f64x2>() {
+ parser.parse::<kw::f64x2>()?;
+ Ok(V128Const::F64x2([parser.parse()?, parser.parse()?]))
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+/// Lanes being shuffled in the `i8x16.shuffle` instruction
+#[derive(Debug)]
+pub struct I8x16Shuffle {
+ #[allow(missing_docs)]
+ pub lanes: [u8; 16],
+}
+
+impl<'a> Parse<'a> for I8x16Shuffle {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(I8x16Shuffle {
+ lanes: [
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ parser.parse()?,
+ ],
+ })
+ }
+}
+
+/// Payload of the `select` instructions
+#[derive(Debug)]
+pub struct SelectTypes<'a> {
+ #[allow(missing_docs)]
+ pub tys: Option<Vec<ast::ValType<'a>>>,
+}
+
+impl<'a> Parse<'a> for SelectTypes<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut tys = None;
+ while parser.peek2::<kw::result>() {
+ let mut list = Vec::new();
+ parser.parens(|p| {
+ p.parse::<kw::result>()?;
+ while !p.is_empty() {
+ list.push(p.parse()?);
+ }
+ Ok(())
+ })?;
+ tys = Some(list);
+ }
+ Ok(SelectTypes { tys })
+ }
+}
+
+/// Payload of the `br_on_exn` instruction
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct BrOnExn<'a> {
+ pub label: ast::Index<'a>,
+ pub exn: ast::Index<'a>,
+}
+
+impl<'a> Parse<'a> for BrOnExn<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let label = parser.parse()?;
+ let exn = parser.parse()?;
+ Ok(BrOnExn { label, exn })
+ }
+}
+
+/// Payload of the `br_on_cast` instruction
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct BrOnCast<'a> {
+ pub label: ast::Index<'a>,
+ pub val: HeapType<'a>,
+ pub rtt: HeapType<'a>,
+}
+
+impl<'a> Parse<'a> for BrOnCast<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let label = parser.parse()?;
+ let val = parser.parse()?;
+ let rtt = parser.parse()?;
+ Ok(BrOnCast { label, val, rtt })
+ }
+}
+
+/// Payload of the `rtt.sub` instruction
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct RTTSub<'a> {
+ pub depth: u32,
+ pub input_rtt: HeapType<'a>,
+ pub output_rtt: HeapType<'a>,
+}
+
+impl<'a> Parse<'a> for RTTSub<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let depth = parser.parse()?;
+ let input_rtt = parser.parse()?;
+ let output_rtt = parser.parse()?;
+ Ok(RTTSub {
+ depth,
+ input_rtt,
+ output_rtt,
+ })
+ }
+}
+
+/// Payload of the `ref.test/cast` instruction
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct RefTest<'a> {
+ pub val: HeapType<'a>,
+ pub rtt: HeapType<'a>,
+}
+
+impl<'a> Parse<'a> for RefTest<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let val = parser.parse()?;
+ let rtt = parser.parse()?;
+ Ok(RefTest { val, rtt })
+ }
+}
diff --git a/third_party/rust/wast/src/ast/func.rs b/third_party/rust/wast/src/ast/func.rs
new file mode 100644
index 0000000000..6bc6105552
--- /dev/null
+++ b/third_party/rust/wast/src/ast/func.rs
@@ -0,0 +1,115 @@
+use crate::ast::{self, kw};
+use crate::parser::{Parse, Parser, Result};
+
+/// A WebAssembly function to be inserted into a module.
+///
+/// This is a member of both the function and code sections.
+#[derive(Debug)]
+pub struct Func<'a> {
+ /// Where this `func` was defined.
+ pub span: ast::Span,
+ /// An identifier that this function is resolved with (optionally) for name
+ /// resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// An optional name for this function stored in the custom `name` section.
+ pub name: Option<ast::NameAnnotation<'a>>,
+ /// If present, inline export annotations which indicate names this
+ /// definition should be exported under.
+ pub exports: ast::InlineExport<'a>,
+ /// What kind of function this is, be it an inline-defined or imported
+ /// function.
+ pub kind: FuncKind<'a>,
+ /// The type that this function will have.
+ pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>,
+}
+
+/// Possible ways to define a function in the text format.
+#[derive(Debug)]
+pub enum FuncKind<'a> {
+ /// A function which is actually defined as an import, such as:
+ ///
+ /// ```text
+ /// (func (type 3) (import "foo" "bar"))
+ /// ```
+ Import(ast::InlineImport<'a>),
+
+ /// Almost all functions, those defined inline in a wasm module.
+ Inline {
+ /// The list of locals, if any, for this function.
+ locals: Vec<Local<'a>>,
+
+ /// The instructions of the function.
+ expression: ast::Expression<'a>,
+ },
+}
+
+impl<'a> Parse<'a> for Func<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::func>()?.0;
+ let id = parser.parse()?;
+ let name = parser.parse()?;
+ let exports = parser.parse()?;
+
+ let (ty, kind) = if let Some(import) = parser.parse()? {
+ (parser.parse()?, FuncKind::Import(import))
+ } else {
+ let ty = parser.parse()?;
+ let locals = Local::parse_remainder(parser)?;
+ (
+ ty,
+ FuncKind::Inline {
+ locals,
+ expression: parser.parse()?,
+ },
+ )
+ };
+
+ Ok(Func {
+ span,
+ id,
+ name,
+ exports,
+ ty,
+ kind,
+ })
+ }
+}
+
+/// A local for a `func` or `let` instruction.
+///
+/// Each local has an optional identifier for name resolution, an optional name
+/// for the custom `name` section, and a value type.
+#[derive(Debug)]
+pub struct Local<'a> {
+ /// An identifier that this local is resolved with (optionally) for name
+ /// resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// An optional name for this local stored in the custom `name` section.
+ pub name: Option<ast::NameAnnotation<'a>>,
+ /// The value type of this local.
+ pub ty: ast::ValType<'a>,
+}
+
+impl<'a> Local<'a> {
+ pub(crate) fn parse_remainder(parser: Parser<'a>) -> Result<Vec<Local<'a>>> {
+ let mut locals = Vec::new();
+ while parser.peek2::<kw::local>() {
+ parser.parens(|p| {
+ p.parse::<kw::local>()?;
+ if p.is_empty() {
+ return Ok(());
+ }
+ let id: Option<_> = p.parse()?;
+ let name: Option<_> = p.parse()?;
+ let ty = p.parse()?;
+ let parse_more = id.is_none() && name.is_none();
+ locals.push(Local { id, name, ty });
+ while parse_more && !p.is_empty() {
+ locals.push(Local { id: None, name: None, ty: p.parse()? });
+ }
+ Ok(())
+ })?;
+ }
+ Ok(locals)
+ }
+}
diff --git a/third_party/rust/wast/src/ast/global.rs b/third_party/rust/wast/src/ast/global.rs
new file mode 100644
index 0000000000..78ca637488
--- /dev/null
+++ b/third_party/rust/wast/src/ast/global.rs
@@ -0,0 +1,53 @@
+use crate::ast::{self, kw};
+use crate::parser::{Parse, Parser, Result};
+
+/// A WebAssembly global in a module
+#[derive(Debug)]
+pub struct Global<'a> {
+ /// Where this `global` was defined.
+ pub span: ast::Span,
+ /// An optional name to reference this global by
+ pub id: Option<ast::Id<'a>>,
+ /// If present, inline export annotations which indicate names this
+ /// definition should be exported under.
+ pub exports: ast::InlineExport<'a>,
+ /// The type of this global, both its value type and whether it's mutable.
+ pub ty: ast::GlobalType<'a>,
+ /// What kind of global this defined as.
+ pub kind: GlobalKind<'a>,
+}
+
+/// Different kinds of globals that can be defined in a module.
+#[derive(Debug)]
+pub enum GlobalKind<'a> {
+ /// A global which is actually defined as an import, such as:
+ ///
+ /// ```text
+ /// (global i32 (import "foo" "bar"))
+ /// ```
+ Import(ast::InlineImport<'a>),
+
+ /// A global defined inline in the module itself
+ Inline(ast::Expression<'a>),
+}
+
+impl<'a> Parse<'a> for Global<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::global>()?.0;
+ let id = parser.parse()?;
+ let exports = parser.parse()?;
+
+ let (ty, kind) = if let Some(import) = parser.parse()? {
+ (parser.parse()?, GlobalKind::Import(import))
+ } else {
+ (parser.parse()?, GlobalKind::Inline(parser.parse()?))
+ };
+ Ok(Global {
+ span,
+ id,
+ exports,
+ ty,
+ kind,
+ })
+ }
+}
diff --git a/third_party/rust/wast/src/ast/import.rs b/third_party/rust/wast/src/ast/import.rs
new file mode 100644
index 0000000000..62e5fb91ee
--- /dev/null
+++ b/third_party/rust/wast/src/ast/import.rs
@@ -0,0 +1,176 @@
+use crate::ast::{self, kw};
+use crate::parser::{Cursor, Parse, Parser, Peek, Result};
+
+/// An `import` statement and entry in a WebAssembly module.
+#[derive(Debug, Clone)]
+pub struct Import<'a> {
+ /// Where this `import` was defined
+ pub span: ast::Span,
+ /// The module that this statement is importing from
+ pub module: &'a str,
+ /// The name of the field in the module this statement imports from.
+ pub field: Option<&'a str>,
+ /// The item that's being imported.
+ pub item: ItemSig<'a>,
+}
+
+impl<'a> Parse<'a> for Import<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::import>()?.0;
+ let module = parser.parse()?;
+ let field = parser.parse()?;
+ let item = parser.parens(|p| p.parse())?;
+ Ok(Import {
+ span,
+ module,
+ field,
+ item,
+ })
+ }
+}
+
+#[derive(Debug, Clone)]
+#[allow(missing_docs)]
+pub struct ItemSig<'a> {
+ /// Where this item is defined in the source.
+ pub span: ast::Span,
+ /// An optional identifier used during name resolution to refer to this item
+ /// from the rest of the module.
+ pub id: Option<ast::Id<'a>>,
+ /// An optional name which, for functions, will be stored in the
+ /// custom `name` section.
+ pub name: Option<ast::NameAnnotation<'a>>,
+ /// What kind of item this is.
+ pub kind: ItemKind<'a>,
+}
+
+#[derive(Debug, Clone)]
+#[allow(missing_docs)]
+pub enum ItemKind<'a> {
+ Func(ast::TypeUse<'a, ast::FunctionType<'a>>),
+ Table(ast::TableType<'a>),
+ Memory(ast::MemoryType),
+ Global(ast::GlobalType<'a>),
+ Event(ast::EventType<'a>),
+ Module(ast::TypeUse<'a, ast::ModuleType<'a>>),
+ Instance(ast::TypeUse<'a, ast::InstanceType<'a>>),
+}
+
+impl<'a> Parse<'a> for ItemSig<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::func>() {
+ let span = parser.parse::<kw::func>()?.0;
+ Ok(ItemSig {
+ span,
+ id: parser.parse()?,
+ name: parser.parse()?,
+ kind: ItemKind::Func(parser.parse()?),
+ })
+ } else if l.peek::<kw::table>() {
+ let span = parser.parse::<kw::table>()?.0;
+ Ok(ItemSig {
+ span,
+ id: parser.parse()?,
+ name: None,
+ kind: ItemKind::Table(parser.parse()?),
+ })
+ } else if l.peek::<kw::memory>() {
+ let span = parser.parse::<kw::memory>()?.0;
+ Ok(ItemSig {
+ span,
+ id: parser.parse()?,
+ name: None,
+ kind: ItemKind::Memory(parser.parse()?),
+ })
+ } else if l.peek::<kw::global>() {
+ let span = parser.parse::<kw::global>()?.0;
+ Ok(ItemSig {
+ span,
+ id: parser.parse()?,
+ name: None,
+ kind: ItemKind::Global(parser.parse()?),
+ })
+ } else if l.peek::<kw::event>() {
+ let span = parser.parse::<kw::event>()?.0;
+ Ok(ItemSig {
+ span,
+ id: parser.parse()?,
+ name: None,
+ kind: ItemKind::Event(parser.parse()?),
+ })
+ } else if l.peek::<kw::module>() {
+ let span = parser.parse::<kw::module>()?.0;
+ Ok(ItemSig {
+ span,
+ id: parser.parse()?,
+ name: None,
+ kind: ItemKind::Module(parser.parse()?),
+ })
+ } else if l.peek::<kw::instance>() {
+ let span = parser.parse::<kw::instance>()?.0;
+ Ok(ItemSig {
+ span,
+ id: parser.parse()?,
+ name: None,
+ kind: ItemKind::Instance(parser.parse()?),
+ })
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+/// A listing of a inline `(import "foo")` statement.
+///
+/// Note that when parsing this type it is somewhat unconventional that it
+/// parses its own surrounding parentheses. This is typically an optional type,
+/// so it's so far been a bit nicer to have the optionality handled through
+/// `Peek` rather than `Option<T>`.
+#[derive(Debug, Copy, Clone)]
+#[allow(missing_docs)]
+pub struct InlineImport<'a> {
+ pub module: &'a str,
+ pub field: Option<&'a str>,
+}
+
+impl<'a> Parse<'a> for InlineImport<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ parser.parens(|p| {
+ p.parse::<kw::import>()?;
+ Ok(InlineImport {
+ module: p.parse()?,
+ field: p.parse()?,
+ })
+ })
+ }
+}
+
+impl Peek for InlineImport<'_> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ let cursor = match cursor.lparen() {
+ Some(cursor) => cursor,
+ None => return false,
+ };
+ let cursor = match cursor.keyword() {
+ Some(("import", cursor)) => cursor,
+ _ => return false,
+ };
+ let cursor = match cursor.string() {
+ Some((_, cursor)) => cursor,
+ None => return false,
+ };
+
+ // optional field
+ let cursor = match cursor.string() {
+ Some((_, cursor)) => cursor,
+ None => cursor,
+ };
+
+ cursor.rparen().is_some()
+ }
+
+ fn display() -> &'static str {
+ "inline import"
+ }
+}
diff --git a/third_party/rust/wast/src/ast/instance.rs b/third_party/rust/wast/src/ast/instance.rs
new file mode 100644
index 0000000000..6b41f477a9
--- /dev/null
+++ b/third_party/rust/wast/src/ast/instance.rs
@@ -0,0 +1,86 @@
+use crate::ast::{self, kw};
+use crate::parser::{Parse, Parser, Result};
+
+/// A nested WebAssembly instance to be created as part of a module.
+#[derive(Debug)]
+pub struct Instance<'a> {
+ /// Where this `instance` was defined.
+ pub span: ast::Span,
+ /// An identifier that this instance is resolved with (optionally) for name
+ /// resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// If present, inline export annotations which indicate names this
+ /// definition should be exported under.
+ pub exports: ast::InlineExport<'a>,
+ /// What kind of instance this is, be it an inline-defined or imported one.
+ pub kind: InstanceKind<'a>,
+}
+
+/// Possible ways to define a instance in the text format.
+#[derive(Debug)]
+pub enum InstanceKind<'a> {
+ /// An instance which is actually defined as an import, such as:
+ Import {
+ /// Where we're importing from
+ import: ast::InlineImport<'a>,
+ /// The type that this instance will have.
+ ty: ast::TypeUse<'a, ast::InstanceType<'a>>,
+ },
+
+ /// Instances whose instantiation is defined inline.
+ Inline {
+ /// Module that we're instantiating
+ module: ast::ItemRef<'a, kw::module>,
+ /// Arguments used to instantiate the instance
+ args: Vec<InstanceArg<'a>>,
+ },
+}
+
+/// Arguments to the `instantiate` instruction
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub struct InstanceArg<'a> {
+ pub name: &'a str,
+ pub index: ast::ItemRef<'a, ast::ExportKind>,
+}
+
+impl<'a> Parse<'a> for Instance<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::instance>()?.0;
+ let id = parser.parse()?;
+ let exports = parser.parse()?;
+
+ let kind = if let Some(import) = parser.parse()? {
+ InstanceKind::Import {
+ import,
+ ty: parser.parse()?,
+ }
+ } else {
+ parser.parens(|p| {
+ p.parse::<kw::instantiate>()?;
+ let module = p.parse::<ast::IndexOrRef<_>>()?.0;
+ let mut args = Vec::new();
+ while !p.is_empty() {
+ args.push(p.parse()?);
+ }
+ Ok(InstanceKind::Inline { module, args })
+ })?
+ };
+
+ Ok(Instance {
+ span,
+ id,
+ exports,
+ kind,
+ })
+ }
+}
+
+impl<'a> Parse<'a> for InstanceArg<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(InstanceArg {
+ name: parser.parse()?,
+ index: parser.parse()?,
+ })
+ }
+}
diff --git a/third_party/rust/wast/src/ast/memory.rs b/third_party/rust/wast/src/ast/memory.rs
new file mode 100644
index 0000000000..ed3d907af4
--- /dev/null
+++ b/third_party/rust/wast/src/ast/memory.rs
@@ -0,0 +1,250 @@
+use crate::ast::{self, kw};
+use crate::parser::{Lookahead1, Parse, Parser, Peek, Result};
+
+/// A defined WebAssembly memory instance inside of a module.
+#[derive(Debug)]
+pub struct Memory<'a> {
+ /// Where this `memory` was defined
+ pub span: ast::Span,
+ /// An optional name to refer to this memory by.
+ pub id: Option<ast::Id<'a>>,
+ /// If present, inline export annotations which indicate names this
+ /// definition should be exported under.
+ pub exports: ast::InlineExport<'a>,
+ /// How this memory is defined in the module.
+ pub kind: MemoryKind<'a>,
+}
+
+/// Different syntactical ways a memory can be defined in a module.
+#[derive(Debug)]
+pub enum MemoryKind<'a> {
+ /// This memory is actually an inlined import definition.
+ #[allow(missing_docs)]
+ Import {
+ import: ast::InlineImport<'a>,
+ ty: ast::MemoryType,
+ },
+
+ /// A typical memory definition which simply says the limits of the memory
+ Normal(ast::MemoryType),
+
+ /// The data of this memory, starting from 0, explicitly listed
+ Inline {
+ /// Whether or not this will be creating a 32-bit memory
+ is_32: bool,
+ /// The inline data specified for this memory
+ data: Vec<DataVal<'a>>,
+ },
+}
+
+impl<'a> Parse<'a> for Memory<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::memory>()?.0;
+ let id = parser.parse()?;
+ let exports = parser.parse()?;
+
+ // Afterwards figure out which style this is, either:
+ //
+ // * `(import "a" "b") limits`
+ // * `(data ...)`
+ // * `limits`
+ let mut l = parser.lookahead1();
+ let kind = if let Some(import) = parser.parse()? {
+ MemoryKind::Import {
+ import,
+ ty: parser.parse()?,
+ }
+ } else if l.peek::<ast::LParen>() || parser.peek2::<ast::LParen>() {
+ let is_32 = if parser.parse::<Option<kw::i32>>()?.is_some() {
+ true
+ } else if parser.parse::<Option<kw::i64>>()?.is_some() {
+ false
+ } else {
+ true
+ };
+ let data = parser.parens(|parser| {
+ parser.parse::<kw::data>()?;
+ let mut data = Vec::new();
+ while !parser.is_empty() {
+ data.push(parser.parse()?);
+ }
+ Ok(data)
+ })?;
+ MemoryKind::Inline { data, is_32 }
+ } else if l.peek::<u32>() || l.peek::<kw::i32>() || l.peek::<kw::i64>() {
+ MemoryKind::Normal(parser.parse()?)
+ } else {
+ return Err(l.error());
+ };
+ Ok(Memory {
+ span,
+ id,
+ exports,
+ kind,
+ })
+ }
+}
+
+/// A `data` directive in a WebAssembly module.
+#[derive(Debug)]
+pub struct Data<'a> {
+ /// Where this `data` was defined
+ pub span: ast::Span,
+
+ /// The optional name of this data segment
+ pub id: Option<ast::Id<'a>>,
+
+ /// Whether this data segment is passive or active
+ pub kind: DataKind<'a>,
+
+ /// Bytes for this `Data` segment, viewed as the concatenation of all the
+ /// contained slices.
+ pub data: Vec<DataVal<'a>>,
+}
+
+/// Different kinds of data segments, either passive or active.
+#[derive(Debug)]
+pub enum DataKind<'a> {
+ /// A passive data segment which isn't associated with a memory and is
+ /// referenced from various instructions.
+ Passive,
+
+ /// An active data segment which is associated and loaded into a particular
+ /// memory on module instantiation.
+ Active {
+ /// The memory that this `Data` will be associated with.
+ memory: ast::ItemRef<'a, kw::memory>,
+
+ /// Initial offset to load this data segment at
+ offset: ast::Expression<'a>,
+ },
+}
+
+impl<'a> Parse<'a> for Data<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::data>()?.0;
+ let id = parser.parse()?;
+
+ // The `passive` keyword is mentioned in the current spec but isn't
+ // mentioned in `wabt` tests, so consider it optional for now
+ let kind = if parser.peek::<kw::passive>() {
+ parser.parse::<kw::passive>()?;
+ DataKind::Passive
+
+ // If data directly follows then assume this is a passive segment
+ } else if parser.peek::<&[u8]>() {
+ DataKind::Passive
+
+ // ... and otherwise we must be attached to a particular memory as well
+ // as having an initialization offset.
+ } else {
+ let memory = if let Some(index) = parser.parse::<Option<ast::IndexOrRef<_>>>()? {
+ index.0
+ } else {
+ ast::ItemRef::Item {
+ kind: kw::memory(parser.prev_span()),
+ idx: ast::Index::Num(0, span),
+ exports: Vec::new(),
+ }
+ };
+ let offset = parser.parens(|parser| {
+ if parser.peek::<kw::offset>() {
+ parser.parse::<kw::offset>()?;
+ }
+ parser.parse()
+ })?;
+ DataKind::Active { memory, offset }
+ };
+
+ let mut data = Vec::new();
+ while !parser.is_empty() {
+ data.push(parser.parse()?);
+ }
+ Ok(Data {
+ span,
+ id,
+ kind,
+ data,
+ })
+ }
+}
+
+/// Differnet ways the value of a data segment can be defined.
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub enum DataVal<'a> {
+ String(&'a [u8]),
+ Integral(Vec<u8>),
+}
+
+impl DataVal<'_> {
+ /// Returns the length, in bytes, of the memory used to represent this data
+ /// value.
+ pub fn len(&self) -> usize {
+ match self {
+ DataVal::String(s) => s.len(),
+ DataVal::Integral(s) => s.len(),
+ }
+ }
+
+ /// Pushes the value of this data value onto the provided list of bytes.
+ pub fn push_onto(&self, dst: &mut Vec<u8>) {
+ match self {
+ DataVal::String(s) => dst.extend_from_slice(s),
+ DataVal::Integral(s) => dst.extend_from_slice(s),
+ }
+ }
+}
+
+impl<'a> Parse<'a> for DataVal<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if !parser.peek::<ast::LParen>() {
+ return Ok(DataVal::String(parser.parse()?));
+ }
+
+ return parser.parens(|p| {
+ let mut result = Vec::new();
+ let mut lookahead = p.lookahead1();
+ let l = &mut lookahead;
+ let r = &mut result;
+ if consume::<kw::i8, i8, _>(p, l, r, |u, v| v.push(u as u8))?
+ || consume::<kw::i16, i16, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
+ || consume::<kw::i32, i32, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
+ || consume::<kw::i64, i64, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
+ || consume::<kw::f32, ast::Float32, _>(p, l, r, |u, v| {
+ v.extend(&u.bits.to_le_bytes())
+ })?
+ || consume::<kw::f64, ast::Float64, _>(p, l, r, |u, v| {
+ v.extend(&u.bits.to_le_bytes())
+ })?
+ || consume::<kw::v128, ast::V128Const, _>(p, l, r, |u, v| {
+ v.extend(&u.to_le_bytes())
+ })?
+ {
+ Ok(DataVal::Integral(result))
+ } else {
+ Err(lookahead.error())
+ }
+ });
+
+ fn consume<'a, T: Peek + Parse<'a>, U: Parse<'a>, F>(
+ parser: Parser<'a>,
+ lookahead: &mut Lookahead1<'a>,
+ dst: &mut Vec<u8>,
+ push: F,
+ ) -> Result<bool>
+ where
+ F: Fn(U, &mut Vec<u8>),
+ {
+ if !lookahead.peek::<T>() {
+ return Ok(false);
+ }
+ parser.parse::<T>()?;
+ while !parser.is_empty() {
+ let val = parser.parse::<U>()?;
+ push(val, dst);
+ }
+ Ok(true)
+ }
+ }
+}
diff --git a/third_party/rust/wast/src/ast/mod.rs b/third_party/rust/wast/src/ast/mod.rs
new file mode 100644
index 0000000000..7b708cd403
--- /dev/null
+++ b/third_party/rust/wast/src/ast/mod.rs
@@ -0,0 +1,430 @@
+/// A macro to create a custom keyword parser.
+///
+/// This macro is invoked in one of two forms:
+///
+/// ```
+/// // keyword derived from the Rust identifier:
+/// wast::custom_keyword!(foo);
+///
+/// // or an explicitly specified string representation of the keyword:
+/// wast::custom_keyword!(my_keyword = "the-wasm-keyword");
+/// ```
+///
+/// This can then be used to parse custom keyword for custom items, such as:
+///
+/// ```
+/// use wast::parser::{Parser, Result, Parse};
+///
+/// struct InlineModule<'a> {
+/// inline_text: &'a str,
+/// }
+///
+/// mod kw {
+/// wast::custom_keyword!(inline);
+/// }
+///
+/// // Parse an inline string module of the form:
+/// //
+/// // (inline "(module (func))")
+/// impl<'a> Parse<'a> for InlineModule<'a> {
+/// fn parse(parser: Parser<'a>) -> Result<Self> {
+/// parser.parse::<kw::inline>()?;
+/// Ok(InlineModule {
+/// inline_text: parser.parse()?,
+/// })
+/// }
+/// }
+/// ```
+///
+/// Note that the keyword name can only start with a lower-case letter, i.e. 'a'..'z'.
+#[macro_export]
+macro_rules! custom_keyword {
+ ($name:ident) => {
+ $crate::custom_keyword!($name = stringify!($name));
+ };
+ ($name:ident = $kw:expr) => {
+ #[allow(non_camel_case_types)]
+ #[allow(missing_docs)]
+ #[derive(Debug, Copy, Clone)]
+ pub struct $name(pub $crate::Span);
+
+ impl<'a> $crate::parser::Parse<'a> for $name {
+ fn parse(parser: $crate::parser::Parser<'a>) -> $crate::parser::Result<Self> {
+ parser.step(|c| {
+ if let Some((kw, rest)) = c.keyword() {
+ if kw == $kw {
+ return Ok(($name(c.cur_span()), rest));
+ }
+ }
+ Err(c.error(concat!("expected keyword `", $kw, "`")))
+ })
+ }
+ }
+
+ impl $crate::parser::Peek for $name {
+ fn peek(cursor: $crate::parser::Cursor<'_>) -> bool {
+ if let Some((kw, _rest)) = cursor.keyword() {
+ kw == $kw
+ } else {
+ false
+ }
+ }
+
+ fn display() -> &'static str {
+ concat!("`", $kw, "`")
+ }
+ }
+ };
+}
+
+/// A macro for defining custom reserved symbols.
+///
+/// This is like `custom_keyword!` but for reserved symbols (`Token::Reserved`)
+/// instead of keywords (`Token::Keyword`).
+///
+/// ```
+/// use wast::parser::{Parser, Result, Parse};
+///
+/// // Define a custom reserved symbol, the "spaceship" operator: `<=>`.
+/// wast::custom_reserved!(spaceship = "<=>");
+///
+/// /// A "three-way comparison" like `(<=> a b)` that returns -1 if `a` is less
+/// /// than `b`, 0 if they're equal, and 1 if `a` is greater than `b`.
+/// struct ThreeWayComparison<'a> {
+/// lhs: wast::Expression<'a>,
+/// rhs: wast::Expression<'a>,
+/// }
+///
+/// impl<'a> Parse<'a> for ThreeWayComparison<'a> {
+/// fn parse(parser: Parser<'a>) -> Result<Self> {
+/// parser.parse::<spaceship>()?;
+/// let lhs = parser.parse()?;
+/// let rhs = parser.parse()?;
+/// Ok(ThreeWayComparison { lhs, rhs })
+/// }
+/// }
+/// ```
+#[macro_export]
+macro_rules! custom_reserved {
+ ($name:ident) => {
+ $crate::custom_reserved!($name = stringify!($name));
+ };
+ ($name:ident = $rsv:expr) => {
+ #[allow(non_camel_case_types)]
+ #[allow(missing_docs)]
+ #[derive(Debug)]
+ pub struct $name(pub $crate::Span);
+
+ impl<'a> $crate::parser::Parse<'a> for $name {
+ fn parse(parser: $crate::parser::Parser<'a>) -> $crate::parser::Result<Self> {
+ parser.step(|c| {
+ if let Some((rsv, rest)) = c.reserved() {
+ if rsv == $rsv {
+ return Ok(($name(c.cur_span()), rest));
+ }
+ }
+ Err(c.error(concat!("expected reserved symbol `", $rsv, "`")))
+ })
+ }
+ }
+
+ impl $crate::parser::Peek for $name {
+ fn peek(cursor: $crate::parser::Cursor<'_>) -> bool {
+ if let Some((rsv, _rest)) = cursor.reserved() {
+ rsv == $rsv
+ } else {
+ false
+ }
+ }
+
+ fn display() -> &'static str {
+ concat!("`", $rsv, "`")
+ }
+ }
+ };
+}
+
+/// A macro, like [`custom_keyword`], to create a type which can be used to
+/// parse/peek annotation directives.
+///
+/// Note that when you're parsing custom annotations it can be somewhat tricky
+/// due to the nature that most of them are skipped. You'll want to be sure to
+/// consult the documentation of [`Parser::register_annotation`][register] when
+/// using this macro.
+///
+/// # Examples
+///
+/// To see an example of how to use this macro, let's invent our own syntax for
+/// the [producers section][section] which looks like:
+///
+/// ```wat
+/// (@producer "wat" "1.0.2")
+/// ```
+///
+/// Here, for simplicity, we'll assume everything is a `processed-by` directive,
+/// but you could get much more fancy with this as well.
+///
+/// ```
+/// # use wast::*;
+/// # use wast::parser::*;
+///
+/// // First we define the custom annotation keyword we're using, and by
+/// // convention we define it in an `annotation` module.
+/// mod annotation {
+/// wast::annotation!(producer);
+/// }
+///
+/// struct Producer<'a> {
+/// name: &'a str,
+/// version: &'a str,
+/// }
+///
+/// impl<'a> Parse<'a> for Producer<'a> {
+/// fn parse(parser: Parser<'a>) -> Result<Self> {
+/// // Remember that parser conventionally parse the *interior* of an
+/// // s-expression, so we parse our `@producer` annotation and then we
+/// // parse the payload of our annotation.
+/// parser.parse::<annotation::producer>()?;
+/// Ok(Producer {
+/// name: parser.parse()?,
+/// version: parser.parse()?,
+/// })
+/// }
+/// }
+/// ```
+///
+/// Note though that this is only half of the parser for annotations. The other
+/// half is calling the [`register_annotation`][register] method at the right
+/// time to ensure the parser doesn't automatically skip our `@producer`
+/// directive. Note that we *can't* call it inside the `Parse for Producer`
+/// definition because that's too late and the annotation would already have
+/// been skipped.
+///
+/// Instead we'll need to call it from a higher level-parser before the
+/// parenthesis have been parsed, like so:
+///
+/// ```
+/// # use wast::*;
+/// # use wast::parser::*;
+/// struct Module<'a> {
+/// fields: Vec<ModuleField<'a>>,
+/// }
+///
+/// impl<'a> Parse<'a> for Module<'a> {
+/// fn parse(parser: Parser<'a>) -> Result<Self> {
+/// // .. parse module header here ...
+///
+/// // register our custom `@producer` annotation before we start
+/// // parsing the parentheses of each field
+/// let _r = parser.register_annotation("producer");
+///
+/// let mut fields = Vec::new();
+/// while !parser.is_empty() {
+/// fields.push(parser.parens(|p| p.parse())?);
+/// }
+/// Ok(Module { fields })
+/// }
+/// }
+///
+/// enum ModuleField<'a> {
+/// Producer(Producer<'a>),
+/// // ...
+/// }
+/// # struct Producer<'a>(&'a str);
+/// # impl<'a> Parse<'a> for Producer<'a> {
+/// # fn parse(parser: Parser<'a>) -> Result<Self> { Ok(Producer(parser.parse()?)) }
+/// # }
+/// # mod annotation { wast::annotation!(producer); }
+///
+/// impl<'a> Parse<'a> for ModuleField<'a> {
+/// fn parse(parser: Parser<'a>) -> Result<Self> {
+/// // and here `peek` works and our delegated parsing works because the
+/// // annotation has been registered.
+/// if parser.peek::<annotation::producer>() {
+/// return Ok(ModuleField::Producer(parser.parse()?));
+/// }
+///
+/// // .. typically we'd parse other module fields here...
+///
+/// Err(parser.error("unknown module field"))
+/// }
+/// }
+/// ```
+///
+/// [register]: crate::parser::Parser::register_annotation
+/// [section]: https://github.com/WebAssembly/tool-conventions/blob/master/ProducersSection.md
+#[macro_export]
+macro_rules! annotation {
+ ($name:ident) => {
+ $crate::annotation!($name = stringify!($name));
+ };
+ ($name:ident = $annotation:expr) => {
+ #[allow(non_camel_case_types)]
+ #[allow(missing_docs)]
+ #[derive(Debug)]
+ pub struct $name(pub $crate::Span);
+
+ impl<'a> $crate::parser::Parse<'a> for $name {
+ fn parse(parser: $crate::parser::Parser<'a>) -> $crate::parser::Result<Self> {
+ parser.step(|c| {
+ if let Some((a, rest)) = c.annotation() {
+ if a == $annotation {
+ return Ok(($name(c.cur_span()), rest));
+ }
+ }
+ Err(c.error(concat!("expected annotation `@", $annotation, "`")))
+ })
+ }
+ }
+
+ impl $crate::parser::Peek for $name {
+ fn peek(cursor: $crate::parser::Cursor<'_>) -> bool {
+ if let Some((a, _rest)) = cursor.annotation() {
+ a == $annotation
+ } else {
+ false
+ }
+ }
+
+ fn display() -> &'static str {
+ concat!("`@", $annotation, "`")
+ }
+ }
+ };
+}
+
+macro_rules! reexport {
+ ($(mod $name:ident;)*) => ($(mod $name; pub use self::$name::*;)*);
+}
+
+reexport! {
+ mod token;
+}
+
+#[cfg(feature = "wasm-module")]
+reexport! {
+ mod alias;
+ mod assert_expr;
+ mod custom;
+ mod event;
+ mod export;
+ mod expr;
+ mod func;
+ mod global;
+ mod import;
+ mod instance;
+ mod memory;
+ mod module;
+ mod nested_module;
+ mod table;
+ mod types;
+ mod wast;
+}
+
+/// Common keyword used to parse WebAssembly text files.
+pub mod kw {
+ custom_keyword!(after);
+ custom_keyword!(alias);
+ custom_keyword!(any);
+ custom_keyword!(anyfunc);
+ custom_keyword!(anyref);
+ custom_keyword!(arg);
+ custom_keyword!(array);
+ custom_keyword!(assert_exhaustion);
+ custom_keyword!(assert_invalid);
+ custom_keyword!(assert_malformed);
+ custom_keyword!(assert_return);
+ custom_keyword!(assert_return_arithmetic_nan);
+ custom_keyword!(assert_return_arithmetic_nan_f32x4);
+ custom_keyword!(assert_return_arithmetic_nan_f64x2);
+ custom_keyword!(assert_return_canonical_nan);
+ custom_keyword!(assert_return_canonical_nan_f32x4);
+ custom_keyword!(assert_return_canonical_nan_f64x2);
+ custom_keyword!(assert_return_func);
+ custom_keyword!(assert_trap);
+ custom_keyword!(assert_unlinkable);
+ custom_keyword!(before);
+ custom_keyword!(binary);
+ custom_keyword!(block);
+ custom_keyword!(catch);
+ custom_keyword!(catch_all);
+ custom_keyword!(code);
+ custom_keyword!(data);
+ custom_keyword!(declare);
+ custom_keyword!(r#do = "do");
+ custom_keyword!(elem);
+ custom_keyword!(end);
+ custom_keyword!(event);
+ custom_keyword!(exn);
+ custom_keyword!(exnref);
+ custom_keyword!(export);
+ custom_keyword!(r#extern = "extern");
+ custom_keyword!(externref);
+ custom_keyword!(eq);
+ custom_keyword!(eqref);
+ custom_keyword!(f32);
+ custom_keyword!(f32x4);
+ custom_keyword!(f64);
+ custom_keyword!(f64x2);
+ custom_keyword!(field);
+ custom_keyword!(first);
+ custom_keyword!(func);
+ custom_keyword!(funcref);
+ custom_keyword!(get);
+ custom_keyword!(global);
+ custom_keyword!(i16);
+ custom_keyword!(i16x8);
+ custom_keyword!(i31);
+ custom_keyword!(i31ref);
+ custom_keyword!(i32);
+ custom_keyword!(i32x4);
+ custom_keyword!(i64);
+ custom_keyword!(i64x2);
+ custom_keyword!(i8);
+ custom_keyword!(i8x16);
+ custom_keyword!(import);
+ custom_keyword!(instance);
+ custom_keyword!(instantiate);
+ custom_keyword!(invoke);
+ custom_keyword!(item);
+ custom_keyword!(last);
+ custom_keyword!(local);
+ custom_keyword!(memory);
+ custom_keyword!(module);
+ custom_keyword!(modulecode);
+ custom_keyword!(nan_arithmetic = "nan:arithmetic");
+ custom_keyword!(nan_canonical = "nan:canonical");
+ custom_keyword!(null);
+ custom_keyword!(nullref);
+ custom_keyword!(offset);
+ custom_keyword!(outer);
+ custom_keyword!(param);
+ custom_keyword!(parent);
+ custom_keyword!(passive);
+ custom_keyword!(quote);
+ custom_keyword!(r#else = "else");
+ custom_keyword!(r#if = "if");
+ custom_keyword!(r#loop = "loop");
+ custom_keyword!(r#mut = "mut");
+ custom_keyword!(r#type = "type");
+ custom_keyword!(r#ref = "ref");
+ custom_keyword!(ref_func = "ref.func");
+ custom_keyword!(ref_null = "ref.null");
+ custom_keyword!(register);
+ custom_keyword!(result);
+ custom_keyword!(rtt);
+ custom_keyword!(shared);
+ custom_keyword!(start);
+ custom_keyword!(r#struct = "struct");
+ custom_keyword!(table);
+ custom_keyword!(then);
+ custom_keyword!(r#try = "try");
+ custom_keyword!(unwind);
+ custom_keyword!(v128);
+}
+
+/// Common annotations used to parse WebAssembly text files.
+pub mod annotation {
+ annotation!(custom);
+ annotation!(name);
+}
diff --git a/third_party/rust/wast/src/ast/module.rs b/third_party/rust/wast/src/ast/module.rs
new file mode 100644
index 0000000000..e3bee2d050
--- /dev/null
+++ b/third_party/rust/wast/src/ast/module.rs
@@ -0,0 +1,239 @@
+use crate::ast::{self, annotation, kw};
+use crate::parser::{Parse, Parser, Result};
+
+pub use crate::resolve::Names;
+
+/// A `*.wat` file parser, or a parser for one parenthesized module.
+///
+/// This is the top-level type which you'll frequently parse when working with
+/// this crate. A `*.wat` file is either one `module` s-expression or a sequence
+/// of s-expressions that are module fields.
+pub struct Wat<'a> {
+ #[allow(missing_docs)]
+ pub module: Module<'a>,
+}
+
+impl<'a> Parse<'a> for Wat<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if !parser.has_meaningful_tokens() {
+ return Err(parser.error("expected at least one module field"));
+ }
+ let _r = parser.register_annotation("custom");
+ let module = if !parser.peek2::<kw::module>() {
+ let fields = ModuleField::parse_remaining(parser)?;
+ Module {
+ span: ast::Span { offset: 0 },
+ id: None,
+ name: None,
+ kind: ModuleKind::Text(fields),
+ }
+ } else {
+ parser.parens(|parser| parser.parse())?
+ };
+ module.validate(parser)?;
+ Ok(Wat { module })
+ }
+}
+
+/// A parsed WebAssembly module.
+pub struct Module<'a> {
+ /// Where this `module` was defined
+ pub span: ast::Span,
+ /// An optional identifier this module is known by
+ pub id: Option<ast::Id<'a>>,
+ /// An optional `@name` annotation for this module
+ pub name: Option<ast::NameAnnotation<'a>>,
+ /// What kind of module this was parsed as.
+ pub kind: ModuleKind<'a>,
+}
+
+/// The different kinds of ways to define a module.
+pub enum ModuleKind<'a> {
+ /// A module defined in the textual s-expression format.
+ Text(Vec<ModuleField<'a>>),
+ /// A module that had its raw binary bytes defined via the `binary`
+ /// directive.
+ Binary(Vec<&'a [u8]>),
+}
+
+impl<'a> Module<'a> {
+ /// Performs a name resolution pass on this [`Module`], resolving all
+ /// symbolic names to indices.
+ ///
+ /// The WAT format contains a number of shorthands to make it easier to
+ /// write, such as inline exports, inline imports, inline type definitions,
+ /// etc. Additionally it allows using symbolic names such as `$foo` instead
+ /// of using indices. This module will postprocess an AST to remove all of
+ /// this syntactic sugar, preparing the AST for binary emission. This is
+ /// where expansion and name resolution happens.
+ ///
+ /// This function will mutate the AST of this [`Module`] and replace all
+ /// [`super::Index`] arguments with `Index::Num`. This will also expand inline
+ /// exports/imports listed on fields and handle various other shorthands of
+ /// the text format.
+ ///
+ /// If successful the AST was modified to be ready for binary encoding. A
+ /// [`Names`] structure is also returned so if you'd like to do your own
+ /// name lookups on the result you can do so as well.
+ ///
+ /// # Errors
+ ///
+ /// If an error happens during resolution, such a name resolution error or
+ /// items are found in the wrong order, then an error is returned.
+ pub fn resolve(&mut self) -> std::result::Result<Names<'a>, crate::Error> {
+ crate::resolve::resolve(self)
+ }
+
+ /// Encodes this [`Module`] to its binary form.
+ ///
+ /// This function will take the textual representation in [`Module`] and
+ /// perform all steps necessary to convert it to a binary WebAssembly
+ /// module, suitable for writing to a `*.wasm` file. This function may
+ /// internally modify the [`Module`], for example:
+ ///
+ /// * Name resolution is performed to ensure that `Index::Id` isn't present
+ /// anywhere in the AST.
+ ///
+ /// * Inline shorthands such as imports/exports/types are all expanded to be
+ /// dedicated fields of the module.
+ ///
+ /// * Module fields may be shuffled around to preserve index ordering from
+ /// expansions.
+ ///
+ /// After all of this expansion has happened the module will be converted to
+ /// its binary form and returned as a `Vec<u8>`. This is then suitable to
+ /// hand off to other wasm runtimes and such.
+ ///
+ /// # Errors
+ ///
+ /// This function can return an error for name resolution errors and other
+ /// expansion-related errors.
+ pub fn encode(&mut self) -> std::result::Result<Vec<u8>, crate::Error> {
+ self.resolve()?;
+ Ok(crate::binary::encode(self))
+ }
+
+ fn validate(&self, parser: Parser<'_>) -> Result<()> {
+ let mut starts = 0;
+ if let ModuleKind::Text(fields) = &self.kind {
+ for item in fields.iter() {
+ if let ModuleField::Start(_) = item {
+ starts += 1;
+ }
+ }
+ }
+ if starts > 1 {
+ return Err(parser.error("multiple start sections found"));
+ }
+ Ok(())
+ }
+}
+
+impl<'a> Parse<'a> for Module<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let _r = parser.register_annotation("custom");
+ let span = parser.parse::<kw::module>()?.0;
+ let id = parser.parse()?;
+ let name = parser.parse()?;
+
+ let kind = if parser.peek::<kw::binary>() {
+ parser.parse::<kw::binary>()?;
+ let mut data = Vec::new();
+ while !parser.is_empty() {
+ data.push(parser.parse()?);
+ }
+ ModuleKind::Binary(data)
+ } else {
+ ModuleKind::Text(ModuleField::parse_remaining(parser)?)
+ };
+ Ok(Module {
+ span,
+ id,
+ name,
+ kind,
+ })
+ }
+}
+
+/// A listing of all possible fields that can make up a WebAssembly module.
+#[allow(missing_docs)]
+#[derive(Debug)]
+pub enum ModuleField<'a> {
+ Type(ast::Type<'a>),
+ Import(ast::Import<'a>),
+ Func(ast::Func<'a>),
+ Table(ast::Table<'a>),
+ Memory(ast::Memory<'a>),
+ Global(ast::Global<'a>),
+ Export(ast::Export<'a>),
+ Start(ast::ItemRef<'a, kw::func>),
+ Elem(ast::Elem<'a>),
+ Data(ast::Data<'a>),
+ Event(ast::Event<'a>),
+ Custom(ast::Custom<'a>),
+ Instance(ast::Instance<'a>),
+ NestedModule(ast::NestedModule<'a>),
+ Alias(ast::Alias<'a>),
+}
+
+impl<'a> ModuleField<'a> {
+ fn parse_remaining(parser: Parser<'a>) -> Result<Vec<ModuleField>> {
+ let mut fields = Vec::new();
+ while !parser.is_empty() {
+ fields.push(parser.parens(ModuleField::parse)?);
+ }
+ Ok(fields)
+ }
+}
+
+impl<'a> Parse<'a> for ModuleField<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if parser.peek::<kw::r#type>() {
+ return Ok(ModuleField::Type(parser.parse()?));
+ }
+ if parser.peek::<kw::import>() {
+ return Ok(ModuleField::Import(parser.parse()?));
+ }
+ if parser.peek::<kw::func>() {
+ return Ok(ModuleField::Func(parser.parse()?));
+ }
+ if parser.peek::<kw::table>() {
+ return Ok(ModuleField::Table(parser.parse()?));
+ }
+ if parser.peek::<kw::memory>() {
+ return Ok(ModuleField::Memory(parser.parse()?));
+ }
+ if parser.peek::<kw::global>() {
+ return Ok(ModuleField::Global(parser.parse()?));
+ }
+ if parser.peek::<kw::export>() {
+ return Ok(ModuleField::Export(parser.parse()?));
+ }
+ if parser.peek::<kw::start>() {
+ parser.parse::<kw::start>()?;
+ return Ok(ModuleField::Start(parser.parse::<ast::IndexOrRef<_>>()?.0));
+ }
+ if parser.peek::<kw::elem>() {
+ return Ok(ModuleField::Elem(parser.parse()?));
+ }
+ if parser.peek::<kw::data>() {
+ return Ok(ModuleField::Data(parser.parse()?));
+ }
+ if parser.peek::<kw::event>() {
+ return Ok(ModuleField::Event(parser.parse()?));
+ }
+ if parser.peek::<annotation::custom>() {
+ return Ok(ModuleField::Custom(parser.parse()?));
+ }
+ if parser.peek::<kw::instance>() {
+ return Ok(ModuleField::Instance(parser.parse()?));
+ }
+ if parser.peek::<kw::module>() {
+ return Ok(ModuleField::NestedModule(parser.parse()?));
+ }
+ if parser.peek::<kw::alias>() {
+ return Ok(ModuleField::Alias(parser.parse()?));
+ }
+ Err(parser.error("expected valid module field"))
+ }
+}
diff --git a/third_party/rust/wast/src/ast/nested_module.rs b/third_party/rust/wast/src/ast/nested_module.rs
new file mode 100644
index 0000000000..af3f1a95a4
--- /dev/null
+++ b/third_party/rust/wast/src/ast/nested_module.rs
@@ -0,0 +1,115 @@
+use crate::ast::{self, kw};
+use crate::parser::{Cursor, Parse, Parser, Peek, Result};
+
+/// A nested WebAssembly nested module to be created as part of a module.
+#[derive(Debug)]
+pub struct NestedModule<'a> {
+ /// Where this `nested module` was defined.
+ pub span: ast::Span,
+ /// An identifier that this nested module is resolved with (optionally) for name
+ /// resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// An optional name for this module stored in the custom `name` section.
+ pub name: Option<ast::NameAnnotation<'a>>,
+ /// If present, inline export annotations which indicate names this
+ /// definition should be exported under.
+ pub exports: ast::InlineExport<'a>,
+ /// What kind of nested module this is, be it an inline-defined or imported one.
+ pub kind: NestedModuleKind<'a>,
+}
+
+/// Possible ways to define a nested module in the text format.
+#[derive(Debug)]
+pub enum NestedModuleKind<'a> {
+ /// An nested module which is actually defined as an import, such as:
+ Import {
+ /// Where this nested module is imported from
+ import: ast::InlineImport<'a>,
+ /// The type that this nested module will have.
+ ty: ast::TypeUse<'a, ast::ModuleType<'a>>,
+ },
+
+ /// Nested modules whose instantiation is defined inline.
+ Inline {
+ /// Fields in the nested module.
+ fields: Vec<ast::ModuleField<'a>>,
+ },
+}
+
+impl<'a> Parse<'a> for NestedModule<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ // This is sort of a fundamental limitation of the way this crate is
+ // designed. Everything is done through recursive descent parsing which
+ // means, well, that we're recursively going down the stack as we parse
+ // nested data structures. While we can handle this for wasm expressions
+ // since that's a pretty local decision, handling this for nested
+ // modules which be far trickier. For now we just say that when the
+ // parser goes too deep we return an error saying there's too many
+ // nested modules. It would be great to not return an error here,
+ // though!
+ if parser.parens_depth() > 100 {
+ return Err(parser.error("module nesting too deep"));
+ }
+
+ let span = parser.parse::<kw::module>()?.0;
+ let id = parser.parse()?;
+ let name = parser.parse()?;
+ let exports = parser.parse()?;
+
+ let kind = if let Some(import) = parser.parse()? {
+ NestedModuleKind::Import {
+ import,
+ ty: parser.parse()?,
+ }
+ } else {
+ let mut fields = Vec::new();
+ while !parser.is_empty() {
+ fields.push(parser.parens(|p| p.parse())?);
+ }
+ NestedModuleKind::Inline { fields }
+ };
+
+ Ok(NestedModule {
+ span,
+ id,
+ name,
+ exports,
+ kind,
+ })
+ }
+}
+
+// Note that this is a custom implementation to get multi-token lookahead to
+// figure out how to parse `(type ...` when it's in an inline module. If this is
+// `(type $x)` or `(type 0)` then it's an inline type annotation, otherwise it's
+// probably a typedef like `(type $x (func))` or something like that. We only
+// want to parse the two-token variant right now.
+struct InlineType;
+
+impl Peek for InlineType {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ let cursor = match cursor.lparen() {
+ Some(cursor) => cursor,
+ None => return false,
+ };
+ let cursor = match cursor.keyword() {
+ Some(("type", cursor)) => cursor,
+ _ => return false,
+ };
+
+ // optional identifier
+ let cursor = match cursor.id() {
+ Some((_, cursor)) => cursor,
+ None => match cursor.integer() {
+ Some((_, cursor)) => cursor,
+ None => return false,
+ },
+ };
+
+ cursor.rparen().is_some()
+ }
+
+ fn display() -> &'static str {
+ "inline type"
+ }
+}
diff --git a/third_party/rust/wast/src/ast/table.rs b/third_party/rust/wast/src/ast/table.rs
new file mode 100644
index 0000000000..d5e71531eb
--- /dev/null
+++ b/third_party/rust/wast/src/ast/table.rs
@@ -0,0 +1,234 @@
+use crate::ast::{self, kw};
+use crate::parser::{Parse, Parser, Result};
+
+/// A WebAssembly `table` directive in a module.
+#[derive(Debug)]
+pub struct Table<'a> {
+ /// Where this table was defined.
+ pub span: ast::Span,
+ /// An optional name to refer to this table by.
+ pub id: Option<ast::Id<'a>>,
+ /// If present, inline export annotations which indicate names this
+ /// definition should be exported under.
+ pub exports: ast::InlineExport<'a>,
+ /// How this table is textually defined in the module.
+ pub kind: TableKind<'a>,
+}
+
+/// Different ways to textually define a table.
+#[derive(Debug)]
+pub enum TableKind<'a> {
+ /// This table is actually an inlined import definition.
+ #[allow(missing_docs)]
+ Import {
+ import: ast::InlineImport<'a>,
+ ty: ast::TableType<'a>,
+ },
+
+ /// A typical memory definition which simply says the limits of the table
+ Normal(ast::TableType<'a>),
+
+ /// The elem segments of this table, starting from 0, explicitly listed
+ Inline {
+ /// The element type of this table.
+ elem: ast::RefType<'a>,
+ /// The element table entries to have, and the length of this list is
+ /// the limits of the table as well.
+ payload: ElemPayload<'a>,
+ },
+}
+
+impl<'a> Parse<'a> for Table<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::table>()?.0;
+ let id = parser.parse()?;
+ let exports = parser.parse()?;
+
+ // Afterwards figure out which style this is, either:
+ //
+ // * `elemtype (elem ...)`
+ // * `(import "a" "b") limits`
+ // * `limits`
+ let mut l = parser.lookahead1();
+ let kind = if l.peek::<ast::RefType>() {
+ let elem = parser.parse()?;
+ let payload = parser.parens(|p| {
+ p.parse::<kw::elem>()?;
+ let ty = if parser.peek::<ast::LParen>() {
+ Some(elem)
+ } else {
+ None
+ };
+ ElemPayload::parse_tail(parser, ty)
+ })?;
+ TableKind::Inline { elem, payload }
+ } else if l.peek::<u32>() {
+ TableKind::Normal(parser.parse()?)
+ } else if let Some(import) = parser.parse()? {
+ TableKind::Import {
+ import,
+ ty: parser.parse()?,
+ }
+ } else {
+ return Err(l.error());
+ };
+ Ok(Table {
+ span,
+ id,
+ exports,
+ kind,
+ })
+ }
+}
+
+/// An `elem` segment in a WebAssembly module.
+#[derive(Debug)]
+pub struct Elem<'a> {
+ /// Where this `elem` was defined.
+ pub span: ast::Span,
+ /// An optional name by which to refer to this segment.
+ pub id: Option<ast::Id<'a>>,
+ /// The way this segment was defined in the module.
+ pub kind: ElemKind<'a>,
+ /// The payload of this element segment, typically a list of functions.
+ pub payload: ElemPayload<'a>,
+}
+
+/// Different ways to define an element segment in an mdoule.
+#[derive(Debug)]
+pub enum ElemKind<'a> {
+ /// A passive segment that isn't associated with a table and can be used in
+ /// various bulk-memory instructions.
+ Passive,
+
+ /// A declared element segment that is purely used to declare function
+ /// references.
+ Declared,
+
+ /// An active segment associated with a table.
+ Active {
+ /// The table this `elem` is initializing.
+ table: ast::ItemRef<'a, kw::table>,
+ /// The offset within `table` that we'll initialize at.
+ offset: ast::Expression<'a>,
+ },
+}
+
+/// Different ways to define the element segment payload in a module.
+#[derive(Debug, Clone)]
+pub enum ElemPayload<'a> {
+ /// This element segment has a contiguous list of function indices
+ Indices(Vec<ast::ItemRef<'a, kw::func>>),
+
+ /// This element segment has a list of optional function indices,
+ /// represented as expressions using `ref.func` and `ref.null`.
+ Exprs {
+ /// The desired type of each expression below.
+ ty: ast::RefType<'a>,
+ /// The expressions, currently optional function indices, in this
+ /// segment.
+ exprs: Vec<Option<ast::ItemRef<'a, kw::func>>>,
+ },
+}
+
+impl<'a> Parse<'a> for Elem<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::elem>()?.0;
+ let id = parser.parse()?;
+
+ let kind = if parser.peek::<u32>()
+ || (parser.peek::<ast::LParen>() && !parser.peek::<ast::RefType>())
+ {
+ let table = if let Some(index) = parser.parse::<Option<ast::IndexOrRef<_>>>()? {
+ index.0
+ } else {
+ ast::ItemRef::Item {
+ kind: kw::table(parser.prev_span()),
+ idx: ast::Index::Num(0, span),
+ exports: Vec::new(),
+ }
+ };
+ let offset = parser.parens(|parser| {
+ if parser.peek::<kw::offset>() {
+ parser.parse::<kw::offset>()?;
+ }
+ parser.parse()
+ })?;
+ ElemKind::Active { table, offset }
+ } else if parser.peek::<kw::declare>() {
+ parser.parse::<kw::declare>()?;
+ ElemKind::Declared
+ } else {
+ ElemKind::Passive
+ };
+ let payload = parser.parse()?;
+ Ok(Elem {
+ span,
+ id,
+ kind,
+ payload,
+ })
+ }
+}
+
+impl<'a> Parse<'a> for ElemPayload<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ ElemPayload::parse_tail(parser, parser.parse()?)
+ }
+}
+
+impl<'a> ElemPayload<'a> {
+ fn parse_tail(parser: Parser<'a>, ty: Option<ast::RefType<'a>>) -> Result<Self> {
+ let ty = match ty {
+ None => {
+ parser.parse::<Option<kw::func>>()?;
+ ast::RefType::func()
+ }
+ Some(ty) => ty,
+ };
+ if let ast::HeapType::Func = ty.heap {
+ if parser.peek::<ast::IndexOrRef<kw::func>>() {
+ let mut elems = Vec::new();
+ while !parser.is_empty() {
+ elems.push(parser.parse::<ast::IndexOrRef<_>>()?.0);
+ }
+ return Ok(ElemPayload::Indices(elems));
+ }
+ }
+ let mut exprs = Vec::new();
+ while !parser.is_empty() {
+ let func = parser.parens(|p| match p.parse::<Option<kw::item>>()? {
+ Some(_) => {
+ if parser.peek::<ast::LParen>() {
+ parser.parens(|p| parse_ref_func(p, ty))
+ } else {
+ parse_ref_func(parser, ty)
+ }
+ }
+ None => parse_ref_func(parser, ty),
+ })?;
+ exprs.push(func);
+ }
+ Ok(ElemPayload::Exprs { exprs, ty })
+ }
+}
+
+fn parse_ref_func<'a>(
+ parser: Parser<'a>,
+ ty: ast::RefType<'a>,
+) -> Result<Option<ast::ItemRef<'a, kw::func>>> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::ref_null>() {
+ parser.parse::<kw::ref_null>()?;
+ let null_ty: ast::HeapType = parser.parse()?;
+ if ty.heap != null_ty {
+ return Err(parser.error("elem segment item doesn't match elem segment type"));
+ }
+ Ok(None)
+ } else if l.peek::<kw::ref_func>() {
+ parser.parse::<kw::ref_func>()?;
+ Ok(Some(parser.parse::<ast::IndexOrRef<_>>()?.0))
+ } else {
+ Err(l.error())
+ }
+}
diff --git a/third_party/rust/wast/src/ast/token.rs b/third_party/rust/wast/src/ast/token.rs
new file mode 100644
index 0000000000..581627cb3d
--- /dev/null
+++ b/third_party/rust/wast/src/ast/token.rs
@@ -0,0 +1,757 @@
+use crate::ast::{annotation, kw};
+use crate::lexer::FloatVal;
+use crate::parser::{Cursor, Parse, Parser, Peek, Result};
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::str;
+
+/// A position in the original source stream, used to render errors.
+#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
+pub struct Span {
+ pub(crate) offset: usize,
+}
+
+impl Span {
+ /// Construct a `Span` from a byte offset in the source file.
+ pub fn from_offset(offset: usize) -> Self {
+ Span { offset }
+ }
+
+ /// Returns the line/column information of this span within `text`.
+ /// Line and column numbers are 0-indexed. User presentation is typically
+ /// 1-indexed, but 0-indexing is appropriate for internal use with
+ /// iterators and slices.
+ pub fn linecol_in(&self, text: &str) -> (usize, usize) {
+ let mut cur = 0;
+ // Use split_terminator instead of lines so that if there is a `\r`,
+ // it is included in the offset calculation. The `+1` values below
+ // account for the `\n`.
+ for (i, line) in text.split_terminator('\n').enumerate() {
+ if cur + line.len() + 1 > self.offset {
+ return (i, self.offset - cur);
+ }
+ cur += line.len() + 1;
+ }
+ (text.lines().count(), 0)
+ }
+}
+
+/// An identifier in a WebAssembly module, prefixed by `$` in the textual
+/// format.
+///
+/// An identifier is used to symbolically refer to items in a a wasm module,
+/// typically via the [`Index`] type.
+#[derive(Copy, Clone)]
+pub struct Id<'a> {
+ name: &'a str,
+ gen: u32,
+ span: Span,
+}
+
+impl<'a> Id<'a> {
+ fn new(name: &'a str, span: Span) -> Id<'a> {
+ Id { name, gen: 0, span }
+ }
+
+ pub(crate) fn gensym(span: Span, gen: u32) -> Id<'a> {
+ Id {
+ name: "gensym",
+ gen,
+ span,
+ }
+ }
+
+ /// Returns the underlying name of this identifier.
+ ///
+ /// The name returned does not contain the leading `$`.
+ pub fn name(&self) -> &'a str {
+ self.name
+ }
+
+ /// Returns span of this identifier in the original source
+ pub fn span(&self) -> Span {
+ self.span
+ }
+
+ pub(crate) fn is_gensym(&self) -> bool {
+ self.gen != 0
+ }
+}
+
+impl<'a> Hash for Id<'a> {
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
+ self.name.hash(hasher);
+ self.gen.hash(hasher);
+ }
+}
+
+impl<'a> PartialEq for Id<'a> {
+ fn eq(&self, other: &Id<'a>) -> bool {
+ self.name == other.name && self.gen == other.gen
+ }
+}
+
+impl<'a> Eq for Id<'a> {}
+
+impl<'a> Parse<'a> for Id<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ parser.step(|c| {
+ if let Some((name, rest)) = c.id() {
+ return Ok((Id::new(name, c.cur_span()), rest));
+ }
+ Err(c.error("expected an identifier"))
+ })
+ }
+}
+
+impl fmt::Debug for Id<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.gen != 0 {
+ f.debug_struct("Id").field("gen", &self.gen).finish()
+ } else {
+ self.name.fmt(f)
+ }
+ }
+}
+
+impl Peek for Id<'_> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ cursor.id().is_some()
+ }
+
+ fn display() -> &'static str {
+ "an identifier"
+ }
+}
+
+/// A reference to another item in a wasm module.
+///
+/// This type is used for items referring to other items (such as `call $foo`
+/// referencing function `$foo`). References can be either an index (u32) or an
+/// [`Id`] in the textual format.
+///
+/// The emission phase of a module will ensure that `Index::Id` is never used
+/// and switch them all to `Index::Num`.
+#[derive(Copy, Clone, Debug)]
+pub enum Index<'a> {
+ /// A numerical index that this references. The index space this is
+ /// referencing is implicit based on where this [`Index`] is stored.
+ Num(u32, Span),
+ /// A human-readable identifier this references. Like `Num`, the namespace
+ /// this references is based on where this is stored.
+ Id(Id<'a>),
+}
+
+impl Index<'_> {
+ /// Returns the source location where this `Index` was defined.
+ pub fn span(&self) -> Span {
+ match self {
+ Index::Num(_, span) => *span,
+ Index::Id(id) => id.span(),
+ }
+ }
+
+ pub(crate) fn is_resolved(&self) -> bool {
+ match self {
+ Index::Num(..) => true,
+ _ => false,
+ }
+ }
+}
+
+impl<'a> Parse<'a> for Index<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<Id>() {
+ Ok(Index::Id(parser.parse()?))
+ } else if l.peek::<u32>() {
+ let (val, span) = parser.parse()?;
+ Ok(Index::Num(val, span))
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+impl Peek for Index<'_> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ u32::peek(cursor) || Id::peek(cursor)
+ }
+
+ fn display() -> &'static str {
+ "an index"
+ }
+}
+
+impl<'a> From<Id<'a>> for Index<'a> {
+ fn from(id: Id<'a>) -> Index<'a> {
+ Index::Id(id)
+ }
+}
+
+impl PartialEq for Index<'_> {
+ fn eq(&self, other: &Index<'_>) -> bool {
+ match (self, other) {
+ (Index::Num(a, _), Index::Num(b, _)) => a == b,
+ (Index::Id(a), Index::Id(b)) => a == b,
+ _ => false,
+ }
+ }
+}
+
+impl Eq for Index<'_> {}
+
+impl Hash for Index<'_> {
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
+ match self {
+ Index::Num(a, _) => {
+ 0u8.hash(hasher);
+ a.hash(hasher);
+ }
+ Index::Id(a) => {
+ 1u8.hash(hasher);
+ a.hash(hasher);
+ }
+ }
+ }
+}
+
+/// Parses `(func $foo)`
+///
+/// Optionally includes export strings for module-linking sugar syntax for alias
+/// injection.
+#[derive(Clone, Debug)]
+#[allow(missing_docs)]
+pub enum ItemRef<'a, K> {
+ Outer {
+ kind: K,
+ module: Index<'a>,
+ idx: Index<'a>,
+ },
+ Item {
+ kind: K,
+ idx: Index<'a>,
+ exports: Vec<&'a str>,
+ },
+}
+
+impl<'a, K> ItemRef<'a, K> {
+ /// Unwraps the underlying `Index` for `ItemRef::Item`.
+ ///
+ /// Panics if this is `ItemRef::Outer` or if exports haven't been expanded
+ /// yet.
+ pub fn unwrap_index(&self) -> &Index<'a> {
+ match self {
+ ItemRef::Item { idx, exports, .. } => {
+ debug_assert!(exports.len() == 0);
+ idx
+ }
+ ItemRef::Outer { .. } => panic!("unwrap_index called on Parent"),
+ }
+ }
+}
+
+impl<'a, K: Parse<'a>> Parse<'a> for ItemRef<'a, K> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ parser.parens(|parser| {
+ let kind = parser.parse::<K>()?;
+ if parser.peek::<kw::outer>() {
+ parser.parse::<kw::outer>()?;
+ let module = parser.parse()?;
+ let idx = parser.parse()?;
+ Ok(ItemRef::Outer { kind, module, idx })
+ } else {
+ let idx = parser.parse()?;
+ let mut exports = Vec::new();
+ while !parser.is_empty() {
+ exports.push(parser.parse()?);
+ }
+ Ok(ItemRef::Item { kind, idx, exports })
+ }
+ })
+ }
+}
+
+impl<'a, K: Peek> Peek for ItemRef<'a, K> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ match cursor.lparen() {
+ Some(remaining) => K::peek(remaining),
+ None => false,
+ }
+ }
+
+ fn display() -> &'static str {
+ "an item reference"
+ }
+}
+
+/// Convenience structure to parse `$f` or `(item $f)`.
+#[derive(Clone, Debug)]
+pub struct IndexOrRef<'a, K>(pub ItemRef<'a, K>);
+
+impl<'a, K> Parse<'a> for IndexOrRef<'a, K>
+where
+ K: Parse<'a> + Default,
+{
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if parser.peek::<Index<'_>>() {
+ Ok(IndexOrRef(ItemRef::Item {
+ kind: K::default(),
+ idx: parser.parse()?,
+ exports: Vec::new(),
+ }))
+ } else {
+ Ok(IndexOrRef(parser.parse()?))
+ }
+ }
+}
+
+impl<'a, K: Peek> Peek for IndexOrRef<'a, K> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ Index::peek(cursor) || ItemRef::<K>::peek(cursor)
+ }
+
+ fn display() -> &'static str {
+ "an item reference"
+ }
+}
+
+/// An `@name` annotation in source, currently of the form `@name "foo"`
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct NameAnnotation<'a> {
+ /// The name specified for the item
+ pub name: &'a str,
+}
+
+impl<'a> Parse<'a> for NameAnnotation<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ parser.parse::<annotation::name>()?;
+ let name = parser.parse()?;
+ Ok(NameAnnotation { name })
+ }
+}
+
+impl<'a> Parse<'a> for Option<NameAnnotation<'a>> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let _r = parser.register_annotation("name");
+ Ok(if parser.peek2::<annotation::name>() {
+ Some(parser.parens(|p| p.parse())?)
+ } else {
+ None
+ })
+ }
+}
+
+macro_rules! integers {
+ ($($i:ident($u:ident))*) => ($(
+ impl<'a> Parse<'a> for $i {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(parser.parse::<($i, Span)>()?.0)
+ }
+ }
+
+ impl<'a> Parse<'a> for ($i, Span) {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ parser.step(|c| {
+ if let Some((i, rest)) = c.integer() {
+ let (s, base) = i.val();
+ let val = $i::from_str_radix(s, base)
+ .or_else(|_| {
+ $u::from_str_radix(s, base).map(|i| i as $i)
+ });
+ return match val {
+ Ok(n) => Ok(((n, c.cur_span()), rest)),
+ Err(_) => Err(c.error(concat!(
+ "invalid ",
+ stringify!($i),
+ " number: constant out of range",
+ ))),
+ };
+ }
+ Err(c.error(concat!("expected a ", stringify!($i))))
+ })
+ }
+ }
+
+ impl Peek for $i {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ cursor.integer().is_some()
+ }
+
+ fn display() -> &'static str {
+ stringify!($i)
+ }
+ }
+ )*)
+}
+
+integers! {
+ u8(u8) u16(u16) u32(u32) u64(u64)
+ i8(u8) i16(u16) i32(u32) i64(u64)
+}
+
+impl<'a> Parse<'a> for &'a [u8] {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ parser.step(|c| {
+ if let Some((i, rest)) = c.string() {
+ return Ok((i, rest));
+ }
+ Err(c.error("expected a string"))
+ })
+ }
+}
+
+impl Peek for &'_ [u8] {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ cursor.string().is_some()
+ }
+
+ fn display() -> &'static str {
+ "string"
+ }
+}
+
+impl<'a> Parse<'a> for &'a str {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ str::from_utf8(parser.parse()?).map_err(|_| parser.error("malformed UTF-8 encoding"))
+ }
+}
+
+impl Parse<'_> for String {
+ fn parse(parser: Parser<'_>) -> Result<Self> {
+ Ok(<&str>::parse(parser)?.to_string())
+ }
+}
+
+impl Peek for &'_ str {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ <&[u8]>::peek(cursor)
+ }
+
+ fn display() -> &'static str {
+ <&[u8]>::display()
+ }
+}
+
+macro_rules! float {
+ ($($name:ident => {
+ bits: $int:ident,
+ float: $float:ident,
+ exponent_bits: $exp_bits:tt,
+ name: $parse:ident,
+ })*) => ($(
+ /// A parsed floating-point type
+ #[derive(Debug)]
+ pub struct $name {
+ /// The raw bits that this floating point number represents.
+ pub bits: $int,
+ }
+
+ impl<'a> Parse<'a> for $name {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ parser.step(|c| {
+ let (val, rest) = if let Some((f, rest)) = c.float() {
+ ($parse(f.val()), rest)
+ } else if let Some((i, rest)) = c.integer() {
+ let (s, base) = i.val();
+ (
+ $parse(&FloatVal::Val {
+ hex: base == 16,
+ integral: s.into(),
+ decimal: None,
+ exponent: None,
+ }),
+ rest,
+ )
+ } else {
+ return Err(c.error("expected a float"));
+ };
+ match val {
+ Some(bits) => Ok(($name { bits }, rest)),
+ None => Err(c.error("invalid float value: constant out of range")),
+ }
+ })
+ }
+ }
+
+ fn $parse(val: &FloatVal<'_>) -> Option<$int> {
+ // Compute a few well-known constants about the float representation
+ // given the parameters to the macro here.
+ let width = std::mem::size_of::<$int>() * 8;
+ let neg_offset = width - 1;
+ let exp_offset = neg_offset - $exp_bits;
+ let signif_bits = width - 1 - $exp_bits;
+ let signif_mask = (1 << exp_offset) - 1;
+ let bias = (1 << ($exp_bits - 1)) - 1;
+
+ let (hex, integral, decimal, exponent_str) = match val {
+ // Infinity is when the exponent bits are all set and
+ // the significand is zero.
+ FloatVal::Inf { negative } => {
+ let exp_bits = (1 << $exp_bits) - 1;
+ let neg_bit = *negative as $int;
+ return Some(
+ (neg_bit << neg_offset) |
+ (exp_bits << exp_offset)
+ );
+ }
+
+ // NaN is when the exponent bits are all set and
+ // the significand is nonzero. The default of NaN is
+ // when only the highest bit of the significand is set.
+ FloatVal::Nan { negative, val } => {
+ let exp_bits = (1 << $exp_bits) - 1;
+ let neg_bit = *negative as $int;
+ let signif = val.unwrap_or(1 << (signif_bits - 1)) as $int;
+ // If the significand is zero then this is actually infinity
+ // so we fail to parse it.
+ if signif & signif_mask == 0 {
+ return None;
+ }
+ return Some(
+ (neg_bit << neg_offset) |
+ (exp_bits << exp_offset) |
+ (signif & signif_mask)
+ );
+ }
+
+ // This is trickier, handle this below
+ FloatVal::Val { hex, integral, decimal, exponent } => {
+ (hex, integral, decimal, exponent)
+ }
+ };
+
+ // Rely on Rust's standard library to parse base 10 floats
+ // correctly.
+ if !*hex {
+ let mut s = integral.to_string();
+ if let Some(decimal) = decimal {
+ s.push_str(".");
+ s.push_str(&decimal);
+ }
+ if let Some(exponent) = exponent_str {
+ s.push_str("e");
+ s.push_str(&exponent);
+ }
+ let float = s.parse::<$float>().ok()?;
+ // looks like the `*.wat` format considers infinite overflow to
+ // be invalid.
+ if float.is_infinite() {
+ return None;
+ }
+ return Some(float.to_bits());
+ }
+
+ // Parsing hex floats is... hard! I don't really know what most of
+ // this below does. It was copied from Gecko's implementation in
+ // `WasmTextToBinary.cpp`. Would love comments on this if you have
+ // them!
+ let decimal = decimal.as_ref().map(|s| &**s).unwrap_or("");
+ let negative = integral.starts_with('-');
+ let integral = integral.trim_start_matches('-').trim_start_matches('0');
+
+ // Do a bunch of work up front to locate the first non-zero digit
+ // to determine the initial exponent. There's a number of
+ // adjustments depending on where the digit was found, but the
+ // general idea here is that I'm not really sure why things are
+ // calculated the way they are but it should match Gecko.
+ let decimal_no_leading = decimal.trim_start_matches('0');
+ let decimal_iter = if integral.is_empty() {
+ decimal_no_leading.chars()
+ } else {
+ decimal.chars()
+ };
+ let mut digits = integral.chars()
+ .map(|c| (to_hex(c) as $int, false))
+ .chain(decimal_iter.map(|c| (to_hex(c) as $int, true)));
+ let lead_nonzero_digit = match digits.next() {
+ Some((c, _)) => c,
+ // No digits? Must be `+0` or `-0`, being careful to handle the
+ // sign encoding here.
+ None if negative => return Some(1 << (width - 1)),
+ None => return Some(0),
+ };
+ let mut significand = 0 as $int;
+ let mut exponent = if !integral.is_empty() {
+ 1
+ } else {
+ -((decimal.len() - decimal_no_leading.len() + 1) as i32) + 1
+ };
+ let lz = (lead_nonzero_digit as u8).leading_zeros() as i32 - 4;
+ exponent = exponent.checked_mul(4)?.checked_sub(lz + 1)?;
+ let mut significand_pos = (width - (4 - (lz as usize))) as isize;
+ assert!(significand_pos >= 0);
+ significand |= lead_nonzero_digit << significand_pos;
+
+ // Now that we've got an anchor in the string we parse the remaining
+ // digits. Again, not entirely sure why everything is the way it is
+ // here! This is copied frmo gecko.
+ let mut discarded_extra_nonzero = false;
+ for (digit, decimal) in digits {
+ if !decimal {
+ exponent += 4;
+ }
+ if significand_pos > -4 {
+ significand_pos -= 4;
+ }
+
+ if significand_pos >= 0 {
+ significand |= digit << significand_pos;
+ } else if significand_pos > -4 {
+ significand |= digit >> (4 - significand_pos);
+ discarded_extra_nonzero = (digit & !((!0) >> (4 - significand_pos))) != 0;
+ } else if digit != 0 {
+ discarded_extra_nonzero = true;
+ }
+ }
+
+ exponent = exponent.checked_add(match exponent_str {
+ Some(s) => s.parse::<i32>().ok()?,
+ None => 0,
+ })?;
+ debug_assert!(significand != 0);
+
+ let (encoded_exponent, encoded_significand, discarded_significand) =
+ if exponent <= -bias {
+ // Underflow to subnormal or zero.
+ let shift = exp_offset as i32 + exponent + bias;
+ if shift == 0 {
+ (0, 0, significand)
+ } else if shift < 0 || shift >= width as i32 {
+ (0, 0, 0)
+ } else {
+ (
+ 0,
+ significand >> (width as i32 - shift),
+ significand << shift,
+ )
+ }
+ } else if exponent <= bias {
+ // Normal (non-zero). The significand's leading 1 is encoded
+ // implicitly.
+ (
+ ((exponent + bias) as $int) << exp_offset,
+ (significand >> (width - exp_offset - 1)) & signif_mask,
+ significand << (exp_offset + 1),
+ )
+ } else {
+ // Overflow to infinity.
+ (
+ ((1 << $exp_bits) - 1) << exp_offset,
+ 0,
+ 0,
+ )
+ };
+
+ let bits = encoded_exponent | encoded_significand;
+
+ // Apply rounding. If this overflows the significand, it carries
+ // into the exponent bit according to the magic of the IEEE 754
+ // encoding.
+ //
+ // Or rather, the comment above is what Gecko says so it's copied
+ // here too.
+ let msb = 1 << (width - 1);
+ let bits = bits
+ + (((discarded_significand & msb != 0)
+ && ((discarded_significand & !msb != 0) ||
+ discarded_extra_nonzero ||
+ // ties to even
+ (encoded_significand & 1 != 0))) as $int);
+
+ // Just before we return the bits be sure to handle the sign bit we
+ // found at the beginning.
+ let bits = if negative {
+ bits | (1 << (width - 1))
+ } else {
+ bits
+ };
+ // looks like the `*.wat` format considers infinite overflow to
+ // be invalid.
+ if $float::from_bits(bits).is_infinite() {
+ return None;
+ }
+ Some(bits)
+ }
+
+ )*)
+}
+
+float! {
+ Float32 => {
+ bits: u32,
+ float: f32,
+ exponent_bits: 8,
+ name: strtof,
+ }
+ Float64 => {
+ bits: u64,
+ float: f64,
+ exponent_bits: 11,
+ name: strtod,
+ }
+}
+
+fn to_hex(c: char) -> u8 {
+ match c {
+ 'a'..='f' => c as u8 - b'a' + 10,
+ 'A'..='F' => c as u8 - b'A' + 10,
+ _ => c as u8 - b'0',
+ }
+}
+
+/// A convenience type to use with [`Parser::peek`](crate::parser::Parser::peek)
+/// to see if the next token is an s-expression.
+pub struct LParen {
+ _priv: (),
+}
+
+impl Peek for LParen {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ cursor.lparen().is_some()
+ }
+
+ fn display() -> &'static str {
+ "left paren"
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn hex_strtof() {
+ macro_rules! f {
+ ($a:tt) => (f!(@mk $a, None, None));
+ ($a:tt p $e:tt) => (f!(@mk $a, None, Some($e.into())));
+ ($a:tt . $b:tt) => (f!(@mk $a, Some($b.into()), None));
+ ($a:tt . $b:tt p $e:tt) => (f!(@mk $a, Some($b.into()), Some($e.into())));
+ (@mk $a:tt, $b:expr, $e:expr) => (crate::lexer::FloatVal::Val {
+ hex: true,
+ integral: $a.into(),
+ decimal: $b,
+ exponent: $e
+ });
+ }
+ assert_eq!(super::strtof(&f!("0")), Some(0));
+ assert_eq!(super::strtof(&f!("0" . "0")), Some(0));
+ assert_eq!(super::strtof(&f!("0" . "0" p "2354")), Some(0));
+ assert_eq!(super::strtof(&f!("-0")), Some(1 << 31));
+ assert_eq!(super::strtof(&f!("f32")), Some(0x45732000));
+ assert_eq!(super::strtof(&f!("0" . "f32")), Some(0x3f732000));
+ assert_eq!(super::strtof(&f!("1" . "2")), Some(0x3f900000));
+ assert_eq!(
+ super::strtof(&f!("0" . "00000100000000000" p "-126")),
+ Some(0)
+ );
+ assert_eq!(
+ super::strtof(&f!("1" . "fffff4" p "-106")),
+ Some(0x0afffffa)
+ );
+ assert_eq!(super::strtof(&f!("fffff98" p "-133")), Some(0x0afffffa));
+ assert_eq!(super::strtof(&f!("0" . "081" p "023")), Some(0x48810000));
+ assert_eq!(
+ super::strtof(&f!("1" . "00000100000000000" p "-50")),
+ Some(0x26800000)
+ );
+ }
+}
diff --git a/third_party/rust/wast/src/ast/types.rs b/third_party/rust/wast/src/ast/types.rs
new file mode 100644
index 0000000000..8a27c13bde
--- /dev/null
+++ b/third_party/rust/wast/src/ast/types.rs
@@ -0,0 +1,807 @@
+use crate::ast::{self, kw};
+use crate::parser::{Cursor, Parse, Parser, Peek, Result};
+use std::mem;
+
+/// The value types for a wasm module.
+#[allow(missing_docs)]
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub enum ValType<'a> {
+ I32,
+ I64,
+ F32,
+ F64,
+ V128,
+ Ref(RefType<'a>),
+ Rtt(u32, ast::Index<'a>),
+}
+
+impl<'a> Parse<'a> for ValType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::i32>() {
+ parser.parse::<kw::i32>()?;
+ Ok(ValType::I32)
+ } else if l.peek::<kw::i64>() {
+ parser.parse::<kw::i64>()?;
+ Ok(ValType::I64)
+ } else if l.peek::<kw::f32>() {
+ parser.parse::<kw::f32>()?;
+ Ok(ValType::F32)
+ } else if l.peek::<kw::f64>() {
+ parser.parse::<kw::f64>()?;
+ Ok(ValType::F64)
+ } else if l.peek::<kw::v128>() {
+ parser.parse::<kw::v128>()?;
+ Ok(ValType::V128)
+ } else if l.peek::<RefType>() {
+ Ok(ValType::Ref(parser.parse()?))
+ } else if l.peek::<ast::LParen>() {
+ parser.parens(|p| {
+ let mut l = p.lookahead1();
+ if l.peek::<kw::rtt>() {
+ p.parse::<kw::rtt>()?;
+ Ok(ValType::Rtt(p.parse()?, p.parse()?))
+ } else {
+ Err(l.error())
+ }
+ })
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+impl<'a> Peek for ValType<'a> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ kw::i32::peek(cursor)
+ || kw::i64::peek(cursor)
+ || kw::f32::peek(cursor)
+ || kw::f64::peek(cursor)
+ || kw::v128::peek(cursor)
+ || (ast::LParen::peek(cursor) && kw::rtt::peek2(cursor))
+ || RefType::peek(cursor)
+ }
+ fn display() -> &'static str {
+ "valtype"
+ }
+}
+
+/// A heap type for a reference type
+#[allow(missing_docs)]
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub enum HeapType<'a> {
+ /// An untyped function reference: funcref. This is part of the reference
+ /// types proposal.
+ Func,
+ /// A reference to any host value: externref. This is part of the reference
+ /// types proposal.
+ Extern,
+ /// A reference to any reference value: anyref. This is part of the GC
+ /// proposal.
+ Any,
+ /// A reference to an exception: exnref. This is part of the exception
+ /// handling proposal.
+ Exn,
+ /// A reference that has an identity that can be compared: eqref. This is
+ /// part of the GC proposal.
+ Eq,
+ /// An unboxed 31-bit integer: i31ref. This may be going away if there is no common
+ /// supertype of all reference types. Part of the GC proposal.
+ I31,
+ /// A reference to a function, struct, or array: ref T. This is part of the
+ /// GC proposal.
+ Index(ast::Index<'a>),
+}
+
+impl<'a> Parse<'a> for HeapType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::func>() {
+ parser.parse::<kw::func>()?;
+ Ok(HeapType::Func)
+ } else if l.peek::<kw::r#extern>() {
+ parser.parse::<kw::r#extern>()?;
+ Ok(HeapType::Extern)
+ } else if l.peek::<kw::r#any>() {
+ parser.parse::<kw::r#any>()?;
+ Ok(HeapType::Any)
+ } else if l.peek::<kw::exn>() {
+ parser.parse::<kw::exn>()?;
+ Ok(HeapType::Exn)
+ } else if l.peek::<kw::eq>() {
+ parser.parse::<kw::eq>()?;
+ Ok(HeapType::Eq)
+ } else if l.peek::<kw::i31>() {
+ parser.parse::<kw::i31>()?;
+ Ok(HeapType::I31)
+ } else if l.peek::<ast::Index>() {
+ Ok(HeapType::Index(parser.parse()?))
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+impl<'a> Peek for HeapType<'a> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ kw::func::peek(cursor)
+ || kw::r#extern::peek(cursor)
+ || kw::any::peek(cursor)
+ || kw::exn::peek(cursor)
+ || kw::eq::peek(cursor)
+ || kw::i31::peek(cursor)
+ || (ast::LParen::peek(cursor) && kw::r#type::peek2(cursor))
+ }
+ fn display() -> &'static str {
+ "heaptype"
+ }
+}
+
+/// A reference type in a wasm module.
+#[allow(missing_docs)]
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub struct RefType<'a> {
+ pub nullable: bool,
+ pub heap: HeapType<'a>,
+}
+
+impl<'a> RefType<'a> {
+ /// A `funcref` as an abbreviation for `(ref null func)`.
+ pub fn func() -> Self {
+ RefType {
+ nullable: true,
+ heap: HeapType::Func,
+ }
+ }
+
+ /// An `externref` as an abbreviation for `(ref null extern)`.
+ pub fn r#extern() -> Self {
+ RefType {
+ nullable: true,
+ heap: HeapType::Extern,
+ }
+ }
+
+ /// An `anyref` as an abbreviation for `(ref null any)`.
+ pub fn any() -> Self {
+ RefType {
+ nullable: true,
+ heap: HeapType::Any,
+ }
+ }
+
+ /// An `exnref` as an abbreviation for `(ref null exn)`.
+ pub fn exn() -> Self {
+ RefType {
+ nullable: true,
+ heap: HeapType::Exn,
+ }
+ }
+
+ /// An `eqref` as an abbreviation for `(ref null eq)`.
+ pub fn eq() -> Self {
+ RefType {
+ nullable: true,
+ heap: HeapType::Eq,
+ }
+ }
+
+ /// An `i31ref` as an abbreviation for `(ref null i31)`.
+ pub fn i31() -> Self {
+ RefType {
+ nullable: true,
+ heap: HeapType::I31,
+ }
+ }
+}
+
+impl<'a> Parse<'a> for RefType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::funcref>() {
+ parser.parse::<kw::funcref>()?;
+ Ok(RefType::func())
+ } else if l.peek::<kw::anyfunc>() {
+ parser.parse::<kw::anyfunc>()?;
+ Ok(RefType::func())
+ } else if l.peek::<kw::externref>() {
+ parser.parse::<kw::externref>()?;
+ Ok(RefType::r#extern())
+ } else if l.peek::<kw::anyref>() {
+ parser.parse::<kw::anyref>()?;
+ Ok(RefType::any())
+ } else if l.peek::<kw::exnref>() {
+ parser.parse::<kw::exnref>()?;
+ Ok(RefType::exn())
+ } else if l.peek::<kw::eqref>() {
+ parser.parse::<kw::eqref>()?;
+ Ok(RefType::eq())
+ } else if l.peek::<kw::i31ref>() {
+ parser.parse::<kw::i31ref>()?;
+ Ok(RefType::i31())
+ } else if l.peek::<ast::LParen>() {
+ parser.parens(|p| {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::r#ref>() {
+ p.parse::<kw::r#ref>()?;
+
+ let mut nullable = false;
+ if parser.peek::<kw::null>() {
+ parser.parse::<kw::null>()?;
+ nullable = true;
+ }
+
+ Ok(RefType {
+ nullable,
+ heap: parser.parse()?,
+ })
+ } else {
+ Err(l.error())
+ }
+ })
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+impl<'a> Peek for RefType<'a> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ kw::funcref::peek(cursor)
+ || /* legacy */ kw::anyfunc::peek(cursor)
+ || kw::externref::peek(cursor)
+ || kw::anyref::peek(cursor)
+ || kw::exnref::peek(cursor)
+ || kw::eqref::peek(cursor)
+ || kw::i31ref::peek(cursor)
+ || (ast::LParen::peek(cursor) && kw::r#ref::peek2(cursor))
+ }
+ fn display() -> &'static str {
+ "reftype"
+ }
+}
+
+/// The types of values that may be used in a struct or array.
+#[allow(missing_docs)]
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub enum StorageType<'a> {
+ I8,
+ I16,
+ Val(ValType<'a>),
+}
+
+impl<'a> Parse<'a> for StorageType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::i8>() {
+ parser.parse::<kw::i8>()?;
+ Ok(StorageType::I8)
+ } else if l.peek::<kw::i16>() {
+ parser.parse::<kw::i16>()?;
+ Ok(StorageType::I16)
+ } else if l.peek::<ValType>() {
+ Ok(StorageType::Val(parser.parse()?))
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+/// Type for a `global` in a wasm module
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct GlobalType<'a> {
+ /// The element type of this `global`
+ pub ty: ValType<'a>,
+ /// Whether or not the global is mutable or not.
+ pub mutable: bool,
+}
+
+impl<'a> Parse<'a> for GlobalType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if parser.peek2::<kw::r#mut>() {
+ parser.parens(|p| {
+ p.parse::<kw::r#mut>()?;
+ Ok(GlobalType {
+ ty: parser.parse()?,
+ mutable: true,
+ })
+ })
+ } else {
+ Ok(GlobalType {
+ ty: parser.parse()?,
+ mutable: false,
+ })
+ }
+ }
+}
+
+/// Min/max limits used for tables/memories.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Limits {
+ /// The minimum number of units for this type.
+ pub min: u32,
+ /// An optional maximum number of units for this type.
+ pub max: Option<u32>,
+}
+
+impl<'a> Parse<'a> for Limits {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let min = parser.parse()?;
+ let max = if parser.peek::<u32>() {
+ Some(parser.parse()?)
+ } else {
+ None
+ };
+ Ok(Limits { min, max })
+ }
+}
+
+/// Min/max limits used for 64-bit memories
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Limits64 {
+ /// The minimum number of units for this type.
+ pub min: u64,
+ /// An optional maximum number of units for this type.
+ pub max: Option<u64>,
+}
+
+impl<'a> Parse<'a> for Limits64 {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let min = parser.parse()?;
+ let max = if parser.peek::<u64>() {
+ Some(parser.parse()?)
+ } else {
+ None
+ };
+ Ok(Limits64 { min, max })
+ }
+}
+
+/// Configuration for a table of a wasm mdoule
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct TableType<'a> {
+ /// Limits on the element sizes of this table
+ pub limits: Limits,
+ /// The type of element stored in this table
+ pub elem: RefType<'a>,
+}
+
+impl<'a> Parse<'a> for TableType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ Ok(TableType {
+ limits: parser.parse()?,
+ elem: parser.parse()?,
+ })
+ }
+}
+
+/// Configuration for a memory of a wasm module
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum MemoryType {
+ /// A 32-bit memory
+ B32 {
+ /// Limits on the page sizes of this memory
+ limits: Limits,
+ /// Whether or not this is a shared (atomic) memory type
+ shared: bool,
+ },
+ /// A 64-bit memory
+ B64 {
+ /// Limits on the page sizes of this memory
+ limits: Limits64,
+ /// Whether or not this is a shared (atomic) memory type
+ shared: bool,
+ },
+}
+
+impl<'a> Parse<'a> for MemoryType {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if parser.peek::<kw::i64>() {
+ parser.parse::<kw::i64>()?;
+ let limits = parser.parse()?;
+ let shared = parser.parse::<Option<kw::shared>>()?.is_some();
+ Ok(MemoryType::B64 { limits, shared })
+ } else {
+ parser.parse::<Option<kw::i32>>()?;
+ let limits = parser.parse()?;
+ let shared = parser.parse::<Option<kw::shared>>()?.is_some();
+ Ok(MemoryType::B32 { limits, shared })
+ }
+ }
+}
+
+/// A function type with parameters and results.
+#[derive(Clone, Debug, Default)]
+pub struct FunctionType<'a> {
+ /// The parameters of a function, optionally each having an identifier for
+ /// name resolution and a name for the custom `name` section.
+ pub params: Box<
+ [(
+ Option<ast::Id<'a>>,
+ Option<ast::NameAnnotation<'a>>,
+ ValType<'a>,
+ )],
+ >,
+ /// The results types of a function.
+ pub results: Box<[ValType<'a>]>,
+}
+
+impl<'a> FunctionType<'a> {
+ fn finish_parse(&mut self, allow_names: bool, parser: Parser<'a>) -> Result<()> {
+ let mut params = Vec::from(mem::take(&mut self.params));
+ let mut results = Vec::from(mem::take(&mut self.results));
+ while parser.peek2::<kw::param>() || parser.peek2::<kw::result>() {
+ parser.parens(|p| {
+ let mut l = p.lookahead1();
+ if l.peek::<kw::param>() {
+ if results.len() > 0 {
+ return Err(p.error(
+ "result before parameter (or unexpected token): \
+ cannot list params after results",
+ ));
+ }
+ p.parse::<kw::param>()?;
+ if p.is_empty() {
+ return Ok(());
+ }
+ let (id, name) = if allow_names {
+ (p.parse::<Option<_>>()?, p.parse::<Option<_>>()?)
+ } else {
+ (None, None)
+ };
+ let parse_more = id.is_none() && name.is_none();
+ let ty = p.parse()?;
+ params.push((id, name, ty));
+ while parse_more && !p.is_empty() {
+ params.push((None, None, p.parse()?));
+ }
+ } else if l.peek::<kw::result>() {
+ p.parse::<kw::result>()?;
+ while !p.is_empty() {
+ results.push(p.parse()?);
+ }
+ } else {
+ return Err(l.error());
+ }
+ Ok(())
+ })?;
+ }
+ self.params = params.into();
+ self.results = results.into();
+ Ok(())
+ }
+}
+
+impl<'a> Parse<'a> for FunctionType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut ret = FunctionType {
+ params: Box::new([]),
+ results: Box::new([]),
+ };
+ ret.finish_parse(true, parser)?;
+ Ok(ret)
+ }
+}
+
+impl<'a> Peek for FunctionType<'a> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ if let Some(next) = cursor.lparen() {
+ match next.keyword() {
+ Some(("param", _)) | Some(("result", _)) => return true,
+ _ => {}
+ }
+ }
+
+ false
+ }
+
+ fn display() -> &'static str {
+ "function type"
+ }
+}
+
+/// A function type with parameters and results.
+#[derive(Clone, Debug, Default)]
+pub struct FunctionTypeNoNames<'a>(pub FunctionType<'a>);
+
+impl<'a> Parse<'a> for FunctionTypeNoNames<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut ret = FunctionType {
+ params: Box::new([]),
+ results: Box::new([]),
+ };
+ ret.finish_parse(false, parser)?;
+ Ok(FunctionTypeNoNames(ret))
+ }
+}
+
+impl<'a> Peek for FunctionTypeNoNames<'a> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ FunctionType::peek(cursor)
+ }
+
+ fn display() -> &'static str {
+ FunctionType::display()
+ }
+}
+
+impl<'a> From<FunctionTypeNoNames<'a>> for FunctionType<'a> {
+ fn from(ty: FunctionTypeNoNames<'a>) -> FunctionType<'a> {
+ ty.0
+ }
+}
+
+/// A struct type with fields.
+#[derive(Clone, Debug)]
+pub struct StructType<'a> {
+ /// The fields of the struct
+ pub fields: Vec<StructField<'a>>,
+}
+
+impl<'a> Parse<'a> for StructType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut ret = StructType { fields: Vec::new() };
+ while !parser.is_empty() {
+ let field = if parser.peek2::<kw::field>() {
+ parser.parens(|parser| {
+ parser.parse::<kw::field>()?;
+ StructField::parse(parser, true)
+ })
+ } else {
+ StructField::parse(parser, false)
+ };
+ ret.fields.push(field?);
+ }
+ Ok(ret)
+ }
+}
+
+/// A field of a struct type.
+#[derive(Clone, Debug)]
+pub struct StructField<'a> {
+ /// An optional identifier for name resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// Whether this field may be mutated or not.
+ pub mutable: bool,
+ /// The storage type stored in this field.
+ pub ty: StorageType<'a>,
+}
+
+impl<'a> StructField<'a> {
+ fn parse(parser: Parser<'a>, with_id: bool) -> Result<Self> {
+ let id = if with_id { parser.parse()? } else { None };
+ let (ty, mutable) = if parser.peek2::<kw::r#mut>() {
+ let ty = parser.parens(|parser| {
+ parser.parse::<kw::r#mut>()?;
+ parser.parse()
+ })?;
+ (ty, true)
+ } else {
+ (parser.parse::<StorageType<'a>>()?, false)
+ };
+ Ok(StructField { id, mutable, ty })
+ }
+}
+
+/// An array type with fields.
+#[derive(Clone, Debug)]
+pub struct ArrayType<'a> {
+ /// Whether this field may be mutated or not.
+ pub mutable: bool,
+ /// The storage type stored in this field.
+ pub ty: StorageType<'a>,
+}
+
+impl<'a> Parse<'a> for ArrayType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let (ty, mutable) = if parser.peek2::<kw::r#mut>() {
+ let ty = parser.parens(|parser| {
+ parser.parse::<kw::r#mut>()?;
+ parser.parse()
+ })?;
+ (ty, true)
+ } else {
+ (parser.parse::<StorageType<'a>>()?, false)
+ };
+ Ok(ArrayType { mutable, ty })
+ }
+}
+
+/// A type for a nested module
+#[derive(Clone, Debug, Default)]
+pub struct ModuleType<'a> {
+ /// The imports that are expected for this module type.
+ pub imports: Vec<ast::Import<'a>>,
+ /// The exports that this module type is expected to have.
+ pub exports: Vec<ExportType<'a>>,
+}
+
+impl<'a> Parse<'a> for ModuleType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut imports = Vec::new();
+ while parser.peek2::<kw::import>() {
+ imports.push(parser.parens(|p| p.parse())?);
+ }
+ let mut exports = Vec::new();
+ while parser.peek2::<kw::export>() {
+ parser.parens(|p| {
+ exports.push(p.parse()?);
+ Ok(())
+ })?;
+ }
+ Ok(ModuleType { imports, exports })
+ }
+}
+
+impl<'a> Peek for ModuleType<'a> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ if let Some(next) = cursor.lparen() {
+ match next.keyword() {
+ Some(("import", _)) | Some(("export", _)) => return true,
+ _ => {}
+ }
+ }
+
+ false
+ }
+
+ fn display() -> &'static str {
+ "module type"
+ }
+}
+
+/// A type for a nested instance
+#[derive(Clone, Debug, Default)]
+pub struct InstanceType<'a> {
+ /// The exported types from this instance
+ pub exports: Vec<ExportType<'a>>,
+}
+
+impl<'a> Parse<'a> for InstanceType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut exports = Vec::new();
+ while !parser.is_empty() {
+ exports.push(parser.parens(|p| p.parse())?);
+ }
+ Ok(InstanceType { exports })
+ }
+}
+
+impl<'a> Peek for InstanceType<'a> {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ if let Some(next) = cursor.lparen() {
+ match next.keyword() {
+ Some(("export", _)) => return true,
+ _ => {}
+ }
+ }
+
+ false
+ }
+
+ fn display() -> &'static str {
+ "instance type"
+ }
+}
+
+/// The type of an exported item from a module or instance.
+#[derive(Debug, Clone)]
+pub struct ExportType<'a> {
+ /// Where this export was defined.
+ pub span: ast::Span,
+ /// The name of this export.
+ pub name: &'a str,
+ /// The signature of the item that's exported.
+ pub item: ast::ItemSig<'a>,
+}
+
+impl<'a> Parse<'a> for ExportType<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::export>()?.0;
+ let name = parser.parse()?;
+ let item = parser.parens(|p| p.parse())?;
+ Ok(ExportType { span, name, item })
+ }
+}
+
+/// A definition of a type.
+#[derive(Debug)]
+pub enum TypeDef<'a> {
+ /// A function type definition.
+ Func(FunctionType<'a>),
+ /// A struct type definition.
+ Struct(StructType<'a>),
+ /// An array type definition.
+ Array(ArrayType<'a>),
+ /// A module type definition.
+ Module(ModuleType<'a>),
+ /// An instance type definition.
+ Instance(InstanceType<'a>),
+}
+
+/// A type declaration in a module
+#[derive(Debug)]
+pub struct Type<'a> {
+ /// Where this type was defined.
+ pub span: ast::Span,
+ /// An optional identifer to refer to this `type` by as part of name
+ /// resolution.
+ pub id: Option<ast::Id<'a>>,
+ /// The type that we're declaring.
+ pub def: TypeDef<'a>,
+}
+
+impl<'a> Parse<'a> for Type<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::r#type>()?.0;
+ let id = parser.parse()?;
+ let def = parser.parens(|parser| {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::func>() {
+ parser.parse::<kw::func>()?;
+ Ok(TypeDef::Func(parser.parse()?))
+ } else if l.peek::<kw::r#struct>() {
+ parser.parse::<kw::r#struct>()?;
+ Ok(TypeDef::Struct(parser.parse()?))
+ } else if l.peek::<kw::array>() {
+ parser.parse::<kw::array>()?;
+ Ok(TypeDef::Array(parser.parse()?))
+ } else if l.peek::<kw::module>() {
+ parser.parse::<kw::module>()?;
+ Ok(TypeDef::Module(parser.parse()?))
+ } else if l.peek::<kw::instance>() {
+ parser.parse::<kw::instance>()?;
+ Ok(TypeDef::Instance(parser.parse()?))
+ } else {
+ Err(l.error())
+ }
+ })?;
+ Ok(Type { span, id, def })
+ }
+}
+
+/// A reference to a type defined in this module.
+#[derive(Clone, Debug)]
+pub struct TypeUse<'a, T> {
+ /// The type that we're referencing, if it was present.
+ pub index: Option<ast::ItemRef<'a, kw::r#type>>,
+ /// The inline type, if present.
+ pub inline: Option<T>,
+}
+
+impl<'a, T> TypeUse<'a, T> {
+ /// Constructs a new instance of `TypeUse` without an inline definition but
+ /// with an index specified.
+ pub fn new_with_index(idx: ast::Index<'a>) -> TypeUse<'a, T> {
+ TypeUse {
+ index: Some(ast::ItemRef::Item {
+ idx,
+ kind: kw::r#type::default(),
+ exports: Vec::new(),
+ }),
+ inline: None,
+ }
+ }
+}
+
+impl<'a, T: Peek + Parse<'a>> Parse<'a> for TypeUse<'a, T> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let index = if parser.peek2::<kw::r#type>() {
+ Some(parser.parse()?)
+ } else {
+ None
+ };
+ let inline = parser.parse()?;
+
+ Ok(TypeUse { index, inline })
+ }
+}
+
+impl<'a> From<TypeUse<'a, FunctionTypeNoNames<'a>>> for TypeUse<'a, FunctionType<'a>> {
+ fn from(src: TypeUse<'a, FunctionTypeNoNames<'a>>) -> TypeUse<'a, FunctionType<'a>> {
+ TypeUse {
+ index: src.index,
+ inline: src.inline.map(|x| x.into()),
+ }
+ }
+}
diff --git a/third_party/rust/wast/src/ast/wast.rs b/third_party/rust/wast/src/ast/wast.rs
new file mode 100644
index 0000000000..dd373d40e4
--- /dev/null
+++ b/third_party/rust/wast/src/ast/wast.rs
@@ -0,0 +1,356 @@
+use crate::ast::{self, kw};
+use crate::parser::{Cursor, Parse, Parser, Peek, Result};
+use crate::{AssertExpression, NanPattern, V128Pattern};
+
+/// A parsed representation of a `*.wast` file.
+///
+/// WAST files are not officially specified but are used in the official test
+/// suite to write official spec tests for wasm. This type represents a parsed
+/// `*.wast` file which parses a list of directives in a file.
+pub struct Wast<'a> {
+ #[allow(missing_docs)]
+ pub directives: Vec<WastDirective<'a>>,
+}
+
+impl<'a> Parse<'a> for Wast<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut directives = Vec::new();
+
+ // If it looks like a directive token is in the stream then we parse a
+ // bunch of directives, otherwise assume this is an inline module.
+ if parser.peek2::<WastDirectiveToken>() {
+ while !parser.is_empty() {
+ directives.push(parser.parens(|p| p.parse())?);
+ }
+ } else {
+ let module = parser.parse::<ast::Wat>()?.module;
+ directives.push(WastDirective::Module(module));
+ }
+ Ok(Wast { directives })
+ }
+}
+
+struct WastDirectiveToken;
+
+impl Peek for WastDirectiveToken {
+ fn peek(cursor: Cursor<'_>) -> bool {
+ let kw = match cursor.keyword() {
+ Some((kw, _)) => kw,
+ None => return false,
+ };
+ kw.starts_with("assert_") || kw == "module" || kw == "register" || kw == "invoke"
+ }
+
+ fn display() -> &'static str {
+ unimplemented!()
+ }
+}
+
+/// The different kinds of directives found in a `*.wast` file.
+///
+/// It's not entirely clear to me what all of these are per se, but they're only
+/// really interesting to test harnesses mostly.
+#[allow(missing_docs)]
+pub enum WastDirective<'a> {
+ Module(ast::Module<'a>),
+ QuoteModule {
+ span: ast::Span,
+ source: Vec<&'a [u8]>,
+ },
+ AssertMalformed {
+ span: ast::Span,
+ module: QuoteModule<'a>,
+ message: &'a str,
+ },
+ AssertInvalid {
+ span: ast::Span,
+ module: ast::Module<'a>,
+ message: &'a str,
+ },
+ Register {
+ span: ast::Span,
+ name: &'a str,
+ module: Option<ast::Id<'a>>,
+ },
+ Invoke(WastInvoke<'a>),
+ AssertTrap {
+ span: ast::Span,
+ exec: WastExecute<'a>,
+ message: &'a str,
+ },
+ AssertReturn {
+ span: ast::Span,
+ exec: WastExecute<'a>,
+ results: Vec<ast::AssertExpression<'a>>,
+ },
+ AssertExhaustion {
+ span: ast::Span,
+ call: WastInvoke<'a>,
+ message: &'a str,
+ },
+ AssertUnlinkable {
+ span: ast::Span,
+ module: ast::Module<'a>,
+ message: &'a str,
+ },
+}
+
+impl WastDirective<'_> {
+ /// Returns the location in the source that this directive was defined at
+ pub fn span(&self) -> ast::Span {
+ match self {
+ WastDirective::Module(m) => m.span,
+ WastDirective::AssertMalformed { span, .. }
+ | WastDirective::Register { span, .. }
+ | WastDirective::QuoteModule{ span, .. }
+ | WastDirective::AssertTrap { span, .. }
+ | WastDirective::AssertReturn { span, .. }
+ | WastDirective::AssertExhaustion { span, .. }
+ | WastDirective::AssertUnlinkable { span, .. }
+ | WastDirective::AssertInvalid { span, .. } => *span,
+ WastDirective::Invoke(i) => i.span,
+ }
+ }
+}
+
+impl<'a> Parse<'a> for WastDirective<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::module>() {
+ if parser.peek2::<kw::quote>() {
+ parser.parse::<kw::module>()?;
+ let span = parser.parse::<kw::quote>()?.0;
+ let mut source = Vec::new();
+ while !parser.is_empty() {
+ source.push(parser.parse()?);
+ }
+ Ok(WastDirective::QuoteModule { span, source })
+ } else {
+ Ok(WastDirective::Module(parser.parse()?))
+ }
+ } else if l.peek::<kw::assert_malformed>() {
+ let span = parser.parse::<kw::assert_malformed>()?.0;
+ Ok(WastDirective::AssertMalformed {
+ span,
+ module: parser.parens(|p| p.parse())?,
+ message: parser.parse()?,
+ })
+ } else if l.peek::<kw::assert_invalid>() {
+ let span = parser.parse::<kw::assert_invalid>()?.0;
+ Ok(WastDirective::AssertInvalid {
+ span,
+ module: parser.parens(|p| p.parse())?,
+ message: parser.parse()?,
+ })
+ } else if l.peek::<kw::register>() {
+ let span = parser.parse::<kw::register>()?.0;
+ Ok(WastDirective::Register {
+ span,
+ name: parser.parse()?,
+ module: parser.parse()?,
+ })
+ } else if l.peek::<kw::invoke>() {
+ Ok(WastDirective::Invoke(parser.parse()?))
+ } else if l.peek::<kw::assert_trap>() {
+ let span = parser.parse::<kw::assert_trap>()?.0;
+ Ok(WastDirective::AssertTrap {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ message: parser.parse()?,
+ })
+ } else if l.peek::<kw::assert_return>() {
+ let span = parser.parse::<kw::assert_return>()?.0;
+ let exec = parser.parens(|p| p.parse())?;
+ let mut results = Vec::new();
+ while !parser.is_empty() {
+ results.push(parser.parens(|p| p.parse())?);
+ }
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec,
+ results,
+ })
+ } else if l.peek::<kw::assert_return_canonical_nan>() {
+ let span = parser.parse::<kw::assert_return_canonical_nan>()?.0;
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ results: vec![AssertExpression::LegacyCanonicalNaN],
+ })
+ } else if l.peek::<kw::assert_return_canonical_nan_f32x4>() {
+ let span = parser.parse::<kw::assert_return_canonical_nan_f32x4>()?.0;
+ let pat = V128Pattern::F32x4([
+ NanPattern::CanonicalNan,
+ NanPattern::CanonicalNan,
+ NanPattern::CanonicalNan,
+ NanPattern::CanonicalNan,
+ ]);
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ results: vec![AssertExpression::V128(pat)],
+ })
+ } else if l.peek::<kw::assert_return_canonical_nan_f64x2>() {
+ let span = parser.parse::<kw::assert_return_canonical_nan_f64x2>()?.0;
+ let pat = V128Pattern::F64x2([NanPattern::CanonicalNan, NanPattern::CanonicalNan]);
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ results: vec![AssertExpression::V128(pat)],
+ })
+ } else if l.peek::<kw::assert_return_arithmetic_nan>() {
+ let span = parser.parse::<kw::assert_return_arithmetic_nan>()?.0;
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ results: vec![AssertExpression::LegacyArithmeticNaN],
+ })
+ } else if l.peek::<kw::assert_return_arithmetic_nan_f32x4>() {
+ let span = parser.parse::<kw::assert_return_arithmetic_nan_f32x4>()?.0;
+ let pat = V128Pattern::F32x4([
+ NanPattern::ArithmeticNan,
+ NanPattern::ArithmeticNan,
+ NanPattern::ArithmeticNan,
+ NanPattern::ArithmeticNan,
+ ]);
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ results: vec![AssertExpression::V128(pat)],
+ })
+ } else if l.peek::<kw::assert_return_arithmetic_nan_f64x2>() {
+ let span = parser.parse::<kw::assert_return_arithmetic_nan_f64x2>()?.0;
+ let pat = V128Pattern::F64x2([NanPattern::ArithmeticNan, NanPattern::ArithmeticNan]);
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ results: vec![AssertExpression::V128(pat)],
+ })
+ } else if l.peek::<kw::assert_return_func>() {
+ let span = parser.parse::<kw::assert_return_func>()?.0;
+ Ok(WastDirective::AssertReturn {
+ span,
+ exec: parser.parens(|p| p.parse())?,
+ results: vec![AssertExpression::RefFunc(None)],
+ })
+ } else if l.peek::<kw::assert_exhaustion>() {
+ let span = parser.parse::<kw::assert_exhaustion>()?.0;
+ Ok(WastDirective::AssertExhaustion {
+ span,
+ call: parser.parens(|p| p.parse())?,
+ message: parser.parse()?,
+ })
+ } else if l.peek::<kw::assert_unlinkable>() {
+ let span = parser.parse::<kw::assert_unlinkable>()?.0;
+ Ok(WastDirective::AssertUnlinkable {
+ span,
+ module: parser.parens(|p| p.parse())?,
+ message: parser.parse()?,
+ })
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+#[allow(missing_docs)]
+pub enum WastExecute<'a> {
+ Invoke(WastInvoke<'a>),
+ Module(ast::Module<'a>),
+ Get {
+ module: Option<ast::Id<'a>>,
+ global: &'a str,
+ },
+}
+
+impl<'a> Parse<'a> for WastExecute<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let mut l = parser.lookahead1();
+ if l.peek::<kw::invoke>() {
+ Ok(WastExecute::Invoke(parser.parse()?))
+ } else if l.peek::<kw::module>() {
+ Ok(WastExecute::Module(parser.parse()?))
+ } else if l.peek::<kw::get>() {
+ parser.parse::<kw::get>()?;
+ Ok(WastExecute::Get {
+ module: parser.parse()?,
+ global: parser.parse()?,
+ })
+ } else {
+ Err(l.error())
+ }
+ }
+}
+
+#[allow(missing_docs)]
+pub struct WastInvoke<'a> {
+ pub span: ast::Span,
+ pub module: Option<ast::Id<'a>>,
+ pub name: &'a str,
+ pub args: Vec<ast::Expression<'a>>,
+}
+
+impl<'a> Parse<'a> for WastInvoke<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ let span = parser.parse::<kw::invoke>()?.0;
+ let module = parser.parse()?;
+ let name = parser.parse()?;
+ let mut args = Vec::new();
+ while !parser.is_empty() {
+ args.push(parser.parens(|p| p.parse())?);
+ }
+ Ok(WastInvoke {
+ span,
+ module,
+ name,
+ args,
+ })
+ }
+}
+
+#[allow(missing_docs)]
+pub enum QuoteModule<'a> {
+ Module(ast::Module<'a>),
+ Quote(Vec<&'a [u8]>),
+}
+
+impl<'a> Parse<'a> for QuoteModule<'a> {
+ fn parse(parser: Parser<'a>) -> Result<Self> {
+ if parser.peek2::<kw::quote>() {
+ parser.parse::<kw::module>()?;
+ parser.parse::<kw::quote>()?;
+ let mut src = Vec::new();
+ while !parser.is_empty() {
+ src.push(parser.parse()?);
+ }
+ Ok(QuoteModule::Quote(src))
+ } else {
+ Ok(QuoteModule::Module(parser.parse()?))
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::ast::wast::WastDirective;
+ use crate::parser::{parse, ParseBuffer};
+
+ macro_rules! assert_parses_to_directive {
+ ($text:expr, $pattern:pat) => {{
+ let buffer = ParseBuffer::new($text).unwrap();
+ let directive: WastDirective = parse(&buffer).unwrap();
+ if let $pattern = directive {
+ } else {
+ panic!("assertion failed")
+ }
+ }};
+ }
+
+ #[test]
+ fn assert_nan() {
+ assert_parses_to_directive!("assert_return_canonical_nan_f32x4 (invoke \"foo\" (f32.const 0))", WastDirective::AssertReturn { .. });
+ assert_parses_to_directive!("assert_return_canonical_nan_f64x2 (invoke \"foo\" (f32.const 0))", WastDirective::AssertReturn { .. });
+ assert_parses_to_directive!("assert_return_arithmetic_nan_f32x4 (invoke \"foo\" (f32.const 0))", WastDirective::AssertReturn { .. });
+ assert_parses_to_directive!("assert_return_arithmetic_nan_f64x2 (invoke \"foo\" (f32.const 0))", WastDirective::AssertReturn { .. });
+ }
+}