diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/wast/src/ast | |
parent | Initial commit. (diff) | |
download | firefox-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.rs | 76 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/assert_expr.rs | 154 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/custom.rs | 165 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/event.rs | 44 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/export.rs | 141 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/expr.rs | 1767 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/func.rs | 115 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/global.rs | 53 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/import.rs | 176 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/instance.rs | 86 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/memory.rs | 250 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/mod.rs | 430 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/module.rs | 239 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/nested_module.rs | 115 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/table.rs | 234 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/token.rs | 757 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/types.rs | 807 | ||||
-rw-r--r-- | third_party/rust/wast/src/ast/wast.rs | 356 |
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 { .. }); + } +} |