зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1770081 - Update wat/wast rust libraries. r=rhunt,glandium
wat syntax change/fixes: remove data "passive" fix table.copy misc call_indirect fixes Differential Revision: https://phabricator.services.mozilla.com/D146863
This commit is contained in:
Родитель
f42205257e
Коммит
94d55a2495
|
@ -5846,9 +5846,9 @@ checksum = "52144d4c78e5cf8b055ceab8e5fa22814ce4315d6002ad32cfd914f37c12fd65"
|
|||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "39.0.0"
|
||||
version = "41.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9bbbd53432b267421186feee3e52436531fa69a7cfee9403f5204352df3dd05"
|
||||
checksum = "f882898b8b817cc4edc16aa3692fdc087b356edc8cc0c2164f5b5181e31c3870"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
"memchr",
|
||||
|
@ -5857,9 +5857,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.0.41"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab98ed25494f97c69f28758617f27c3e92e5336040b5c3a14634f2dd3fe61830"
|
||||
checksum = "48b3b9b3e39e66c7fd3f8be785e74444d216260f491e93369e317ed6482ff80f"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
`(module
|
||||
(import "m" "exn" (tag $exn))
|
||||
(tag $localExn (param i32))
|
||||
(type $t (func))
|
||||
(import "m" "tab" (table $importTable 2 funcref))
|
||||
(import "m" "throwsExn" (func $importFuncThrowsExn))
|
||||
(memory $mem (data "foo"))
|
||||
|
@ -65,11 +66,11 @@
|
|||
["(call $anotherLocalFuncThrowsExn)",
|
||||
"(call $importFuncThrowsExn)",
|
||||
// Calls $localFuncThrowsExn.
|
||||
"(call_indirect (table $localTable) (i32.const 0))",
|
||||
"(call_indirect $localTable (type $t) (i32.const 0))",
|
||||
// Calls $importFuncThrowsExn.
|
||||
"(call_indirect (table $localTable) (i32.const 1))",
|
||||
"(call_indirect $localTable (type $t) (i32.const 1))",
|
||||
// Calls non exported function of the exports module $anotherThrowsExn.
|
||||
"(call_indirect (table $importTable) (i32.const 0))"];
|
||||
"(call_indirect $importTable (type $t) (i32.const 0))"];
|
||||
|
||||
for (let callInstruction of callInstructions) {
|
||||
testMemoryAfterCall(callInstruction);
|
||||
|
|
|
@ -16,7 +16,7 @@ for ( let [pages,maxpages] of [[pages_vanilla, pages_vanilla+100],
|
|||
(memory (export "mem") ${pages} ${maxpages})
|
||||
|
||||
(data (i32.const ${(pages-5)*pagesz}) "yabbadabbado")
|
||||
(data $flintstone passive "yabbadabbado")
|
||||
(data $flintstone "yabbadabbado")
|
||||
|
||||
(func (export "get_constaddr") (result i32)
|
||||
(i32.load (i32.const ${pages*pagesz-4})))
|
||||
|
|
|
@ -498,7 +498,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`
|
|||
|
||||
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`
|
||||
(module
|
||||
(data $d passive "01234")
|
||||
(data $d "01234")
|
||||
(func (param i32 i32)
|
||||
(memory.init $d (local.get 0) (local.get 1))))`)),
|
||||
WebAssembly.CompileError,
|
||||
|
|
|
@ -134,7 +134,7 @@ for (let [memType, ptrType] of memTypes ) {
|
|||
assertEq(WebAssembly.validate(wasmTextToBinary(`
|
||||
(module
|
||||
(memory ${memType} 1)
|
||||
(data $seg passive "0123456789abcdef")
|
||||
(data $seg "0123456789abcdef")
|
||||
(func (param $p ${ptrType})
|
||||
(drop (${ptrType}.add (${ptrType}.const 1) (memory.size)))
|
||||
(drop (${ptrType}.add (${ptrType}.const 1) (memory.grow (${ptrType}.const 1))))
|
||||
|
@ -1435,7 +1435,7 @@ function makeModule(initial, maximum, shared) {
|
|||
(module
|
||||
(memory (export "mem") i64 ${initial} ${maximum} ${shared})
|
||||
|
||||
(data $seg passive "0123456789")
|
||||
(data $seg "0123456789")
|
||||
|
||||
(func (export "size") (result i64)
|
||||
memory.size)
|
||||
|
|
|
@ -12,7 +12,7 @@ for (let shared of ['', 'shared']) {
|
|||
var ins = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`
|
||||
(module
|
||||
(memory (export "mem") i64 65537 65537 ${shared})
|
||||
(data $d passive "${S}")
|
||||
(data $d "${S}")
|
||||
(func (export "f") (param $p i64) (param $o i32) (param $n i32)
|
||||
(memory.init $d (local.get $p) (local.get $o) (local.get $n))))`)));
|
||||
} catch (e) {
|
||||
|
|
|
@ -413,16 +413,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f
|
||||
(table.copy 0 (i32.const 0) (i32.const 0) (i32.const 2))))`), // target without source
|
||||
SyntaxError,
|
||||
/expected keyword `table`/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t0 2 funcref)
|
||||
(table $t1 2 funcref)
|
||||
(func $f
|
||||
(table.copy (i32.const 0) 0 (i32.const 0) (i32.const 2))))`), // source without target
|
||||
SyntaxError,
|
||||
/wasm text error/);
|
||||
/unexpected token, expected an identifier or u32/);
|
||||
|
||||
// Make sure that dead code doesn't prevent compilation.
|
||||
wasmEvalText(
|
||||
|
|
|
@ -27,7 +27,7 @@ var ins = wasmEvalText(`
|
|||
(func (export "wasm2import") (result i32)
|
||||
(call $g))
|
||||
(func (export "wasmIndirect") (result i32)
|
||||
(call_indirect (type $ty) $t (i32.const 0)))
|
||||
(call_indirect $t (type $ty) (i32.const 0)))
|
||||
(func (export "instanceCall") (result i32)
|
||||
(memory.size))
|
||||
)`, {'':{'wasm2import': function() {}}});
|
||||
|
|
|
@ -22,5 +22,5 @@ gluesmith = ['jsrust_shared/gluesmith']
|
|||
jsrust_shared = { path = "./shared" }
|
||||
# Workaround for https://github.com/rust-lang/rust/issues/58393
|
||||
mozglue-static = { path = "../../../mozglue/static/rust" }
|
||||
wat = { version = "1.0.39" }
|
||||
wat = { version = "1.0.43" }
|
||||
wasmparser = { version = "0.78.2" }
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -10,20 +10,24 @@
|
|||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
name = "wast"
|
||||
version = "39.0.0"
|
||||
version = "41.0.0"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
description = "Customizable Rust parsers for the WebAssembly Text formats WAT and WAST\n"
|
||||
description = """
|
||||
Customizable Rust parsers for the WebAssembly Text formats WAT and WAST
|
||||
"""
|
||||
homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast"
|
||||
documentation = "https://docs.rs/wast"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast"
|
||||
resolver = "2"
|
||||
|
||||
[[test]]
|
||||
name = "parse-fail"
|
||||
harness = false
|
||||
|
||||
[dependencies.leb128]
|
||||
version = "0.2"
|
||||
|
||||
|
@ -32,6 +36,7 @@ version = "2.4.1"
|
|||
|
||||
[dependencies.unicode-width]
|
||||
version = "0.1.9"
|
||||
|
||||
[dev-dependencies.anyhow]
|
||||
version = "1.0"
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::ast::{kw, Float32, Float64, Index, HeapType};
|
||||
use crate::core::HeapType;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Float32, Float64, Index};
|
||||
|
||||
/// 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
|
||||
/// 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).
|
|
@ -1,60 +0,0 @@
|
|||
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 source of this alias.
|
||||
pub source: AliasSource<'a>,
|
||||
/// The kind of item that's being aliased.
|
||||
pub kind: ast::ExportKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum AliasSource<'a> {
|
||||
InstanceExport {
|
||||
instance: ast::ItemRef<'a, kw::instance>,
|
||||
export: &'a str,
|
||||
},
|
||||
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>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Alias<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::alias>()?.0;
|
||||
let source = if parser.parse::<Option<kw::outer>>()?.is_some() {
|
||||
AliasSource::Outer {
|
||||
module: parser.parse()?,
|
||||
index: parser.parse()?,
|
||||
}
|
||||
} else {
|
||||
AliasSource::InstanceExport {
|
||||
instance: parser.parse::<ast::IndexOrRef<_>>()?.0,
|
||||
export: parser.parse()?,
|
||||
}
|
||||
};
|
||||
let (kind, id, name) = parser.parens(|p| Ok((p.parse()?, p.parse()?, p.parse()?)))?;
|
||||
|
||||
Ok(Alias {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
kind,
|
||||
source,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
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>>,
|
||||
/// 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 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 name = 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.parens(|p| p.parse())?);
|
||||
}
|
||||
Ok(InstanceKind::Inline { module, args })
|
||||
})?
|
||||
};
|
||||
|
||||
Ok(Instance {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
exports,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for InstanceArg<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::import>()?;
|
||||
Ok(InstanceArg {
|
||||
name: parser.parse()?,
|
||||
index: parser.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,430 +0,0 @@
|
|||
/// 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 tag;
|
||||
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_exception);
|
||||
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!(dataref);
|
||||
custom_keyword!(declare);
|
||||
custom_keyword!(delegate);
|
||||
custom_keyword!(r#do = "do");
|
||||
custom_keyword!(elem);
|
||||
custom_keyword!(end);
|
||||
custom_keyword!(tag);
|
||||
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!(v128);
|
||||
}
|
||||
|
||||
/// Common annotations used to parse WebAssembly text files.
|
||||
pub mod annotation {
|
||||
annotation!(custom);
|
||||
annotation!(name);
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
use crate::core;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, Index, NameAnnotation, Span};
|
||||
|
||||
/// An `alias` statement used to juggle indices with nested components.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Alias<'a> {
|
||||
/// Where this `alias` was defined.
|
||||
pub span: Span,
|
||||
/// An identifier that this alias is resolved with (optionally) for name
|
||||
/// resolution.
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this alias stored in the custom `name` section.
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// The target of this alias.
|
||||
pub target: AliasTarget<'a>,
|
||||
/// The kind of item that's being aliased.
|
||||
pub kind: AliasKind,
|
||||
}
|
||||
|
||||
/// aliaskind ::= (module <id>?)
|
||||
/// | (component <id>?)
|
||||
/// | (instance <id>?)
|
||||
/// | (func <id>?)
|
||||
/// | (value <id>?)
|
||||
/// | (type <id>?)
|
||||
/// | (table <id>?)
|
||||
/// | (memory <id>?)
|
||||
/// | (global <id>?)
|
||||
/// | ... other Post-MVP Core definition kinds
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum AliasKind {
|
||||
Module,
|
||||
Component,
|
||||
Instance,
|
||||
Value,
|
||||
ExportKind(core::ExportKind),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for AliasKind {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<core::ExportKind>() {
|
||||
let kind = parser.parse::<core::ExportKind>()?;
|
||||
Ok(AliasKind::ExportKind(kind))
|
||||
} else if l.peek::<kw::module>() {
|
||||
parser.parse::<kw::module>()?;
|
||||
Ok(AliasKind::Module)
|
||||
} else if l.peek::<kw::component>() {
|
||||
parser.parse::<kw::component>()?;
|
||||
Ok(AliasKind::Component)
|
||||
} else if l.peek::<kw::instance>() {
|
||||
parser.parse::<kw::instance>()?;
|
||||
Ok(AliasKind::Instance)
|
||||
} else if l.peek::<kw::value>() {
|
||||
parser.parse::<kw::value>()?;
|
||||
Ok(AliasKind::Value)
|
||||
} else {
|
||||
Err(l.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// aliastarget ::= export <instanceidx> <name>
|
||||
/// | outer <outeridx> <idx>
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum AliasTarget<'a> {
|
||||
Export {
|
||||
instance: Index<'a>,
|
||||
export: &'a str,
|
||||
},
|
||||
Outer {
|
||||
/// The number of enclosing components to skip.
|
||||
outer: Index<'a>,
|
||||
/// An index into the target component's `aliaskind` index space.
|
||||
index: Index<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Alias<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::alias>()?.0;
|
||||
let target = if parser.parse::<Option<kw::outer>>()?.is_some() {
|
||||
AliasTarget::Outer {
|
||||
outer: parser.parse()?,
|
||||
index: parser.parse()?,
|
||||
}
|
||||
} else {
|
||||
parser.parse::<kw::export>()?;
|
||||
AliasTarget::Export {
|
||||
instance: parser.parse::<Index<'a>>()?,
|
||||
export: parser.parse()?,
|
||||
}
|
||||
};
|
||||
let (kind, id, name) = parser.parens(|p| Ok((p.parse()?, p.parse()?, p.parse()?)))?;
|
||||
|
||||
Ok(Alias {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
kind,
|
||||
target,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,632 @@
|
|||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::encode::Encode;
|
||||
use crate::token::{Id, NameAnnotation};
|
||||
|
||||
pub fn encode(component: &Component<'_>) -> Vec<u8> {
|
||||
match &component.kind {
|
||||
ComponentKind::Text(fields) => encode_fields(&component.id, &component.name, fields),
|
||||
ComponentKind::Binary(bytes) => bytes.iter().flat_map(|b| b.iter().cloned()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_fields(
|
||||
component_id: &Option<Id<'_>>,
|
||||
component_name: &Option<NameAnnotation<'_>>,
|
||||
fields: &[ComponentField<'_>],
|
||||
) -> Vec<u8> {
|
||||
let mut e = Encoder {
|
||||
wasm: Vec::new(),
|
||||
tmp: Vec::new(),
|
||||
last_section: None,
|
||||
last_section_contents: Vec::new(),
|
||||
last_section_count: 0,
|
||||
};
|
||||
e.wasm.extend(b"\0asm");
|
||||
e.wasm.extend(b"\x0a\0\x01\0");
|
||||
|
||||
for field in fields {
|
||||
match field {
|
||||
ComponentField::Type(i) => e.append(1, i),
|
||||
ComponentField::Import(i) => e.append(2, i),
|
||||
ComponentField::Func(i) => e.append(3, i),
|
||||
ComponentField::Module(i) => e.section(4, i),
|
||||
ComponentField::Component(i) => e.section(5, i),
|
||||
ComponentField::Instance(i) => e.append(6, i),
|
||||
ComponentField::Export(i) => e.append(7, i),
|
||||
ComponentField::Start(i) => e.section(8, i),
|
||||
ComponentField::Alias(i) => e.append(9, i),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(WebAssembly/component-model#14): once a name section is defined it
|
||||
// should be encoded here.
|
||||
drop((component_id, component_name));
|
||||
|
||||
e.flush();
|
||||
|
||||
return e.wasm;
|
||||
}
|
||||
|
||||
struct Encoder {
|
||||
wasm: Vec<u8>,
|
||||
tmp: Vec<u8>,
|
||||
|
||||
last_section: Option<u8>,
|
||||
last_section_contents: Vec<u8>,
|
||||
last_section_count: usize,
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
/// Appends an entire section represented by the `section` provided
|
||||
fn section(&mut self, id: u8, section: &dyn Encode) {
|
||||
self.flush();
|
||||
self.tmp.truncate(0);
|
||||
section.encode(&mut self.tmp);
|
||||
self.wasm.push(id);
|
||||
self.tmp.encode(&mut self.wasm);
|
||||
}
|
||||
|
||||
/// Appends an `item` specified within the section `id` specified.
|
||||
fn append(&mut self, id: u8, item: &dyn Encode) {
|
||||
// Flush if necessary to start a new section
|
||||
if self.last_section != Some(id) {
|
||||
self.flush();
|
||||
}
|
||||
// ... and afterwards start building up this section incrementally
|
||||
// in case the next item encoded is also part of this section.
|
||||
item.encode(&mut self.last_section_contents);
|
||||
self.last_section_count += 1;
|
||||
self.last_section = Some(id);
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
let id = match self.last_section.take() {
|
||||
Some(id) => id,
|
||||
None => return,
|
||||
};
|
||||
self.wasm.push(id);
|
||||
self.tmp.truncate(0);
|
||||
self.last_section_count.encode(&mut self.tmp);
|
||||
self.last_section_count = 0;
|
||||
self.tmp.extend(self.last_section_contents.drain(..));
|
||||
self.tmp.encode(&mut self.wasm);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for NestedComponent<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
let fields = match &self.kind {
|
||||
NestedComponentKind::Import { .. } => panic!("imports should be gone by now"),
|
||||
NestedComponentKind::Inline(fields) => fields,
|
||||
};
|
||||
e.extend(encode_fields(&self.id, &self.name, fields));
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Module<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.kind {
|
||||
ModuleKind::Import { .. } => panic!("imports should be gone by now"),
|
||||
ModuleKind::Inline { fields } => {
|
||||
e.extend(crate::core::binary::encode(&self.id, &self.name, fields));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Instance<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.kind {
|
||||
InstanceKind::Module { module, args } => {
|
||||
e.push(0x00);
|
||||
e.push(0x00);
|
||||
module.idx.encode(e);
|
||||
args.encode(e);
|
||||
}
|
||||
InstanceKind::Component { component, args } => {
|
||||
e.push(0x00);
|
||||
e.push(0x01);
|
||||
component.idx.encode(e);
|
||||
args.encode(e);
|
||||
}
|
||||
InstanceKind::BundleOfComponentExports { args } => {
|
||||
e.push(0x01);
|
||||
args.encode(e);
|
||||
}
|
||||
InstanceKind::BundleOfExports { args } => {
|
||||
e.push(0x02);
|
||||
args.encode(e);
|
||||
}
|
||||
InstanceKind::Import { .. } => unreachable!("should be removed during expansion"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for NamedModuleArg<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
e.push(0x02);
|
||||
self.arg.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ModuleArg<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
ModuleArg::Def(def) => {
|
||||
def.idx.encode(e);
|
||||
}
|
||||
ModuleArg::BundleOfExports(..) => {
|
||||
unreachable!("should be expanded already")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for NamedComponentArg<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
self.arg.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentArg<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
ComponentArg::Def(def) => {
|
||||
match def.kind {
|
||||
DefTypeKind::Module => e.push(0x00),
|
||||
DefTypeKind::Component => e.push(0x01),
|
||||
DefTypeKind::Instance => e.push(0x02),
|
||||
DefTypeKind::Func => e.push(0x03),
|
||||
DefTypeKind::Value => e.push(0x04),
|
||||
}
|
||||
def.idx.encode(e);
|
||||
}
|
||||
ComponentArg::BundleOfExports(..) => {
|
||||
unreachable!("should be expanded already")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Start<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.func.encode(e);
|
||||
self.args.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Alias<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self.target {
|
||||
AliasTarget::Export { instance, export } => {
|
||||
match self.kind {
|
||||
AliasKind::Module => {
|
||||
e.push(0x00);
|
||||
e.push(0x00);
|
||||
}
|
||||
AliasKind::Component => {
|
||||
e.push(0x00);
|
||||
e.push(0x01);
|
||||
}
|
||||
AliasKind::Instance => {
|
||||
e.push(0x00);
|
||||
e.push(0x02);
|
||||
}
|
||||
AliasKind::Value => {
|
||||
e.push(0x00);
|
||||
e.push(0x04);
|
||||
}
|
||||
AliasKind::ExportKind(export_kind) => {
|
||||
e.push(0x01);
|
||||
export_kind.encode(e);
|
||||
}
|
||||
}
|
||||
instance.encode(e);
|
||||
export.encode(e);
|
||||
}
|
||||
AliasTarget::Outer { outer, index } => {
|
||||
e.push(0x02);
|
||||
match self.kind {
|
||||
AliasKind::Module => e.push(0x00),
|
||||
AliasKind::Component => e.push(0x01),
|
||||
AliasKind::ExportKind(core::ExportKind::Type) => e.push(0x05),
|
||||
// FIXME(#590): this feels a bit odd but it's also weird to
|
||||
// make this an explicit error somewhere else. Should
|
||||
// revisit this when the encodings of aliases and such have
|
||||
// all settled down. Hopefully #590 and
|
||||
// WebAssembly/component-model#29 will help solve this.
|
||||
_ => e.push(0xff),
|
||||
}
|
||||
outer.encode(e);
|
||||
index.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for CanonLower<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x01);
|
||||
self.opts.encode(e);
|
||||
self.func.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for CanonLift<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x00);
|
||||
self.type_.encode(e);
|
||||
self.opts.encode(e);
|
||||
self.func.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for CanonOpt<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
CanonOpt::StringUtf8 => e.push(0x00),
|
||||
CanonOpt::StringUtf16 => e.push(0x01),
|
||||
CanonOpt::StringLatin1Utf16 => e.push(0x02),
|
||||
CanonOpt::Into(index) => {
|
||||
e.push(0x03);
|
||||
index.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ModuleType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x4f);
|
||||
self.defs.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ModuleTypeDef<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
ModuleTypeDef::Type(f) => {
|
||||
e.push(0x01);
|
||||
f.encode(e);
|
||||
}
|
||||
ModuleTypeDef::Import(i) => {
|
||||
e.push(0x02);
|
||||
i.encode(e);
|
||||
}
|
||||
ModuleTypeDef::Export(name, x) => {
|
||||
e.push(0x07);
|
||||
name.encode(e);
|
||||
x.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x4e);
|
||||
self.fields.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentTypeField<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
ComponentTypeField::Type(ty_) => {
|
||||
e.push(0x01);
|
||||
ty_.encode(e);
|
||||
}
|
||||
ComponentTypeField::Alias(alias) => {
|
||||
e.push(0x09);
|
||||
alias.encode(e);
|
||||
}
|
||||
ComponentTypeField::Export(export) => {
|
||||
e.push(0x07);
|
||||
export.encode(e);
|
||||
}
|
||||
ComponentTypeField::Import(import) => {
|
||||
e.push(0x02);
|
||||
import.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for InstanceType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x4d);
|
||||
self.fields.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for InstanceTypeField<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
InstanceTypeField::Type(ty_) => {
|
||||
e.push(0x01);
|
||||
ty_.encode(e);
|
||||
}
|
||||
InstanceTypeField::Alias(alias) => {
|
||||
e.push(0x09);
|
||||
alias.encode(e);
|
||||
}
|
||||
InstanceTypeField::Export(export) => {
|
||||
e.push(0x07);
|
||||
export.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentExportType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
self.item.encode(e);
|
||||
}
|
||||
}
|
||||
impl Encode for TypeField<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.def {
|
||||
ComponentTypeDef::DefType(d) => d.encode(e),
|
||||
ComponentTypeDef::InterType(i) => i.encode(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Primitive {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
Primitive::Unit => e.push(0x7f),
|
||||
Primitive::Bool => e.push(0x7e),
|
||||
Primitive::S8 => e.push(0x7d),
|
||||
Primitive::U8 => e.push(0x7c),
|
||||
Primitive::S16 => e.push(0x7b),
|
||||
Primitive::U16 => e.push(0x7a),
|
||||
Primitive::S32 => e.push(0x79),
|
||||
Primitive::U32 => e.push(0x78),
|
||||
Primitive::S64 => e.push(0x77),
|
||||
Primitive::U64 => e.push(0x76),
|
||||
Primitive::Float32 => e.push(0x75),
|
||||
Primitive::Float64 => e.push(0x74),
|
||||
Primitive::Char => e.push(0x73),
|
||||
Primitive::String => e.push(0x72),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for InterType<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
InterType::Primitive(p) => p.encode(e),
|
||||
InterType::Record(r) => r.encode(e),
|
||||
InterType::Variant(v) => v.encode(e),
|
||||
InterType::List(l) => l.encode(e),
|
||||
InterType::Tuple(t) => t.encode(e),
|
||||
InterType::Flags(f) => f.encode(e),
|
||||
InterType::Enum(n) => n.encode(e),
|
||||
InterType::Union(u) => u.encode(e),
|
||||
InterType::Option(o) => o.encode(e),
|
||||
InterType::Expected(x) => x.encode(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for InterTypeRef<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
InterTypeRef::Primitive(p) => p.encode(e),
|
||||
InterTypeRef::Ref(i) => i.encode(e),
|
||||
InterTypeRef::Inline(_) => unreachable!("should be expanded by now"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for DefType<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
DefType::Func(f) => f.encode(e),
|
||||
DefType::Module(m) => m.encode(e),
|
||||
DefType::Component(c) => c.encode(e),
|
||||
DefType::Instance(i) => i.encode(e),
|
||||
DefType::Value(v) => v.encode(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Record<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x71);
|
||||
self.fields.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Field<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
self.type_.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Variant<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x70);
|
||||
self.cases.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Case<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
self.type_.encode(e);
|
||||
if let Some(defaults_to) = self.defaults_to {
|
||||
e.push(0x01);
|
||||
defaults_to.encode(e);
|
||||
} else {
|
||||
e.push(0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for List<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x6f);
|
||||
self.element.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Tuple<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x6e);
|
||||
self.fields.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Flags<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x6d);
|
||||
self.flag_names.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Enum<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x6c);
|
||||
self.arms.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Union<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x6b);
|
||||
self.arms.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for OptionType<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x6a);
|
||||
self.element.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Expected<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x69);
|
||||
self.ok.encode(e);
|
||||
self.err.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for ComponentFunctionType<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x4c);
|
||||
self.params.encode(e);
|
||||
self.result.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for ComponentFunctionParam<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
if let Some(id) = self.name {
|
||||
e.push(0x01);
|
||||
id.encode(e);
|
||||
} else {
|
||||
e.push(0x00);
|
||||
}
|
||||
self.type_.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for ValueType<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(0x4b);
|
||||
self.value_type.encode(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Encode for ComponentTypeUse<'_, T> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
ComponentTypeUse::Inline(_) => unreachable!("should be expanded already"),
|
||||
ComponentTypeUse::Ref(index) => index.encode(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentExport<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
match &self.arg {
|
||||
ComponentArg::Def(item_ref) => {
|
||||
match item_ref.kind {
|
||||
DefTypeKind::Module => e.push(0x00),
|
||||
DefTypeKind::Component => e.push(0x01),
|
||||
DefTypeKind::Instance => e.push(0x02),
|
||||
DefTypeKind::Func => e.push(0x03),
|
||||
DefTypeKind::Value => e.push(0x04),
|
||||
}
|
||||
item_ref.idx.encode(e);
|
||||
}
|
||||
ComponentArg::BundleOfExports(..) => {
|
||||
unreachable!("should be expanded already")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentFunc<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.kind {
|
||||
ComponentFuncKind::Import { .. } => unreachable!("should be expanded by now"),
|
||||
ComponentFuncKind::Inline { body } => {
|
||||
body.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentFuncBody<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
ComponentFuncBody::CanonLift(lift) => lift.encode(e),
|
||||
ComponentFuncBody::CanonLower(lower) => lower.encode(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ComponentImport<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
self.item.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ItemSig<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.kind {
|
||||
ItemKind::Component(t) => t.encode(e),
|
||||
ItemKind::Module(t) => t.encode(e),
|
||||
ItemKind::Func(t) => t.encode(e),
|
||||
ItemKind::Instance(t) => t.encode(e),
|
||||
ItemKind::Value(t) => t.encode(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> Encode for ItemRef<'_, K> {
|
||||
fn encode(&self, dst: &mut Vec<u8>) {
|
||||
assert!(self.export_names.is_empty());
|
||||
self.idx.encode(dst);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for CoreExport<'_> {
|
||||
fn encode(&self, dst: &mut Vec<u8>) {
|
||||
self.name.encode(dst);
|
||||
self.index.kind.encode(dst);
|
||||
self.index.encode(dst);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// A parsed WebAssembly component module.
|
||||
#[derive(Debug)]
|
||||
pub struct Component<'a> {
|
||||
/// Where this `component` was defined
|
||||
pub span: Span,
|
||||
/// An optional identifier this component is known by
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional `@name` annotation for this component
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// What kind of component this was parsed as.
|
||||
pub kind: ComponentKind<'a>,
|
||||
}
|
||||
|
||||
/// The different kinds of ways to define a component.
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentKind<'a> {
|
||||
/// A component defined in the textual s-expression format.
|
||||
Text(Vec<ComponentField<'a>>),
|
||||
/// A component that had its raw binary bytes defined via the `binary`
|
||||
/// directive.
|
||||
Binary(Vec<&'a [u8]>),
|
||||
}
|
||||
|
||||
impl<'a> Component<'a> {
|
||||
/// Performs a name resolution pass on this [`Component`], 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 [`Component`] and replace all
|
||||
/// [`Index`](crate::token::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.
|
||||
///
|
||||
/// # 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<(), crate::Error> {
|
||||
match &mut self.kind {
|
||||
ComponentKind::Text(fields) => {
|
||||
crate::component::expand::expand(fields);
|
||||
}
|
||||
ComponentKind::Binary(_) => {}
|
||||
}
|
||||
crate::component::resolve::resolve(self)
|
||||
}
|
||||
|
||||
/// Encodes this [`Component`] to its binary form.
|
||||
///
|
||||
/// This function will take the textual representation in [`Component`] and
|
||||
/// perform all steps necessary to convert it to a binary WebAssembly
|
||||
/// component, suitable for writing to a `*.wasm` file. This function may
|
||||
/// internally modify the [`Component`], 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 component.
|
||||
///
|
||||
/// * Component fields may be shuffled around to preserve index ordering from
|
||||
/// expansions.
|
||||
///
|
||||
/// After all of this expansion has happened the component 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::component::binary::encode(self))
|
||||
}
|
||||
|
||||
pub(crate) fn validate(&self, parser: Parser<'_>) -> Result<()> {
|
||||
let mut starts = 0;
|
||||
if let ComponentKind::Text(fields) = &self.kind {
|
||||
for item in fields.iter() {
|
||||
if let ComponentField::Start(_) = item {
|
||||
starts += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if starts > 1 {
|
||||
return Err(parser.error("multiple start sections found"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Component<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let _r = parser.register_annotation("custom");
|
||||
|
||||
let span = parser.parse::<kw::component>()?.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()?);
|
||||
}
|
||||
ComponentKind::Binary(data)
|
||||
} else {
|
||||
ComponentKind::Text(ComponentField::parse_remaining(parser)?)
|
||||
};
|
||||
Ok(Component {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A listing of all possible fields that can make up a WebAssembly component.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentField<'a> {
|
||||
Type(TypeField<'a>),
|
||||
Import(ComponentImport<'a>),
|
||||
Func(ComponentFunc<'a>),
|
||||
Export(ComponentExport<'a>),
|
||||
Start(Start<'a>),
|
||||
Instance(Instance<'a>),
|
||||
Module(Module<'a>),
|
||||
Component(NestedComponent<'a>),
|
||||
Alias(Alias<'a>),
|
||||
}
|
||||
|
||||
impl<'a> ComponentField<'a> {
|
||||
fn parse_remaining(parser: Parser<'a>) -> Result<Vec<ComponentField>> {
|
||||
let mut fields = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
fields.push(parser.parens(ComponentField::parse)?);
|
||||
}
|
||||
Ok(fields)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentField<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<kw::r#type>() {
|
||||
return Ok(ComponentField::Type(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::import>() {
|
||||
return Ok(ComponentField::Import(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::func>() {
|
||||
return Ok(ComponentField::Func(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::export>() {
|
||||
return Ok(ComponentField::Export(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::start>() {
|
||||
return Ok(ComponentField::Start(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::instance>() {
|
||||
return Ok(ComponentField::Instance(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::module>() {
|
||||
return Ok(ComponentField::Module(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::component>() {
|
||||
return Ok(ComponentField::Component(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::alias>() {
|
||||
return Ok(ComponentField::Alias(parser.parse()?));
|
||||
}
|
||||
Err(parser.error("expected valid component field"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<TypeField<'a>> for ComponentField<'a> {
|
||||
fn from(field: TypeField<'a>) -> ComponentField<'a> {
|
||||
ComponentField::Type(field)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Alias<'a>> for ComponentField<'a> {
|
||||
fn from(field: Alias<'a>) -> ComponentField<'a> {
|
||||
ComponentField::Alias(field)
|
||||
}
|
||||
}
|
||||
|
||||
/// A function to call at instantiation time.
|
||||
#[derive(Debug)]
|
||||
pub struct Start<'a> {
|
||||
/// The function to call.
|
||||
pub func: ItemRef<'a, kw::func>,
|
||||
/// The arguments to pass to the function.
|
||||
pub args: Vec<ItemRef<'a, kw::value>>,
|
||||
/// Name of the result value.
|
||||
pub result: Option<Id<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Start<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::start>()?;
|
||||
let func = parser.parse::<IndexOrRef<_>>()?.0;
|
||||
let mut args = Vec::new();
|
||||
while !parser.is_empty() && !parser.peek2::<kw::result>() {
|
||||
args.push(parser.parse()?);
|
||||
}
|
||||
let result = if !parser.is_empty() {
|
||||
parser.parens(|parser| {
|
||||
parser.parse::<kw::result>()?;
|
||||
if !parser.is_empty() {
|
||||
parser.parens(|parser| {
|
||||
parser.parse::<kw::value>()?;
|
||||
let id = parser.parse()?;
|
||||
Ok(Some(id))
|
||||
})
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Start { func, args, result })
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed WebAssembly component module.
|
||||
#[derive(Debug)]
|
||||
pub struct NestedComponent<'a> {
|
||||
/// Where this `component` was defined
|
||||
pub span: Span,
|
||||
/// An optional identifier this component is known by
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional `@name` annotation for this component
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: core::InlineExport<'a>,
|
||||
/// What kind of component this was parsed as.
|
||||
pub kind: NestedComponentKind<'a>,
|
||||
}
|
||||
|
||||
/// The different kinds of ways to define a nested component.
|
||||
#[derive(Debug)]
|
||||
pub enum NestedComponentKind<'a> {
|
||||
/// This is actually an inline import of a component
|
||||
Import {
|
||||
/// The information about where this is being imported from.
|
||||
import: InlineImport<'a>,
|
||||
/// The type of component being imported.
|
||||
ty: ComponentTypeUse<'a, ComponentType<'a>>,
|
||||
},
|
||||
/// The component is defined inline as a local definition with its fields
|
||||
/// listed here.
|
||||
Inline(Vec<ComponentField<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for NestedComponent<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::component>()?.0;
|
||||
let id = parser.parse()?;
|
||||
let name = parser.parse()?;
|
||||
let exports = parser.parse()?;
|
||||
|
||||
let kind = if let Some(import) = parser.parse()? {
|
||||
NestedComponentKind::Import {
|
||||
import,
|
||||
ty: parser.parse()?,
|
||||
}
|
||||
} else {
|
||||
let mut fields = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
fields.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
NestedComponentKind::Inline(fields)
|
||||
};
|
||||
|
||||
Ok(NestedComponent {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
exports,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
//! The `deftype` production in the component-model AST, and its children.
|
||||
|
||||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::kw;
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
|
||||
/// Different kinds of elements that can be exported from a WebAssembly component,
|
||||
/// contained in a [`ComponentExport`].
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DefTypeKind {
|
||||
Func,
|
||||
Module,
|
||||
Component,
|
||||
Instance,
|
||||
Value,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for DefTypeKind {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<kw::func>() {
|
||||
parser.parse::<kw::func>()?;
|
||||
Ok(DefTypeKind::Func)
|
||||
} else if l.peek::<kw::module>() {
|
||||
parser.parse::<kw::module>()?;
|
||||
Ok(DefTypeKind::Module)
|
||||
} else if l.peek::<kw::component>() {
|
||||
parser.parse::<kw::component>()?;
|
||||
Ok(DefTypeKind::Component)
|
||||
} else if l.peek::<kw::instance>() {
|
||||
parser.parse::<kw::instance>()?;
|
||||
Ok(DefTypeKind::Instance)
|
||||
} else if l.peek::<kw::value>() {
|
||||
parser.parse::<kw::value>()?;
|
||||
Ok(DefTypeKind::Value)
|
||||
} else {
|
||||
Err(l.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Peek for DefTypeKind {
|
||||
fn peek(cursor: Cursor<'_>) -> bool {
|
||||
kw::func::peek(cursor)
|
||||
|| kw::module::peek(cursor)
|
||||
|| kw::component::peek(cursor)
|
||||
|| kw::instance::peek(cursor)
|
||||
|| kw::value::peek(cursor)
|
||||
}
|
||||
fn display() -> &'static str {
|
||||
"deftype kind"
|
||||
}
|
||||
}
|
||||
|
||||
/// deftype ::= <moduletype>
|
||||
/// | <componenttype>
|
||||
/// | <instancetype>
|
||||
/// | <functype>
|
||||
/// | <valuetype>
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DefType<'a> {
|
||||
Func(ComponentFunctionType<'a>),
|
||||
Module(ModuleType<'a>),
|
||||
Component(ComponentType<'a>),
|
||||
Instance(InstanceType<'a>),
|
||||
Value(ValueType<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for DefType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<kw::func>() {
|
||||
parser.parse::<kw::func>()?;
|
||||
Ok(DefType::Func(parser.parse()?))
|
||||
} else if parser.peek::<kw::module>() {
|
||||
parser.parse::<kw::module>()?;
|
||||
Ok(DefType::Module(parser.parse()?))
|
||||
} else if parser.peek::<kw::component>() {
|
||||
parser.parse::<kw::component>()?;
|
||||
Ok(DefType::Component(parser.parse()?))
|
||||
} else if parser.peek::<kw::instance>() {
|
||||
parser.parse::<kw::instance>()?;
|
||||
Ok(DefType::Instance(parser.parse()?))
|
||||
} else if parser.peek::<kw::value>() {
|
||||
parser.parse::<kw::value>()?;
|
||||
Ok(DefType::Value(parser.parse()?))
|
||||
} else {
|
||||
Err(parser.error("expected a deftype"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A component function type with parameters and results.
|
||||
///
|
||||
/// functype ::= (func <id>? (param <name>? <intertype>)* (result <intertype>)?)
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentFunctionType<'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<[ComponentFunctionParam<'a>]>,
|
||||
/// The result type of a function.
|
||||
pub result: InterTypeRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentFunctionType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let mut params = Vec::new();
|
||||
while parser.peek2::<kw::param>() {
|
||||
parser.parens(|p| {
|
||||
p.parse::<kw::param>()?;
|
||||
params.push(ComponentFunctionParam {
|
||||
name: p.parse()?,
|
||||
type_: p.parse()?,
|
||||
});
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
let result = if parser.peek2::<kw::result>() {
|
||||
// Parse a `(result ...)`.
|
||||
parser.parens(|parser| {
|
||||
parser.parse::<kw::result>()?;
|
||||
parser.parse()
|
||||
})?
|
||||
} else {
|
||||
// If the result is omitted, use `unit`.
|
||||
InterTypeRef::Primitive(Primitive::Unit)
|
||||
};
|
||||
Ok(Self {
|
||||
params: params.into(),
|
||||
result,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A parameter of a [`ComponentFunctionType`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ComponentFunctionParam<'a> {
|
||||
/// An optionally-specified name of this parameter
|
||||
pub name: Option<&'a str>,
|
||||
/// The type of the parameter.
|
||||
pub type_: InterTypeRef<'a>,
|
||||
}
|
||||
|
||||
/// A type for a nested module
|
||||
#[derive(Debug)]
|
||||
pub struct ModuleType<'a> {
|
||||
/// The fields of the module type.
|
||||
pub defs: Vec<ModuleTypeDef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ModuleType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
// See comments in `nested_module.rs` for why this is tested here.
|
||||
if parser.parens_depth() > 100 {
|
||||
return Err(parser.error("module type nesting too deep"));
|
||||
}
|
||||
|
||||
let mut defs = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
defs.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
Ok(ModuleType { defs })
|
||||
}
|
||||
}
|
||||
|
||||
/// The contents of a [`ModuleType`].
|
||||
#[derive(Debug)]
|
||||
pub enum ModuleTypeDef<'a> {
|
||||
/// A function type.
|
||||
Type(core::Type<'a>),
|
||||
/// An import.
|
||||
Import(core::Import<'a>),
|
||||
/// An export.
|
||||
Export(&'a str, core::ItemSig<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ModuleTypeDef<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<kw::r#type>() {
|
||||
Ok(ModuleTypeDef::Type(parser.parse()?))
|
||||
} else if l.peek::<kw::import>() {
|
||||
Ok(ModuleTypeDef::Import(parser.parse()?))
|
||||
} else if l.peek::<kw::export>() {
|
||||
parser.parse::<kw::export>()?;
|
||||
let name = parser.parse()?;
|
||||
let et = parser.parens(|parser| parser.parse())?;
|
||||
Ok(ModuleTypeDef::Export(name, et))
|
||||
} else {
|
||||
Err(parser.error("Expected a moduletype-def"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type for a nested component
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ComponentType<'a> {
|
||||
/// The fields of this `ComponentType`.
|
||||
pub fields: Vec<ComponentTypeField<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
// See comments in `nested_module.rs` for why this is tested here.
|
||||
if parser.parens_depth() > 100 {
|
||||
return Err(parser.error("component type nesting too deep"));
|
||||
}
|
||||
|
||||
let mut fields = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
parser.parens(|parser| {
|
||||
if parser.peek::<kw::import>() {
|
||||
fields.push(ComponentTypeField::Import(parser.parse()?));
|
||||
} else if parser.peek::<kw::export>() {
|
||||
fields.push(ComponentTypeField::Export(parser.parse()?));
|
||||
} else if parser.peek::<kw::r#type>() {
|
||||
fields.push(ComponentTypeField::Type(parser.parse()?));
|
||||
} else if parser.peek::<kw::alias>() {
|
||||
fields.push(ComponentTypeField::Alias(parser.parse()?));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
Ok(ComponentType { fields })
|
||||
}
|
||||
}
|
||||
|
||||
/// A field of a type for a nested component
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentTypeField<'a> {
|
||||
/// A public type for this component.
|
||||
Type(TypeField<'a>),
|
||||
|
||||
/// A public type relationships for this component.
|
||||
Alias(Alias<'a>),
|
||||
|
||||
/// An import expected for this component type.
|
||||
Import(ComponentImport<'a>),
|
||||
|
||||
/// An export this component type is expected to have.
|
||||
Export(ComponentExportType<'a>),
|
||||
}
|
||||
|
||||
impl<'a> From<TypeField<'a>> for ComponentTypeField<'a> {
|
||||
fn from(field: TypeField<'a>) -> ComponentTypeField<'a> {
|
||||
ComponentTypeField::Type(field)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Alias<'a>> for ComponentTypeField<'a> {
|
||||
fn from(field: Alias<'a>) -> ComponentTypeField<'a> {
|
||||
ComponentTypeField::Alias(field)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type for a nested instance
|
||||
#[derive(Debug)]
|
||||
pub struct InstanceType<'a> {
|
||||
/// The fields of this `InstanceType`.
|
||||
pub fields: Vec<InstanceTypeField<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for InstanceType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
// See comments in `nested_module.rs` for why this is tested here.
|
||||
if parser.parens_depth() > 100 {
|
||||
return Err(parser.error("instance type nesting too deep"));
|
||||
}
|
||||
|
||||
let mut fields = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
parser.parens(|parser| {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<kw::export>() {
|
||||
fields.push(InstanceTypeField::Export(parser.parse()?));
|
||||
} else if l.peek::<kw::r#type>() {
|
||||
fields.push(InstanceTypeField::Type(parser.parse()?));
|
||||
} else if l.peek::<kw::alias>() {
|
||||
fields.push(InstanceTypeField::Alias(parser.parse()?));
|
||||
} else {
|
||||
return Err(l.error());
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
Ok(InstanceType { fields })
|
||||
}
|
||||
}
|
||||
|
||||
/// A field of a type for a nested instance
|
||||
#[derive(Debug)]
|
||||
pub enum InstanceTypeField<'a> {
|
||||
/// A public type for this component.
|
||||
Type(TypeField<'a>),
|
||||
|
||||
/// A public type relationships for this component.
|
||||
Alias(Alias<'a>),
|
||||
|
||||
/// An export this component type is expected to have.
|
||||
Export(ComponentExportType<'a>),
|
||||
}
|
||||
|
||||
impl<'a> From<TypeField<'a>> for InstanceTypeField<'a> {
|
||||
fn from(field: TypeField<'a>) -> InstanceTypeField<'a> {
|
||||
InstanceTypeField::Type(field)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Alias<'a>> for InstanceTypeField<'a> {
|
||||
fn from(field: Alias<'a>) -> InstanceTypeField<'a> {
|
||||
InstanceTypeField::Alias(field)
|
||||
}
|
||||
}
|
||||
|
||||
/// A value type.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueType<'a> {
|
||||
/// The type of the value.
|
||||
pub value_type: InterTypeRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ValueType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
Ok(ValueType {
|
||||
value_type: parser.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,642 @@
|
|||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::gensym;
|
||||
use crate::kw;
|
||||
use crate::token::{Id, Index, Span};
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
|
||||
/// Performs an AST "expansion" pass over the component fields provided.
|
||||
///
|
||||
/// This expansion is intended to desugar the AST from various parsed constructs
|
||||
/// to bits and bobs amenable for name resolution as well as binary encoding.
|
||||
/// For example `(import "" (func))` is split into a type definition followed by
|
||||
/// the import referencing that type definition.
|
||||
///
|
||||
/// Most forms of AST expansion happen in this file and afterwards the AST will
|
||||
/// be handed to the name resolution pass which will convert `Index::Id` to
|
||||
/// `Index::Num` wherever it's found.
|
||||
pub fn expand(fields: &mut Vec<ComponentField<'_>>) {
|
||||
Expander::default().expand_component_fields(fields)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Expander<'a> {
|
||||
/// Fields, during processing, which should be prepended to the
|
||||
/// currently-being-processed field. This should always be empty after
|
||||
/// processing is complete.
|
||||
to_prepend: Vec<TypeField<'a>>,
|
||||
component_fields_to_prepend: Vec<ComponentField<'a>>,
|
||||
|
||||
/// Fields that are appended to the end of the module once everything has
|
||||
/// finished.
|
||||
component_fields_to_append: Vec<ComponentField<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Expander<'a> {
|
||||
fn expand_component_fields(&mut self, fields: &mut Vec<ComponentField<'a>>) {
|
||||
let mut cur = 0;
|
||||
while cur < fields.len() {
|
||||
self.expand_field(&mut fields[cur]);
|
||||
let amt = self.to_prepend.len() + self.component_fields_to_prepend.len();
|
||||
fields.splice(cur..cur, self.component_fields_to_prepend.drain(..));
|
||||
fields.splice(cur..cur, self.to_prepend.drain(..).map(|f| f.into()));
|
||||
cur += 1 + amt;
|
||||
}
|
||||
fields.extend(self.component_fields_to_append.drain(..));
|
||||
}
|
||||
|
||||
fn expand_fields<T>(&mut self, fields: &mut Vec<T>, expand: fn(&mut Self, &mut T))
|
||||
where
|
||||
T: From<TypeField<'a>>,
|
||||
{
|
||||
let mut cur = 0;
|
||||
while cur < fields.len() {
|
||||
expand(self, &mut fields[cur]);
|
||||
assert!(self.component_fields_to_prepend.is_empty());
|
||||
assert!(self.component_fields_to_append.is_empty());
|
||||
let amt = self.to_prepend.len();
|
||||
fields.splice(cur..cur, self.to_prepend.drain(..).map(T::from));
|
||||
cur += 1 + amt;
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_field(&mut self, item: &mut ComponentField<'a>) {
|
||||
match item {
|
||||
ComponentField::Type(t) => self.expand_type_field(t),
|
||||
ComponentField::Import(t) => {
|
||||
self.expand_item_sig(&mut t.item);
|
||||
}
|
||||
ComponentField::Component(c) => {
|
||||
for name in c.exports.names.drain(..) {
|
||||
self.component_fields_to_append.push(export(
|
||||
c.span,
|
||||
name,
|
||||
DefTypeKind::Component,
|
||||
&mut c.id,
|
||||
));
|
||||
}
|
||||
match &mut c.kind {
|
||||
NestedComponentKind::Inline(fields) => expand(fields),
|
||||
NestedComponentKind::Import { import, ty } => {
|
||||
let idx = self.expand_component_type_use(ty);
|
||||
*item = ComponentField::Import(ComponentImport {
|
||||
span: c.span,
|
||||
name: import.name,
|
||||
item: ItemSig {
|
||||
span: c.span,
|
||||
id: c.id,
|
||||
name: None,
|
||||
kind: ItemKind::Component(ComponentTypeUse::Ref(idx)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComponentField::Func(f) => {
|
||||
for name in f.exports.names.drain(..) {
|
||||
self.component_fields_to_append.push(export(
|
||||
f.span,
|
||||
name,
|
||||
DefTypeKind::Func,
|
||||
&mut f.id,
|
||||
));
|
||||
}
|
||||
match &mut f.kind {
|
||||
ComponentFuncKind::Import { import, ty } => {
|
||||
let idx = self.expand_component_type_use(ty);
|
||||
*item = ComponentField::Import(ComponentImport {
|
||||
span: f.span,
|
||||
name: import.name,
|
||||
item: ItemSig {
|
||||
span: f.span,
|
||||
id: f.id,
|
||||
name: None,
|
||||
kind: ItemKind::Func(ComponentTypeUse::Ref(idx)),
|
||||
},
|
||||
});
|
||||
}
|
||||
ComponentFuncKind::Inline { body } => match body {
|
||||
ComponentFuncBody::CanonLift(lift) => {
|
||||
self.expand_component_type_use(&mut lift.type_);
|
||||
}
|
||||
ComponentFuncBody::CanonLower(_) => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
ComponentField::Module(m) => {
|
||||
for name in m.exports.names.drain(..) {
|
||||
self.component_fields_to_append.push(export(
|
||||
m.span,
|
||||
name,
|
||||
DefTypeKind::Module,
|
||||
&mut m.id,
|
||||
));
|
||||
}
|
||||
match &mut m.kind {
|
||||
// inline modules are expanded later during resolution
|
||||
ModuleKind::Inline { .. } => {}
|
||||
ModuleKind::Import { import, ty } => {
|
||||
let idx = self.expand_component_type_use(ty);
|
||||
*item = ComponentField::Import(ComponentImport {
|
||||
span: m.span,
|
||||
name: import.name,
|
||||
item: ItemSig {
|
||||
span: m.span,
|
||||
id: m.id,
|
||||
name: None,
|
||||
kind: ItemKind::Module(ComponentTypeUse::Ref(idx)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComponentField::Instance(i) => {
|
||||
for name in i.exports.names.drain(..) {
|
||||
self.component_fields_to_append.push(export(
|
||||
i.span,
|
||||
name,
|
||||
DefTypeKind::Instance,
|
||||
&mut i.id,
|
||||
));
|
||||
}
|
||||
match &mut i.kind {
|
||||
InstanceKind::Import { import, ty } => {
|
||||
let idx = self.expand_component_type_use(ty);
|
||||
*item = ComponentField::Import(ComponentImport {
|
||||
span: i.span,
|
||||
name: import.name,
|
||||
item: ItemSig {
|
||||
span: i.span,
|
||||
id: i.id,
|
||||
name: None,
|
||||
kind: ItemKind::Instance(ComponentTypeUse::Ref(idx)),
|
||||
},
|
||||
});
|
||||
}
|
||||
InstanceKind::Module { args, .. } => {
|
||||
for arg in args {
|
||||
self.expand_module_arg(&mut arg.arg);
|
||||
}
|
||||
}
|
||||
InstanceKind::Component { args, .. } => {
|
||||
for arg in args {
|
||||
self.expand_component_arg(&mut arg.arg);
|
||||
}
|
||||
}
|
||||
InstanceKind::BundleOfExports { .. }
|
||||
| InstanceKind::BundleOfComponentExports { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
ComponentField::Export(e) => {
|
||||
self.expand_component_arg(&mut e.arg);
|
||||
}
|
||||
|
||||
ComponentField::Alias(_) | ComponentField::Start(_) => {}
|
||||
}
|
||||
|
||||
fn export<'a>(
|
||||
span: Span,
|
||||
name: &'a str,
|
||||
kind: DefTypeKind,
|
||||
id: &mut Option<Id<'a>>,
|
||||
) -> ComponentField<'a> {
|
||||
let id = gensym::fill(span, id);
|
||||
ComponentField::Export(ComponentExport {
|
||||
span,
|
||||
name,
|
||||
arg: ComponentArg::Def(ItemRef {
|
||||
idx: Index::Id(id),
|
||||
kind,
|
||||
export_names: Vec::new(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_type_field(&mut self, field: &mut TypeField<'a>) {
|
||||
match &mut field.def {
|
||||
ComponentTypeDef::DefType(t) => self.expand_deftype(t),
|
||||
ComponentTypeDef::InterType(t) => self.expand_intertype(t),
|
||||
}
|
||||
let name = gensym::fill(field.span, &mut field.id);
|
||||
let name = Index::Id(name);
|
||||
match &field.def {
|
||||
ComponentTypeDef::DefType(DefType::Func(t)) => t.key().insert(self, name),
|
||||
ComponentTypeDef::DefType(DefType::Module(t)) => t.key().insert(self, name),
|
||||
ComponentTypeDef::DefType(DefType::Component(t)) => t.key().insert(self, name),
|
||||
ComponentTypeDef::DefType(DefType::Instance(t)) => t.key().insert(self, name),
|
||||
ComponentTypeDef::DefType(DefType::Value(t)) => t.key().insert(self, name),
|
||||
ComponentTypeDef::InterType(t) => t.key().insert(self, name),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_deftype(&mut self, ty: &mut DefType<'a>) {
|
||||
match ty {
|
||||
DefType::Func(t) => self.expand_func_ty(t),
|
||||
DefType::Module(m) => self.expand_module_ty(m),
|
||||
DefType::Component(c) => self.expand_component_ty(c),
|
||||
DefType::Instance(i) => self.expand_instance_ty(i),
|
||||
DefType::Value(v) => self.expand_value_ty(v),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_func_ty(&mut self, ty: &mut ComponentFunctionType<'a>) {
|
||||
for param in ty.params.iter_mut() {
|
||||
self.expand_intertype_ref(&mut param.type_);
|
||||
}
|
||||
self.expand_intertype_ref(&mut ty.result);
|
||||
}
|
||||
|
||||
fn expand_module_ty(&mut self, ty: &mut ModuleType<'a>) {
|
||||
use crate::core::resolve::types::{FuncKey, TypeKey, TypeReference};
|
||||
|
||||
// Note that this is a custom implementation from everything else in
|
||||
// this file since this is using core wasm types instead of component
|
||||
// types, so a small part of the core wasm expansion process is
|
||||
// inlined here to handle the `TypeUse` from core wasm.
|
||||
|
||||
let mut func_type_to_idx = HashMap::new();
|
||||
let mut to_prepend = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < ty.defs.len() {
|
||||
match &mut ty.defs[i] {
|
||||
ModuleTypeDef::Type(ty) => match &ty.def {
|
||||
core::TypeDef::Func(f) => {
|
||||
let id = gensym::fill(ty.span, &mut ty.id);
|
||||
func_type_to_idx.insert(f.key(), Index::Id(id));
|
||||
}
|
||||
core::TypeDef::Struct(_) => {}
|
||||
core::TypeDef::Array(_) => {}
|
||||
},
|
||||
ModuleTypeDef::Import(ty) => {
|
||||
expand_sig(&mut ty.item, &mut to_prepend, &mut func_type_to_idx);
|
||||
}
|
||||
ModuleTypeDef::Export(_, item) => {
|
||||
expand_sig(item, &mut to_prepend, &mut func_type_to_idx);
|
||||
}
|
||||
}
|
||||
ty.defs.splice(i..i, to_prepend.drain(..));
|
||||
i += 1;
|
||||
}
|
||||
|
||||
fn expand_sig<'a>(
|
||||
item: &mut core::ItemSig<'a>,
|
||||
to_prepend: &mut Vec<ModuleTypeDef<'a>>,
|
||||
func_type_to_idx: &mut HashMap<FuncKey<'a>, Index<'a>>,
|
||||
) {
|
||||
match &mut item.kind {
|
||||
core::ItemKind::Func(t) | core::ItemKind::Tag(core::TagType::Exception(t)) => {
|
||||
// If the index is already filled in then this is skipped
|
||||
if t.index.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise the inline type information is used to
|
||||
// generate a type into this module if necessary. If the
|
||||
// function type already exists we reuse the same key,
|
||||
// otherwise a fresh type definition is created and we use
|
||||
// that one instead.
|
||||
let ty = t.inline.take().unwrap_or_default();
|
||||
let key = ty.key();
|
||||
if let Some(idx) = func_type_to_idx.get(&key) {
|
||||
t.index = Some(idx.clone());
|
||||
return;
|
||||
}
|
||||
let id = gensym::gen(item.span);
|
||||
to_prepend.push(ModuleTypeDef::Type(core::Type {
|
||||
span: item.span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
def: key.to_def(item.span),
|
||||
}));
|
||||
let idx = Index::Id(id);
|
||||
t.index = Some(idx);
|
||||
}
|
||||
core::ItemKind::Global(_)
|
||||
| core::ItemKind::Table(_)
|
||||
| core::ItemKind::Memory(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_component_ty(&mut self, ty: &mut ComponentType<'a>) {
|
||||
Expander::default().expand_fields(&mut ty.fields, |e, field| match field {
|
||||
ComponentTypeField::Type(t) => e.expand_type_field(t),
|
||||
ComponentTypeField::Alias(_) => {}
|
||||
ComponentTypeField::Export(t) => e.expand_item_sig(&mut t.item),
|
||||
ComponentTypeField::Import(t) => e.expand_item_sig(&mut t.item),
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_instance_ty(&mut self, ty: &mut InstanceType<'a>) {
|
||||
Expander::default().expand_fields(&mut ty.fields, |e, field| match field {
|
||||
InstanceTypeField::Type(t) => e.expand_type_field(t),
|
||||
InstanceTypeField::Alias(_) => {}
|
||||
InstanceTypeField::Export(t) => e.expand_item_sig(&mut t.item),
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_item_sig(&mut self, sig: &mut ItemSig<'a>) {
|
||||
match &mut sig.kind {
|
||||
ItemKind::Component(t) => self.expand_component_type_use(t),
|
||||
ItemKind::Module(t) => self.expand_component_type_use(t),
|
||||
ItemKind::Instance(t) => self.expand_component_type_use(t),
|
||||
ItemKind::Value(t) => self.expand_component_type_use(t),
|
||||
ItemKind::Func(t) => self.expand_component_type_use(t),
|
||||
};
|
||||
}
|
||||
|
||||
fn expand_value_ty(&mut self, ty: &mut ValueType<'a>) {
|
||||
self.expand_intertype_ref(&mut ty.value_type);
|
||||
}
|
||||
|
||||
fn expand_intertype(&mut self, ty: &mut InterType<'a>) {
|
||||
match ty {
|
||||
InterType::Primitive(_) | InterType::Flags(_) | InterType::Enum(_) => {}
|
||||
InterType::Record(r) => {
|
||||
for field in r.fields.iter_mut() {
|
||||
self.expand_intertype_ref(&mut field.type_);
|
||||
}
|
||||
}
|
||||
InterType::Variant(v) => {
|
||||
for case in v.cases.iter_mut() {
|
||||
self.expand_intertype_ref(&mut case.type_);
|
||||
}
|
||||
}
|
||||
InterType::List(t) => {
|
||||
self.expand_intertype_ref(&mut t.element);
|
||||
}
|
||||
InterType::Tuple(t) => {
|
||||
for field in t.fields.iter_mut() {
|
||||
self.expand_intertype_ref(field);
|
||||
}
|
||||
}
|
||||
InterType::Union(u) => {
|
||||
for arm in u.arms.iter_mut() {
|
||||
self.expand_intertype_ref(arm);
|
||||
}
|
||||
}
|
||||
InterType::Option(t) => {
|
||||
self.expand_intertype_ref(&mut t.element);
|
||||
}
|
||||
InterType::Expected(e) => {
|
||||
self.expand_intertype_ref(&mut e.ok);
|
||||
self.expand_intertype_ref(&mut e.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_intertype_ref(&mut self, ty: &mut InterTypeRef<'a>) {
|
||||
let inline = match ty {
|
||||
InterTypeRef::Primitive(_) | InterTypeRef::Ref(_) => return,
|
||||
InterTypeRef::Inline(inline) => {
|
||||
mem::replace(inline, InterType::Primitive(Primitive::Unit))
|
||||
}
|
||||
};
|
||||
// If this inline type has already been defined within this context
|
||||
// then reuse the previously defined type to avoid injecting too many
|
||||
// types into the type index space.
|
||||
if let Some(idx) = inline.key().lookup(self) {
|
||||
*ty = InterTypeRef::Ref(idx);
|
||||
return;
|
||||
}
|
||||
// And if this type isn't already defined we append it to the index
|
||||
// space with a fresh and unique name.
|
||||
let span = Span::from_offset(0); // FIXME(#613): don't manufacture
|
||||
let id = gensym::gen(span);
|
||||
self.to_prepend.push(TypeField {
|
||||
span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
def: inline.into_def(),
|
||||
});
|
||||
let idx = Index::Id(id);
|
||||
*ty = InterTypeRef::Ref(idx);
|
||||
}
|
||||
|
||||
fn expand_component_type_use<T>(
|
||||
&mut self,
|
||||
item: &mut ComponentTypeUse<'a, T>,
|
||||
) -> ItemRef<'a, kw::r#type>
|
||||
where
|
||||
T: TypeReference<'a>,
|
||||
{
|
||||
let span = Span::from_offset(0); // FIXME(#613): don't manufacture
|
||||
let dummy = ComponentTypeUse::Ref(ItemRef {
|
||||
idx: Index::Num(0, span),
|
||||
kind: kw::r#type(span),
|
||||
export_names: Vec::new(),
|
||||
});
|
||||
let mut inline = match mem::replace(item, dummy) {
|
||||
// If this type-use was already a reference to an existing type
|
||||
// then we put it back the way it was and return the corresponding
|
||||
// index.
|
||||
ComponentTypeUse::Ref(idx) => {
|
||||
*item = ComponentTypeUse::Ref(idx.clone());
|
||||
return idx;
|
||||
}
|
||||
|
||||
// ... otherwise with an inline type definition we go into
|
||||
// processing below.
|
||||
ComponentTypeUse::Inline(inline) => inline,
|
||||
};
|
||||
inline.expand(self);
|
||||
|
||||
// If this inline type has already been defined within this context
|
||||
// then reuse the previously defined type to avoid injecting too many
|
||||
// types into the type index space.
|
||||
if let Some(idx) = inline.key().lookup(self) {
|
||||
let ret = ItemRef {
|
||||
idx,
|
||||
kind: kw::r#type(span),
|
||||
export_names: Vec::new(),
|
||||
};
|
||||
*item = ComponentTypeUse::Ref(ret.clone());
|
||||
return ret;
|
||||
}
|
||||
|
||||
// And if this type isn't already defined we append it to the index
|
||||
// space with a fresh and unique name.
|
||||
let id = gensym::gen(span);
|
||||
self.to_prepend.push(TypeField {
|
||||
span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
def: inline.into_def(),
|
||||
});
|
||||
let idx = Index::Id(id);
|
||||
let ret = ItemRef {
|
||||
idx,
|
||||
kind: kw::r#type(span),
|
||||
export_names: Vec::new(),
|
||||
};
|
||||
*item = ComponentTypeUse::Ref(ret.clone());
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn expand_module_arg(&mut self, arg: &mut ModuleArg<'a>) {
|
||||
let (span, args) = match arg {
|
||||
ModuleArg::Def(_) => return,
|
||||
ModuleArg::BundleOfExports(span, exports) => (*span, mem::take(exports)),
|
||||
};
|
||||
let id = gensym::gen(span);
|
||||
self.component_fields_to_prepend
|
||||
.push(ComponentField::Instance(Instance {
|
||||
span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
exports: Default::default(),
|
||||
kind: InstanceKind::BundleOfExports { args },
|
||||
}));
|
||||
*arg = ModuleArg::Def(ItemRef {
|
||||
idx: Index::Id(id),
|
||||
kind: kw::instance(span),
|
||||
export_names: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
fn expand_component_arg(&mut self, arg: &mut ComponentArg<'a>) {
|
||||
let (span, args) = match arg {
|
||||
ComponentArg::Def(_) => return,
|
||||
ComponentArg::BundleOfExports(span, exports) => (*span, mem::take(exports)),
|
||||
};
|
||||
let id = gensym::gen(span);
|
||||
self.component_fields_to_prepend
|
||||
.push(ComponentField::Instance(Instance {
|
||||
span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
exports: Default::default(),
|
||||
kind: InstanceKind::BundleOfComponentExports { args },
|
||||
}));
|
||||
*arg = ComponentArg::Def(ItemRef {
|
||||
idx: Index::Id(id),
|
||||
kind: DefTypeKind::Instance,
|
||||
export_names: Vec::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trait TypeReference<'a> {
|
||||
type Key: TypeKey<'a>;
|
||||
fn key(&self) -> Self::Key;
|
||||
fn expand(&mut self, cx: &mut Expander<'a>);
|
||||
fn into_def(self) -> ComponentTypeDef<'a>;
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for InterType<'a> {
|
||||
type Key = Todo; // FIXME(#598): should implement this
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
Todo
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
cx.expand_intertype(self)
|
||||
}
|
||||
|
||||
fn into_def(self) -> ComponentTypeDef<'a> {
|
||||
ComponentTypeDef::InterType(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for ComponentType<'a> {
|
||||
type Key = Todo; // FIXME(#598): should implement this
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
Todo
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
cx.expand_component_ty(self)
|
||||
}
|
||||
|
||||
fn into_def(self) -> ComponentTypeDef<'a> {
|
||||
ComponentTypeDef::DefType(DefType::Component(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for ModuleType<'a> {
|
||||
type Key = Todo; // FIXME(#598): should implement this
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
Todo
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
cx.expand_module_ty(self)
|
||||
}
|
||||
|
||||
fn into_def(self) -> ComponentTypeDef<'a> {
|
||||
ComponentTypeDef::DefType(DefType::Module(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for InstanceType<'a> {
|
||||
type Key = Todo; // FIXME(#598): should implement this
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
Todo
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
cx.expand_instance_ty(self)
|
||||
}
|
||||
|
||||
fn into_def(self) -> ComponentTypeDef<'a> {
|
||||
ComponentTypeDef::DefType(DefType::Instance(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for ComponentFunctionType<'a> {
|
||||
type Key = Todo; // FIXME(#598): should implement this
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
Todo
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
cx.expand_func_ty(self)
|
||||
}
|
||||
|
||||
fn into_def(self) -> ComponentTypeDef<'a> {
|
||||
ComponentTypeDef::DefType(DefType::Func(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for ValueType<'a> {
|
||||
type Key = Todo; // FIXME(#598): should implement this
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
Todo
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
cx.expand_value_ty(self)
|
||||
}
|
||||
|
||||
fn into_def(self) -> ComponentTypeDef<'a> {
|
||||
ComponentTypeDef::DefType(DefType::Value(self))
|
||||
}
|
||||
}
|
||||
|
||||
trait TypeKey<'a> {
|
||||
fn lookup(&self, cx: &Expander<'a>) -> Option<Index<'a>>;
|
||||
fn insert(&self, cx: &mut Expander<'a>, id: Index<'a>);
|
||||
}
|
||||
|
||||
struct Todo;
|
||||
|
||||
impl<'a> TypeKey<'a> for Todo {
|
||||
fn lookup(&self, _cx: &Expander<'a>) -> Option<Index<'a>> {
|
||||
None
|
||||
}
|
||||
fn insert(&self, cx: &mut Expander<'a>, id: Index<'a>) {
|
||||
drop((cx, id));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
use crate::component::ComponentArg;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::Span;
|
||||
|
||||
/// A entry in a WebAssembly component's export section.
|
||||
///
|
||||
/// export ::= (export <name> <componentarg>)
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentExport<'a> {
|
||||
/// Where this export was defined.
|
||||
pub span: Span,
|
||||
/// The name of this export from the component.
|
||||
pub name: &'a str,
|
||||
/// What's being exported from the component.
|
||||
pub arg: ComponentArg<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentExport<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::export>()?.0;
|
||||
let name = parser.parse()?;
|
||||
let arg = parser.parse()?;
|
||||
Ok(ComponentExport { span, name, arg })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, LParen, NameAnnotation, Span};
|
||||
|
||||
/// A WebAssembly function to be inserted into a module.
|
||||
///
|
||||
/// This is a member of both the function and code sections.
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentFunc<'a> {
|
||||
/// Where this `func` was defined.
|
||||
pub span: Span,
|
||||
/// An identifier that this function is resolved with (optionally) for name
|
||||
/// resolution.
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: core::InlineExport<'a>,
|
||||
/// What kind of function this is, be it an inline-defined or imported
|
||||
/// function.
|
||||
pub kind: ComponentFuncKind<'a>,
|
||||
}
|
||||
|
||||
/// Possible ways to define a function in the text format.
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentFuncKind<'a> {
|
||||
/// A function which is actually defined as an import, such as:
|
||||
///
|
||||
/// ```text
|
||||
/// (func (import "foo") (type 3))
|
||||
/// ```
|
||||
Import {
|
||||
/// The import name of this import
|
||||
import: InlineImport<'a>,
|
||||
/// The type that this function will have.
|
||||
ty: ComponentTypeUse<'a, ComponentFunctionType<'a>>,
|
||||
},
|
||||
|
||||
/// Almost all functions, those defined inline in a wasm module.
|
||||
Inline {
|
||||
/// The body of the function.
|
||||
body: ComponentFuncBody<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentFunc<'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 kind = if let Some(import) = parser.parse()? {
|
||||
ComponentFuncKind::Import {
|
||||
import,
|
||||
ty: parser.parse()?,
|
||||
}
|
||||
} else {
|
||||
ComponentFuncKind::Inline {
|
||||
body: parser.parens(|p| p.parse())?,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ComponentFunc {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
exports,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The body of a `ComponentFunc`.
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentFuncBody<'a> {
|
||||
/// A `canon.lift`.
|
||||
CanonLift(CanonLift<'a>),
|
||||
/// A `canon.lower`.
|
||||
CanonLower(CanonLower<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentFuncBody<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<kw::canon_lift>() {
|
||||
Ok(ComponentFuncBody::CanonLift(parser.parse()?))
|
||||
} else if parser.peek::<kw::canon_lower>() {
|
||||
Ok(ComponentFuncBody::CanonLower(parser.parse()?))
|
||||
} else {
|
||||
Err(parser.error("Expected canon.lift or canon.lower"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extra information associated with canon.lift instructions.
|
||||
#[derive(Debug)]
|
||||
pub struct CanonLift<'a> {
|
||||
/// The type exported to other components
|
||||
pub type_: ComponentTypeUse<'a, ComponentFunctionType<'a>>,
|
||||
/// Configuration options
|
||||
pub opts: Vec<CanonOpt<'a>>,
|
||||
/// The function to wrap
|
||||
pub func: ItemRef<'a, kw::func>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for CanonLift<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::canon_lift>()?;
|
||||
let type_ = if parser.peek2::<kw::func>() {
|
||||
ComponentTypeUse::Inline(parser.parens(|p| {
|
||||
p.parse::<kw::func>()?;
|
||||
p.parse()
|
||||
})?)
|
||||
} else {
|
||||
ComponentTypeUse::Ref(parser.parse()?)
|
||||
};
|
||||
let mut opts = Vec::new();
|
||||
while !parser.peek2::<kw::func>() {
|
||||
opts.push(parser.parse()?);
|
||||
}
|
||||
let func = parser.parse()?;
|
||||
Ok(CanonLift { type_, opts, func })
|
||||
}
|
||||
}
|
||||
|
||||
/// Extra information associated with canon.lower instructions.
|
||||
#[derive(Debug)]
|
||||
pub struct CanonLower<'a> {
|
||||
/// Configuration options
|
||||
pub opts: Vec<CanonOpt<'a>>,
|
||||
/// The function being wrapped
|
||||
pub func: ItemRef<'a, kw::func>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for CanonLower<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::canon_lower>()?;
|
||||
let mut opts = Vec::new();
|
||||
while !parser.is_empty() && (!parser.peek::<LParen>() || !parser.peek2::<kw::func>()) {
|
||||
opts.push(parser.parse()?);
|
||||
}
|
||||
let func = parser.parse()?;
|
||||
Ok(CanonLower { opts, func })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Canonical ABI options.
|
||||
pub enum CanonOpt<'a> {
|
||||
/// Encode strings as UTF-8.
|
||||
StringUtf8,
|
||||
/// Encode strings as UTF-16.
|
||||
StringUtf16,
|
||||
/// Encode strings as "compact UTF-16".
|
||||
StringLatin1Utf16,
|
||||
/// A target instance which supplies the memory that the canonical ABI
|
||||
/// should operate on as well as functions that the canonical ABI can call
|
||||
/// to allocate, reallocate and free linear memory
|
||||
Into(ItemRef<'a, kw::instance>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for CanonOpt<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<kw::string_utf8>() {
|
||||
parser.parse::<kw::string_utf8>()?;
|
||||
Ok(CanonOpt::StringUtf8)
|
||||
} else if l.peek::<kw::string_utf16>() {
|
||||
parser.parse::<kw::string_utf16>()?;
|
||||
Ok(CanonOpt::StringUtf16)
|
||||
} else if l.peek::<kw::string_latin1_utf16>() {
|
||||
parser.parse::<kw::string_latin1_utf16>()?;
|
||||
Ok(CanonOpt::StringLatin1Utf16)
|
||||
} else if l.peek::<LParen>() {
|
||||
parser.parens(|parser| {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<kw::into>() {
|
||||
parser.parse::<kw::into>()?;
|
||||
Ok(CanonOpt::Into(parser.parse::<IndexOrRef<'_, _>>()?.0))
|
||||
} else {
|
||||
Err(l.error())
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Err(l.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for kw::instance {
|
||||
fn default() -> kw::instance {
|
||||
kw::instance(Span::from_offset(0))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
use crate::component::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// An `import` statement and entry in a WebAssembly component.
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentImport<'a> {
|
||||
/// Where this `import` was defined
|
||||
pub span: Span,
|
||||
/// The name of the item to import.
|
||||
pub name: &'a str,
|
||||
/// The item that's being imported.
|
||||
pub item: ItemSig<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentImport<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::import>()?.0;
|
||||
let name = parser.parse()?;
|
||||
let item = parser.parens(|p| p.parse())?;
|
||||
Ok(ComponentImport { span, name, item })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ItemSig<'a> {
|
||||
/// Where this item is defined in the source.
|
||||
pub span: Span,
|
||||
/// An optional identifier used during name resolution to refer to this item
|
||||
/// from the rest of the component.
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name which, for functions, will be stored in the
|
||||
/// custom `name` section.
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// What kind of item this is.
|
||||
pub kind: ItemKind<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ItemKind<'a> {
|
||||
Component(ComponentTypeUse<'a, ComponentType<'a>>),
|
||||
Module(ComponentTypeUse<'a, ModuleType<'a>>),
|
||||
Instance(ComponentTypeUse<'a, InstanceType<'a>>),
|
||||
Value(ComponentTypeUse<'a, ValueType<'a>>),
|
||||
Func(ComponentTypeUse<'a, ComponentFunctionType<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ItemSig<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let mut l = parser.lookahead1();
|
||||
let (span, parse_kind): (_, fn(Parser<'a>) -> Result<ItemKind>) =
|
||||
if l.peek::<kw::component>() {
|
||||
let span = parser.parse::<kw::component>()?.0;
|
||||
(span, |parser| Ok(ItemKind::Component(parser.parse()?)))
|
||||
} else if l.peek::<kw::module>() {
|
||||
let span = parser.parse::<kw::module>()?.0;
|
||||
(span, |parser| Ok(ItemKind::Module(parser.parse()?)))
|
||||
} else if l.peek::<kw::instance>() {
|
||||
let span = parser.parse::<kw::instance>()?.0;
|
||||
(span, |parser| Ok(ItemKind::Instance(parser.parse()?)))
|
||||
} else if l.peek::<kw::func>() {
|
||||
let span = parser.parse::<kw::func>()?.0;
|
||||
(span, |parser| Ok(ItemKind::Func(parser.parse()?)))
|
||||
} else if l.peek::<kw::value>() {
|
||||
let span = parser.parse::<kw::value>()?.0;
|
||||
(span, |parser| Ok(ItemKind::Value(parser.parse()?)))
|
||||
} else {
|
||||
return Err(l.error());
|
||||
};
|
||||
Ok(ItemSig {
|
||||
span,
|
||||
id: parser.parse()?,
|
||||
name: parser.parse()?,
|
||||
kind: parse_kind(parser)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A listing of a inline `(import "foo")` statement.
|
||||
///
|
||||
/// This is the same as `core::InlineImport` except only one string import is
|
||||
/// required.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct InlineImport<'a> {
|
||||
pub name: &'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 { name: 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,
|
||||
};
|
||||
cursor.rparen().is_some()
|
||||
}
|
||||
|
||||
fn display() -> &'static str {
|
||||
"inline import"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, Index, LParen, NameAnnotation, Span};
|
||||
|
||||
/// 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: Span,
|
||||
/// An identifier that this instance is resolved with (optionally) for name
|
||||
/// resolution.
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: core::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> {
|
||||
/// The `(instance (import "x"))` sugar syntax
|
||||
Import {
|
||||
/// The name of the import
|
||||
import: InlineImport<'a>,
|
||||
/// The type of the instance being imported
|
||||
ty: ComponentTypeUse<'a, InstanceType<'a>>,
|
||||
},
|
||||
|
||||
/// Instantiate a core module.
|
||||
Module {
|
||||
/// Module that we're instantiating
|
||||
module: ItemRef<'a, kw::module>,
|
||||
/// Arguments used to instantiate the instance
|
||||
args: Vec<NamedModuleArg<'a>>,
|
||||
},
|
||||
|
||||
/// Instantiate a component.
|
||||
Component {
|
||||
/// Component that we're instantiating
|
||||
component: ItemRef<'a, kw::component>,
|
||||
/// Arguments used to instantiate the instance
|
||||
args: Vec<NamedComponentArg<'a>>,
|
||||
},
|
||||
|
||||
/// A bundle of module exports which isn't an instance, but can be used
|
||||
/// in places that need an instance.
|
||||
BundleOfExports {
|
||||
/// Arguments used to create the anonymous instance
|
||||
args: Vec<CoreExport<'a>>,
|
||||
},
|
||||
|
||||
/// A bundle of component exports which isn't an instance, but can be used
|
||||
/// in places that need an instance.
|
||||
BundleOfComponentExports {
|
||||
/// Arguments used to create the anonymous instance
|
||||
args: Vec<ComponentExport<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Arguments to the module `instantiate` instruction
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct NamedModuleArg<'a> {
|
||||
pub name: &'a str,
|
||||
pub arg: ModuleArg<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for NamedModuleArg<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::with>()?;
|
||||
Ok(NamedModuleArg {
|
||||
name: parser.parse()?,
|
||||
arg: parser.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Arguments to the component `instantiate` instruction
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct NamedComponentArg<'a> {
|
||||
pub name: &'a str,
|
||||
pub arg: ComponentArg<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for NamedComponentArg<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::with>()?;
|
||||
Ok(NamedComponentArg {
|
||||
name: parser.parse()?,
|
||||
arg: parser.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// ```text
|
||||
/// modulearg ::= (instance <instanceidx>)
|
||||
/// | (instance <core:export>*)
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub enum ModuleArg<'a> {
|
||||
/// Core modules can reference instances.
|
||||
Def(ItemRef<'a, kw::instance>),
|
||||
/// `instance`, but it isn't actually an instance; it's a tuple of exports
|
||||
/// which can be used in place of an instance.
|
||||
BundleOfExports(Span, Vec<CoreExport<'a>>),
|
||||
}
|
||||
|
||||
/// ```text
|
||||
/// componentarg ::= (module <moduleidx>)
|
||||
/// | (component <componentidx>)
|
||||
/// | (instance <instanceidx>)
|
||||
/// | (func <funcidx>)
|
||||
/// | (value <valueidx>)
|
||||
/// | (instance <export>*)
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentArg<'a> {
|
||||
/// A reference to an item of one of the deftype kinds.
|
||||
Def(ItemRef<'a, DefTypeKind>),
|
||||
/// `instance`, but it isn't actually an instance; it's a tuple of exports
|
||||
/// which can be used in place of an instance.
|
||||
BundleOfExports(Span, Vec<ComponentExport<'a>>),
|
||||
}
|
||||
|
||||
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 name = parser.parse()?;
|
||||
let exports = parser.parse()?;
|
||||
|
||||
let kind = if let Some(import) = parser.parse()? {
|
||||
InstanceKind::Import {
|
||||
import,
|
||||
ty: parser.parse()?,
|
||||
}
|
||||
} else if parser.peek::<LParen>() && parser.peek2::<kw::instantiate>() {
|
||||
parser.parens(|p| {
|
||||
p.parse::<kw::instantiate>()?;
|
||||
if p.peek2::<kw::module>() {
|
||||
let module = p.parse()?;
|
||||
let mut args = Vec::new();
|
||||
while !p.is_empty() {
|
||||
args.push(p.parens(|p| p.parse())?);
|
||||
}
|
||||
Ok(InstanceKind::Module { module, args })
|
||||
} else if p.peek2::<kw::component>() {
|
||||
let component = p.parse()?;
|
||||
let mut args = Vec::new();
|
||||
while !p.is_empty() {
|
||||
args.push(p.parens(|p| p.parse())?);
|
||||
}
|
||||
Ok(InstanceKind::Component { component, args })
|
||||
} else {
|
||||
return Err(parser.error("expected module or component"));
|
||||
}
|
||||
})?
|
||||
} else if parser.peek::<kw::core>() {
|
||||
parser.parse::<kw::core>()?;
|
||||
let mut args = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
args.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
InstanceKind::BundleOfExports { args }
|
||||
} else {
|
||||
let mut args = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
args.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
InstanceKind::BundleOfComponentExports { args }
|
||||
};
|
||||
|
||||
Ok(Instance {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
exports,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ModuleArg<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<ItemRef<'a, kw::instance>>() && parser.peek3::<Index>() {
|
||||
// `(instance <index>)`
|
||||
let def = parser.parse::<ItemRef<kw::instance>>()?;
|
||||
Ok(ModuleArg::Def(def))
|
||||
} else if parser.peek::<LParen>() && parser.peek2::<kw::instance>() {
|
||||
let (span, exports) = parser.parens(|p| {
|
||||
let span = p.parse::<kw::instance>()?.0;
|
||||
let mut exports = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
exports.push(parser.parens(|parser| parser.parse())?);
|
||||
}
|
||||
Ok((span, exports))
|
||||
})?;
|
||||
Ok(ModuleArg::BundleOfExports(span, exports))
|
||||
} else {
|
||||
Err(parser.error("expected an instance"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentArg<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<ItemRef<'a, DefTypeKind>>() && parser.peek3::<Index>() {
|
||||
// `(<deftypekind> <index>)`
|
||||
let def = parser.parse::<ItemRef<'a, DefTypeKind>>()?;
|
||||
Ok(ComponentArg::Def(def))
|
||||
} else if parser.peek::<LParen>() && parser.peek2::<kw::instance>() {
|
||||
let (span, exports) = parser.parens(|p| {
|
||||
let span = p.parse::<kw::instance>()?.0;
|
||||
let mut exports = Vec::new();
|
||||
while !p.is_empty() {
|
||||
exports.push(p.parens(|p| p.parse())?);
|
||||
}
|
||||
Ok((span, exports))
|
||||
})?;
|
||||
Ok(ComponentArg::BundleOfExports(span, exports))
|
||||
} else {
|
||||
Err(parser.error("expected def type, type, or instance"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A entry in a WebAssembly module's export section.
|
||||
#[derive(Debug)]
|
||||
pub struct CoreExport<'a> {
|
||||
/// Where this export was defined.
|
||||
pub span: Span,
|
||||
/// The name of this export from the module.
|
||||
pub name: &'a str,
|
||||
/// What's being exported from the module.
|
||||
pub index: ItemRef<'a, core::ExportKind>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for CoreExport<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
Ok(CoreExport {
|
||||
span: parser.parse::<kw::export>()?.0,
|
||||
name: parser.parse()?,
|
||||
index: parser.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,369 @@
|
|||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::*;
|
||||
|
||||
/// An interface-types type.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Primitive {
|
||||
Unit,
|
||||
Bool,
|
||||
S8,
|
||||
U8,
|
||||
S16,
|
||||
U16,
|
||||
S32,
|
||||
U32,
|
||||
S64,
|
||||
U64,
|
||||
Float32,
|
||||
Float64,
|
||||
Char,
|
||||
String,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Primitive {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<kw::unit>() {
|
||||
parser.parse::<kw::unit>()?;
|
||||
Ok(Primitive::Unit)
|
||||
} else if l.peek::<kw::bool_>() {
|
||||
parser.parse::<kw::bool_>()?;
|
||||
Ok(Primitive::Bool)
|
||||
} else if l.peek::<kw::s8>() {
|
||||
parser.parse::<kw::s8>()?;
|
||||
Ok(Primitive::S8)
|
||||
} else if l.peek::<kw::u8>() {
|
||||
parser.parse::<kw::u8>()?;
|
||||
Ok(Primitive::U8)
|
||||
} else if l.peek::<kw::s16>() {
|
||||
parser.parse::<kw::s16>()?;
|
||||
Ok(Primitive::S16)
|
||||
} else if l.peek::<kw::u16>() {
|
||||
parser.parse::<kw::u16>()?;
|
||||
Ok(Primitive::U16)
|
||||
} else if l.peek::<kw::s32>() {
|
||||
parser.parse::<kw::s32>()?;
|
||||
Ok(Primitive::S32)
|
||||
} else if l.peek::<kw::u32>() {
|
||||
parser.parse::<kw::u32>()?;
|
||||
Ok(Primitive::U32)
|
||||
} else if l.peek::<kw::s64>() {
|
||||
parser.parse::<kw::s64>()?;
|
||||
Ok(Primitive::S64)
|
||||
} else if l.peek::<kw::u64>() {
|
||||
parser.parse::<kw::u64>()?;
|
||||
Ok(Primitive::U64)
|
||||
} else if l.peek::<kw::float32>() {
|
||||
parser.parse::<kw::float32>()?;
|
||||
Ok(Primitive::Float32)
|
||||
} else if l.peek::<kw::float64>() {
|
||||
parser.parse::<kw::float64>()?;
|
||||
Ok(Primitive::Float64)
|
||||
} else if l.peek::<kw::char>() {
|
||||
parser.parse::<kw::char>()?;
|
||||
Ok(Primitive::Char)
|
||||
} else if l.peek::<kw::string>() {
|
||||
parser.parse::<kw::string>()?;
|
||||
Ok(Primitive::String)
|
||||
} else {
|
||||
Err(l.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types type.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InterType<'a> {
|
||||
Primitive(Primitive),
|
||||
Record(Record<'a>),
|
||||
Variant(Variant<'a>),
|
||||
List(List<'a>),
|
||||
Tuple(Tuple<'a>),
|
||||
Flags(Flags<'a>),
|
||||
Enum(Enum<'a>),
|
||||
Union(Union<'a>),
|
||||
Option(OptionType<'a>),
|
||||
Expected(Expected<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for InterType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<LParen>() {
|
||||
parser.parens(|parser| {
|
||||
if parser.peek::<kw::record>() {
|
||||
let record = parser.parse()?;
|
||||
Ok(InterType::Record(record))
|
||||
} else if parser.peek::<kw::variant>() {
|
||||
let variant = parser.parse()?;
|
||||
Ok(InterType::Variant(variant))
|
||||
} else if parser.peek::<kw::list>() {
|
||||
let list = parser.parse()?;
|
||||
Ok(InterType::List(list))
|
||||
} else if parser.peek::<kw::tuple>() {
|
||||
let tuple = parser.parse()?;
|
||||
Ok(InterType::Tuple(tuple))
|
||||
} else if parser.peek::<kw::flags>() {
|
||||
let flags = parser.parse()?;
|
||||
Ok(InterType::Flags(flags))
|
||||
} else if parser.peek::<kw::enum_>() {
|
||||
let enum_ = parser.parse()?;
|
||||
Ok(InterType::Enum(enum_))
|
||||
} else if parser.peek::<kw::union>() {
|
||||
let union = parser.parse()?;
|
||||
Ok(InterType::Union(union))
|
||||
} else if parser.peek::<kw::option>() {
|
||||
let optional = parser.parse()?;
|
||||
Ok(InterType::Option(optional))
|
||||
} else if parser.peek::<kw::expected>() {
|
||||
let expected = parser.parse()?;
|
||||
Ok(InterType::Expected(expected))
|
||||
} else {
|
||||
Err(parser.error("expected derived intertype"))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Ok(InterType::Primitive(parser.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types type.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InterTypeRef<'a> {
|
||||
Primitive(Primitive),
|
||||
Inline(InterType<'a>),
|
||||
Ref(Index<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for InterTypeRef<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<Index<'_>>() {
|
||||
Ok(InterTypeRef::Ref(parser.parse()?))
|
||||
} else if parser.peek::<LParen>() {
|
||||
Ok(InterTypeRef::Inline(parser.parse()?))
|
||||
} else {
|
||||
Ok(InterTypeRef::Primitive(parser.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types record, aka a struct.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Record<'a> {
|
||||
/// The fields of the struct.
|
||||
pub fields: Vec<Field<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Record<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::record>()?;
|
||||
let mut fields = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
fields.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
Ok(Record { fields })
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types record field.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Field<'a> {
|
||||
/// The name of the field.
|
||||
pub name: &'a str,
|
||||
/// The type of the field.
|
||||
pub type_: InterTypeRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Field<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::field>()?;
|
||||
Ok(Field {
|
||||
name: parser.parse()?,
|
||||
type_: parser.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types variant, aka a discriminated union with named arms.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Variant<'a> {
|
||||
/// The cases of the variant type.
|
||||
pub cases: Vec<Case<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Variant<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::variant>()?;
|
||||
let mut cases = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
cases.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
Ok(Variant { cases })
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types variant case.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Case<'a> {
|
||||
/// The name of the case.
|
||||
pub name: &'a str,
|
||||
/// Where this `component` was defined
|
||||
pub span: Span,
|
||||
/// The type of the case.
|
||||
pub type_: InterTypeRef<'a>,
|
||||
/// The optional defaults-to name.
|
||||
pub defaults_to: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Case<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::case>()?.0;
|
||||
let name = parser.parse()?;
|
||||
let type_ = parser.parse()?;
|
||||
let defaults_to = if !parser.is_empty() {
|
||||
Some(parser.parens(|parser| {
|
||||
parser.parse::<kw::defaults_to>()?;
|
||||
Ok(parser.parse()?)
|
||||
})?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Case {
|
||||
name,
|
||||
span,
|
||||
type_,
|
||||
defaults_to,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types list, aka a fixed-size array.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct List<'a> {
|
||||
/// The element type of the array.
|
||||
pub element: Box<InterTypeRef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for List<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::list>()?;
|
||||
let ty = parser.parse()?;
|
||||
Ok(List {
|
||||
element: Box::new(ty),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types tuple, aka a record with anonymous fields.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Tuple<'a> {
|
||||
/// The types of the fields of the tuple.
|
||||
pub fields: Vec<InterTypeRef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Tuple<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::tuple>()?;
|
||||
let mut fields = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
fields.push(parser.parse()?);
|
||||
}
|
||||
Ok(Tuple { fields })
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types flags, aka a fixed-sized bitfield with named fields.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Flags<'a> {
|
||||
/// The names of the individual flags.
|
||||
pub flag_names: Vec<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Flags<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::flags>()?;
|
||||
let mut flag_names = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
flag_names.push(parser.parse()?);
|
||||
}
|
||||
Ok(Flags { flag_names })
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types enum, aka a discriminated union with unit arms.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Enum<'a> {
|
||||
/// The arms of the enum.
|
||||
pub arms: Vec<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Enum<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::enum_>()?;
|
||||
let mut arms = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
arms.push(parser.parse()?);
|
||||
}
|
||||
Ok(Enum { arms })
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types union, aka a discriminated union with anonymous arms.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Union<'a> {
|
||||
/// The arms of the union.
|
||||
pub arms: Vec<InterTypeRef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Union<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::union>()?;
|
||||
let mut arms = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
arms.push(parser.parse()?);
|
||||
}
|
||||
Ok(Union { arms })
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types optional, aka an option.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OptionType<'a> {
|
||||
/// The type of the value, when a value is present.
|
||||
pub element: Box<InterTypeRef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for OptionType<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::option>()?;
|
||||
let ty = parser.parse()?;
|
||||
Ok(OptionType {
|
||||
element: Box::new(ty),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface-types expected, aka an result.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Expected<'a> {
|
||||
/// The type on success.
|
||||
pub ok: Box<InterTypeRef<'a>>,
|
||||
/// The type on failure.
|
||||
pub err: Box<InterTypeRef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Expected<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<kw::expected>()?;
|
||||
let ok = parser.parse()?;
|
||||
let err = parser.parse()?;
|
||||
Ok(Expected {
|
||||
ok: Box::new(ok),
|
||||
err: Box::new(err),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use crate::token::Index;
|
||||
|
||||
/// Parses `(func $foo)`
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ItemRef<'a, K> {
|
||||
pub kind: K,
|
||||
pub idx: Index<'a>,
|
||||
pub export_names: Vec<&'a str>,
|
||||
}
|
||||
|
||||
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>()?;
|
||||
let idx = parser.parse()?;
|
||||
let mut export_names = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
export_names.push(parser.parse()?);
|
||||
}
|
||||
Ok(ItemRef {
|
||||
kind,
|
||||
idx,
|
||||
export_names,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Peek> Peek for ItemRef<'a, K> {
|
||||
fn peek(cursor: Cursor<'_>) -> bool {
|
||||
match cursor.lparen() {
|
||||
// This is a little fancy because when parsing something like:
|
||||
//
|
||||
// (type (component (type $foo)))
|
||||
//
|
||||
// we need to disambiguate that from
|
||||
//
|
||||
// (type (component (type $foo (func))))
|
||||
//
|
||||
// where the first is a type reference and the second is an inline
|
||||
// component type defining a type internally. The peek here not only
|
||||
// peeks for `K` but also for the index and possibly trailing
|
||||
// strings.
|
||||
Some(remaining) if K::peek(remaining) => {
|
||||
let remaining = match remaining.keyword() {
|
||||
Some((_, remaining)) => remaining,
|
||||
None => return false,
|
||||
};
|
||||
match remaining
|
||||
.id()
|
||||
.map(|p| p.1)
|
||||
.or_else(|| remaining.integer().map(|p| p.1))
|
||||
{
|
||||
Some(remaining) => remaining.rparen().is_some() || remaining.string().is_some(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
_ => 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 {
|
||||
kind: K::default(),
|
||||
idx: parser.parse()?,
|
||||
export_names: 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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//! Types and support for parsing the component model text format.
|
||||
|
||||
mod alias;
|
||||
mod component;
|
||||
mod deftype;
|
||||
mod export;
|
||||
mod func;
|
||||
mod import;
|
||||
mod instance;
|
||||
mod intertype;
|
||||
mod item_ref;
|
||||
mod module;
|
||||
mod types;
|
||||
pub use self::alias::*;
|
||||
pub use self::component::*;
|
||||
pub use self::deftype::*;
|
||||
pub use self::export::*;
|
||||
pub use self::func::*;
|
||||
pub use self::import::*;
|
||||
pub use self::instance::*;
|
||||
pub use self::intertype::*;
|
||||
pub use self::item_ref::*;
|
||||
pub use self::module::*;
|
||||
pub use self::types::*;
|
||||
|
||||
mod binary;
|
||||
mod expand;
|
||||
mod resolve;
|
|
@ -1,42 +1,45 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// A nested WebAssembly nested module to be created as part of a module.
|
||||
/// A nested WebAssembly module to be created as part of a component.
|
||||
#[derive(Debug)]
|
||||
pub struct NestedModule<'a> {
|
||||
pub struct Module<'a> {
|
||||
/// Where this `nested module` was defined.
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An identifier that this nested module is resolved with (optionally) for name
|
||||
/// resolution.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this module stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: ast::InlineExport<'a>,
|
||||
pub exports: core::InlineExport<'a>,
|
||||
/// What kind of nested module this is, be it an inline-defined or imported one.
|
||||
pub kind: NestedModuleKind<'a>,
|
||||
pub kind: ModuleKind<'a>,
|
||||
}
|
||||
|
||||
/// Possible ways to define a nested module in the text format.
|
||||
#[derive(Debug)]
|
||||
pub enum NestedModuleKind<'a> {
|
||||
pub enum ModuleKind<'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>,
|
||||
import: InlineImport<'a>,
|
||||
/// The type that this nested module will have.
|
||||
ty: ast::TypeUse<'a, ast::ModuleType<'a>>,
|
||||
ty: ComponentTypeUse<'a, ModuleType<'a>>,
|
||||
},
|
||||
|
||||
/// Nested modules whose instantiation is defined inline.
|
||||
/// modules whose instantiation is defined inline.
|
||||
Inline {
|
||||
/// Fields in the nested module.
|
||||
fields: Vec<ast::ModuleField<'a>>,
|
||||
fields: Vec<core::ModuleField<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for NestedModule<'a> {
|
||||
impl<'a> Parse<'a> for Module<'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
|
||||
|
@ -57,7 +60,7 @@ impl<'a> Parse<'a> for NestedModule<'a> {
|
|||
let exports = parser.parse()?;
|
||||
|
||||
let kind = if let Some(import) = parser.parse()? {
|
||||
NestedModuleKind::Import {
|
||||
ModuleKind::Import {
|
||||
import,
|
||||
ty: parser.parse()?,
|
||||
}
|
||||
|
@ -66,10 +69,10 @@ impl<'a> Parse<'a> for NestedModule<'a> {
|
|||
while !parser.is_empty() {
|
||||
fields.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
NestedModuleKind::Inline { fields }
|
||||
ModuleKind::Inline { fields }
|
||||
};
|
||||
|
||||
Ok(NestedModule {
|
||||
Ok(Module {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
|
@ -78,38 +81,3 @@ impl<'a> Parse<'a> for NestedModule<'a> {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,726 @@
|
|||
use crate::component::*;
|
||||
use crate::core;
|
||||
use crate::kw;
|
||||
use crate::names::Namespace;
|
||||
use crate::token::{Id, Index};
|
||||
use crate::Error;
|
||||
|
||||
/// Resolve the fields of a component and everything nested within it, changing
|
||||
/// `Index::Id` to `Index::Num` and expanding alias syntax sugar.
|
||||
pub fn resolve(component: &mut Component<'_>) -> Result<(), Error> {
|
||||
let fields = match &mut component.kind {
|
||||
ComponentKind::Text(fields) => fields,
|
||||
ComponentKind::Binary(_) => return Ok(()),
|
||||
};
|
||||
let mut resolver = Resolver::default();
|
||||
resolver.fields(component.id, fields)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Resolver<'a> {
|
||||
stack: Vec<ComponentState<'a>>,
|
||||
|
||||
// When a name refers to a definition in an outer scope, we'll need to
|
||||
// insert an outer alias before it. This collects the aliases to be
|
||||
// inserted during resolution.
|
||||
aliases_to_insert: Vec<Alias<'a>>,
|
||||
}
|
||||
|
||||
/// Context structure used to perform name resolution.
|
||||
#[derive(Default)]
|
||||
struct ComponentState<'a> {
|
||||
id: Option<Id<'a>>,
|
||||
|
||||
// Namespaces within each component. Note that each namespace carries
|
||||
// with it information about the signature of the item in that namespace.
|
||||
// The signature is later used to synthesize the type of a component and
|
||||
// inject type annotations if necessary.
|
||||
funcs: Namespace<'a>,
|
||||
globals: Namespace<'a>,
|
||||
tables: Namespace<'a>,
|
||||
memories: Namespace<'a>,
|
||||
types: Namespace<'a>,
|
||||
tags: Namespace<'a>,
|
||||
instances: Namespace<'a>,
|
||||
modules: Namespace<'a>,
|
||||
components: Namespace<'a>,
|
||||
values: Namespace<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ComponentState<'a> {
|
||||
fn new(id: Option<Id<'a>>) -> ComponentState<'a> {
|
||||
ComponentState {
|
||||
id,
|
||||
..ComponentState::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
fn current(&mut self) -> &mut ComponentState<'a> {
|
||||
self.stack
|
||||
.last_mut()
|
||||
.expect("should have at least one component state")
|
||||
}
|
||||
|
||||
fn fields(
|
||||
&mut self,
|
||||
id: Option<Id<'a>>,
|
||||
fields: &mut Vec<ComponentField<'a>>,
|
||||
) -> Result<(), Error> {
|
||||
self.stack.push(ComponentState::new(id));
|
||||
self.resolve_prepending_aliases(fields, Resolver::field, ComponentState::register)?;
|
||||
self.stack.pop();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_prepending_aliases<T>(
|
||||
&mut self,
|
||||
fields: &mut Vec<T>,
|
||||
resolve: fn(&mut Self, &mut T) -> Result<(), Error>,
|
||||
register: fn(&mut ComponentState<'a>, &T) -> Result<(), Error>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: From<Alias<'a>>,
|
||||
{
|
||||
assert!(self.aliases_to_insert.is_empty());
|
||||
|
||||
// Iterate through the fields of the component. We use an index
|
||||
// instead of an iterator because we'll be inserting aliases
|
||||
// as we go.
|
||||
let mut i = 0;
|
||||
while i < fields.len() {
|
||||
// Resolve names within the field.
|
||||
resolve(self, &mut fields[i])?;
|
||||
|
||||
// Name resolution may have emitted some aliases. Insert them before
|
||||
// the current definition.
|
||||
let amt = self.aliases_to_insert.len();
|
||||
fields.splice(i..i, self.aliases_to_insert.drain(..).map(T::from));
|
||||
i += amt;
|
||||
|
||||
// Definitions can't refer to themselves or to definitions that appear
|
||||
// later in the format. Now that we're done resolving this field,
|
||||
// assign it an index for later defintions to refer to.
|
||||
register(self.current(), &fields[i])?;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn field(&mut self, field: &mut ComponentField<'a>) -> Result<(), Error> {
|
||||
match field {
|
||||
ComponentField::Import(i) => self.item_sig(&mut i.item),
|
||||
|
||||
ComponentField::Type(t) => self.type_field(t),
|
||||
|
||||
ComponentField::Func(f) => {
|
||||
let body = match &mut f.kind {
|
||||
ComponentFuncKind::Import { .. } => return Ok(()),
|
||||
ComponentFuncKind::Inline { body } => body,
|
||||
};
|
||||
let opts = match body {
|
||||
ComponentFuncBody::CanonLift(lift) => {
|
||||
self.type_use(&mut lift.type_)?;
|
||||
self.item_ref(&mut lift.func)?;
|
||||
&mut lift.opts
|
||||
}
|
||||
ComponentFuncBody::CanonLower(lower) => {
|
||||
self.item_ref(&mut lower.func)?;
|
||||
&mut lower.opts
|
||||
}
|
||||
};
|
||||
for opt in opts {
|
||||
match opt {
|
||||
CanonOpt::StringUtf8
|
||||
| CanonOpt::StringUtf16
|
||||
| CanonOpt::StringLatin1Utf16 => {}
|
||||
CanonOpt::Into(instance) => {
|
||||
self.item_ref(instance)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ComponentField::Instance(i) => match &mut i.kind {
|
||||
InstanceKind::Module { module, args } => {
|
||||
self.item_ref(module)?;
|
||||
for arg in args {
|
||||
match &mut arg.arg {
|
||||
ModuleArg::Def(def) => {
|
||||
self.item_ref(def)?;
|
||||
}
|
||||
ModuleArg::BundleOfExports(..) => {
|
||||
unreachable!("should be expanded already");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
InstanceKind::Component { component, args } => {
|
||||
self.item_ref(component)?;
|
||||
for arg in args {
|
||||
match &mut arg.arg {
|
||||
ComponentArg::Def(def) => {
|
||||
self.item_ref(def)?;
|
||||
}
|
||||
ComponentArg::BundleOfExports(..) => {
|
||||
unreachable!("should be expanded already")
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
InstanceKind::BundleOfExports { args } => {
|
||||
for arg in args {
|
||||
self.item_ref(&mut arg.index)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
InstanceKind::BundleOfComponentExports { args } => {
|
||||
for arg in args {
|
||||
self.arg(&mut arg.arg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
InstanceKind::Import { .. } => {
|
||||
unreachable!("should be removed by expansion")
|
||||
}
|
||||
},
|
||||
ComponentField::Module(m) => {
|
||||
match &mut m.kind {
|
||||
ModuleKind::Inline { fields } => {
|
||||
crate::core::resolve::resolve(fields)?;
|
||||
}
|
||||
|
||||
ModuleKind::Import { .. } => {
|
||||
unreachable!("should be expanded already")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
ComponentField::Component(c) => match &mut c.kind {
|
||||
NestedComponentKind::Import { .. } => {
|
||||
unreachable!("should be expanded already")
|
||||
}
|
||||
NestedComponentKind::Inline(fields) => self.fields(c.id, fields),
|
||||
},
|
||||
ComponentField::Alias(a) => self.alias(a),
|
||||
|
||||
ComponentField::Start(s) => {
|
||||
self.item_ref(&mut s.func)?;
|
||||
for arg in s.args.iter_mut() {
|
||||
self.item_ref(arg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ComponentField::Export(e) => {
|
||||
self.arg(&mut e.arg)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn item_sig(&mut self, sig: &mut ItemSig<'a>) -> Result<(), Error> {
|
||||
match &mut sig.kind {
|
||||
ItemKind::Component(t) => self.type_use(t),
|
||||
ItemKind::Module(t) => self.type_use(t),
|
||||
ItemKind::Instance(t) => self.type_use(t),
|
||||
ItemKind::Func(t) => self.type_use(t),
|
||||
ItemKind::Value(t) => self.type_use(t),
|
||||
}
|
||||
}
|
||||
|
||||
fn alias(&mut self, alias: &mut Alias<'a>) -> Result<(), Error> {
|
||||
match &mut alias.target {
|
||||
AliasTarget::Export {
|
||||
instance,
|
||||
export: _,
|
||||
} => self.resolve_ns(instance, Ns::Instance),
|
||||
AliasTarget::Outer { outer, index } => {
|
||||
// Short-circuit when both indices are already resolved as this
|
||||
// helps to write tests for invalid modules where wasmparser should
|
||||
// be the one returning the error.
|
||||
if let Index::Num(..) = outer {
|
||||
if let Index::Num(..) = index {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve `outer`, and compute the depth at which to look up
|
||||
// `index`.
|
||||
let depth = match outer {
|
||||
Index::Id(id) => {
|
||||
let mut depth = 0;
|
||||
for resolver in self.stack.iter_mut().rev() {
|
||||
if resolver.id == Some(*id) {
|
||||
break;
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
if depth as usize == self.stack.len() {
|
||||
return Err(Error::new(
|
||||
alias.span,
|
||||
format!("outer component `{}` not found", id.name()),
|
||||
));
|
||||
}
|
||||
depth
|
||||
}
|
||||
Index::Num(n, _span) => *n,
|
||||
};
|
||||
*outer = Index::Num(depth, alias.span);
|
||||
if depth as usize >= self.stack.len() {
|
||||
return Err(Error::new(
|
||||
alias.span,
|
||||
format!("component depth of `{}` is too large", depth),
|
||||
));
|
||||
}
|
||||
|
||||
// Resolve `index` within the computed scope depth.
|
||||
let ns = match alias.kind {
|
||||
AliasKind::Module => Ns::Module,
|
||||
AliasKind::Component => Ns::Component,
|
||||
AliasKind::Instance => Ns::Instance,
|
||||
AliasKind::Value => Ns::Value,
|
||||
AliasKind::ExportKind(kind) => kind.into(),
|
||||
};
|
||||
let computed = self.stack.len() - 1 - depth as usize;
|
||||
self.stack[computed].resolve(ns, index)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn arg(&mut self, arg: &mut ComponentArg<'a>) -> Result<(), Error> {
|
||||
match arg {
|
||||
ComponentArg::Def(item_ref) => {
|
||||
self.item_ref(item_ref)?;
|
||||
}
|
||||
ComponentArg::BundleOfExports(..) => unreachable!("should be expanded already"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn type_use<T>(&mut self, ty: &mut ComponentTypeUse<'a, T>) -> Result<(), Error> {
|
||||
let item = match ty {
|
||||
ComponentTypeUse::Ref(r) => r,
|
||||
ComponentTypeUse::Inline(_) => {
|
||||
unreachable!("inline type-use should be expanded by now")
|
||||
}
|
||||
};
|
||||
self.item_ref(item)
|
||||
}
|
||||
|
||||
fn intertype(&mut self, ty: &mut InterType<'a>) -> Result<(), Error> {
|
||||
match ty {
|
||||
InterType::Primitive(_) => {}
|
||||
InterType::Flags(_) => {}
|
||||
InterType::Enum(_) => {}
|
||||
InterType::Record(r) => {
|
||||
for field in r.fields.iter_mut() {
|
||||
self.intertype_ref(&mut field.type_)?;
|
||||
}
|
||||
}
|
||||
InterType::Variant(v) => {
|
||||
for case in v.cases.iter_mut() {
|
||||
self.intertype_ref(&mut case.type_)?;
|
||||
}
|
||||
}
|
||||
InterType::List(l) => {
|
||||
self.intertype_ref(&mut *l.element)?;
|
||||
}
|
||||
InterType::Tuple(t) => {
|
||||
for field in t.fields.iter_mut() {
|
||||
self.intertype_ref(field)?;
|
||||
}
|
||||
}
|
||||
InterType::Union(t) => {
|
||||
for arm in t.arms.iter_mut() {
|
||||
self.intertype_ref(arm)?;
|
||||
}
|
||||
}
|
||||
InterType::Option(o) => {
|
||||
self.intertype_ref(&mut *o.element)?;
|
||||
}
|
||||
InterType::Expected(r) => {
|
||||
self.intertype_ref(&mut *r.ok)?;
|
||||
self.intertype_ref(&mut *r.err)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn intertype_ref(&mut self, ty: &mut InterTypeRef<'a>) -> Result<(), Error> {
|
||||
match ty {
|
||||
InterTypeRef::Primitive(_) => Ok(()),
|
||||
InterTypeRef::Ref(idx) => self.resolve_ns(idx, Ns::Type),
|
||||
InterTypeRef::Inline(_) => unreachable!("should be expanded by now"),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_field(&mut self, field: &mut TypeField<'a>) -> Result<(), Error> {
|
||||
match &mut field.def {
|
||||
ComponentTypeDef::DefType(DefType::Func(f)) => {
|
||||
for param in f.params.iter_mut() {
|
||||
self.intertype_ref(&mut param.type_)?;
|
||||
}
|
||||
self.intertype_ref(&mut f.result)?;
|
||||
}
|
||||
ComponentTypeDef::DefType(DefType::Module(m)) => {
|
||||
self.stack.push(ComponentState::new(field.id));
|
||||
self.moduletype(m)?;
|
||||
self.stack.pop();
|
||||
}
|
||||
ComponentTypeDef::DefType(DefType::Component(c)) => {
|
||||
self.stack.push(ComponentState::new(field.id));
|
||||
self.nested_component_type(c)?;
|
||||
self.stack.pop();
|
||||
}
|
||||
ComponentTypeDef::DefType(DefType::Instance(i)) => {
|
||||
self.stack.push(ComponentState::new(field.id));
|
||||
self.instance_type(i)?;
|
||||
self.stack.pop();
|
||||
}
|
||||
ComponentTypeDef::DefType(DefType::Value(v)) => {
|
||||
self.intertype_ref(&mut v.value_type)?
|
||||
}
|
||||
ComponentTypeDef::InterType(i) => self.intertype(i)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn nested_component_type(&mut self, c: &mut ComponentType<'a>) -> Result<(), Error> {
|
||||
self.resolve_prepending_aliases(
|
||||
&mut c.fields,
|
||||
|resolver, field| match field {
|
||||
ComponentTypeField::Alias(alias) => resolver.alias(alias),
|
||||
ComponentTypeField::Type(ty) => resolver.type_field(ty),
|
||||
ComponentTypeField::Import(import) => resolver.item_sig(&mut import.item),
|
||||
ComponentTypeField::Export(export) => resolver.item_sig(&mut export.item),
|
||||
},
|
||||
|state, field| {
|
||||
match field {
|
||||
ComponentTypeField::Alias(alias) => {
|
||||
state.register_alias(alias)?;
|
||||
}
|
||||
ComponentTypeField::Type(ty) => {
|
||||
state.types.register(ty.id, "type")?;
|
||||
}
|
||||
// Only the type namespace is populated within the component type
|
||||
// namespace so these are ignored here.
|
||||
ComponentTypeField::Import(_) | ComponentTypeField::Export(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn instance_type(&mut self, c: &mut InstanceType<'a>) -> Result<(), Error> {
|
||||
self.resolve_prepending_aliases(
|
||||
&mut c.fields,
|
||||
|resolver, field| match field {
|
||||
InstanceTypeField::Alias(alias) => resolver.alias(alias),
|
||||
InstanceTypeField::Type(ty) => resolver.type_field(ty),
|
||||
InstanceTypeField::Export(export) => resolver.item_sig(&mut export.item),
|
||||
},
|
||||
|state, field| {
|
||||
match field {
|
||||
InstanceTypeField::Alias(alias) => {
|
||||
state.register_alias(alias)?;
|
||||
}
|
||||
InstanceTypeField::Type(ty) => {
|
||||
state.types.register(ty.id, "type")?;
|
||||
}
|
||||
InstanceTypeField::Export(_export) => {}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn item_ref<K>(&mut self, item: &mut ItemRef<'a, K>) -> Result<(), Error>
|
||||
where
|
||||
K: Into<Ns> + Copy,
|
||||
{
|
||||
let last_ns = item.kind.into();
|
||||
|
||||
// If there are no extra `export_names` listed then this is a reference to
|
||||
// something defined within this component's index space, so resolve as
|
||||
// necessary.
|
||||
if item.export_names.is_empty() {
|
||||
self.resolve_ns(&mut item.idx, last_ns)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// ... otherwise the `index` of `item` refers to an intance and the
|
||||
// `export_names` refer to recursive exports from this item. Resolve the
|
||||
// instance locally and then process the export names one at a time,
|
||||
// injecting aliases as necessary.
|
||||
let mut index = item.idx.clone();
|
||||
self.resolve_ns(&mut index, Ns::Instance)?;
|
||||
let span = item.idx.span();
|
||||
for (pos, export_name) in item.export_names.iter().enumerate() {
|
||||
// The last name is in the namespace of the reference. All others are
|
||||
// instances.
|
||||
let ns = if pos == item.export_names.len() - 1 {
|
||||
last_ns
|
||||
} else {
|
||||
Ns::Instance
|
||||
};
|
||||
|
||||
// Record an outer alias to be inserted in front of the current
|
||||
// definition.
|
||||
let mut alias = Alias {
|
||||
span,
|
||||
id: None,
|
||||
name: None,
|
||||
target: AliasTarget::Export {
|
||||
instance: index,
|
||||
export: export_name,
|
||||
},
|
||||
kind: match ns {
|
||||
Ns::Module => AliasKind::Module,
|
||||
Ns::Component => AliasKind::Component,
|
||||
Ns::Instance => AliasKind::Instance,
|
||||
Ns::Value => AliasKind::Value,
|
||||
Ns::Func => AliasKind::ExportKind(core::ExportKind::Func),
|
||||
Ns::Table => AliasKind::ExportKind(core::ExportKind::Table),
|
||||
Ns::Global => AliasKind::ExportKind(core::ExportKind::Global),
|
||||
Ns::Memory => AliasKind::ExportKind(core::ExportKind::Memory),
|
||||
Ns::Tag => AliasKind::ExportKind(core::ExportKind::Tag),
|
||||
Ns::Type => AliasKind::ExportKind(core::ExportKind::Type),
|
||||
},
|
||||
};
|
||||
|
||||
index = Index::Num(self.current().register_alias(&mut alias)?, span);
|
||||
self.aliases_to_insert.push(alias);
|
||||
}
|
||||
item.idx = index;
|
||||
item.export_names = Vec::new();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_ns(&mut self, idx: &mut Index<'a>, ns: Ns) -> Result<(), Error> {
|
||||
// Perform resolution on a local clone walking up the stack of components
|
||||
// that we have. Note that a local clone is used since we don't want to use
|
||||
// the parent's resolved index if a parent matches, instead we want to use
|
||||
// the index of the alias that we will automatically insert.
|
||||
let mut idx_clone = idx.clone();
|
||||
for (depth, resolver) in self.stack.iter_mut().rev().enumerate() {
|
||||
let depth = depth as u32;
|
||||
let found = match resolver.resolve(ns, &mut idx_clone) {
|
||||
Ok(idx) => idx,
|
||||
// Try the next parent
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
// If this is the current component then no extra alias is necessary, so
|
||||
// return success.
|
||||
if depth == 0 {
|
||||
*idx = idx_clone;
|
||||
return Ok(());
|
||||
}
|
||||
let id = match idx {
|
||||
Index::Id(id) => id.clone(),
|
||||
Index::Num(..) => unreachable!(),
|
||||
};
|
||||
|
||||
// When resolution succeeds in a parent then an outer alias is
|
||||
// automatically inserted here in this component.
|
||||
let span = idx.span();
|
||||
let mut alias = Alias {
|
||||
span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
target: AliasTarget::Outer {
|
||||
outer: Index::Num(depth, span),
|
||||
index: Index::Num(found, span),
|
||||
},
|
||||
kind: match ns {
|
||||
Ns::Module => AliasKind::Module,
|
||||
Ns::Component => AliasKind::Component,
|
||||
Ns::Instance => AliasKind::Instance,
|
||||
Ns::Value => AliasKind::Value,
|
||||
Ns::Func => AliasKind::ExportKind(core::ExportKind::Func),
|
||||
Ns::Table => AliasKind::ExportKind(core::ExportKind::Table),
|
||||
Ns::Global => AliasKind::ExportKind(core::ExportKind::Global),
|
||||
Ns::Memory => AliasKind::ExportKind(core::ExportKind::Memory),
|
||||
Ns::Tag => AliasKind::ExportKind(core::ExportKind::Tag),
|
||||
Ns::Type => AliasKind::ExportKind(core::ExportKind::Type),
|
||||
},
|
||||
};
|
||||
let local_index = self.current().register_alias(&mut alias)?;
|
||||
self.aliases_to_insert.push(alias);
|
||||
*idx = Index::Num(local_index, span);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If resolution in any parent failed then simply return the error from our
|
||||
// local namespace
|
||||
self.current().resolve(ns, idx)?;
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn moduletype(&mut self, ty: &mut ModuleType<'_>) -> Result<(), Error> {
|
||||
let mut types = Namespace::default();
|
||||
for def in ty.defs.iter_mut() {
|
||||
match def {
|
||||
ModuleTypeDef::Type(t) => {
|
||||
types.register(t.id, "type")?;
|
||||
}
|
||||
ModuleTypeDef::Import(t) => resolve_item_sig(&mut t.item, &types)?,
|
||||
ModuleTypeDef::Export(_, t) => resolve_item_sig(t, &types)?,
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
|
||||
fn resolve_item_sig<'a>(
|
||||
sig: &mut core::ItemSig<'a>,
|
||||
names: &Namespace<'a>,
|
||||
) -> Result<(), Error> {
|
||||
match &mut sig.kind {
|
||||
core::ItemKind::Func(ty) | core::ItemKind::Tag(core::TagType::Exception(ty)) => {
|
||||
let idx = ty.index.as_mut().expect("index should be filled in");
|
||||
names.resolve(idx, "type")?;
|
||||
}
|
||||
core::ItemKind::Memory(_)
|
||||
| core::ItemKind::Global(_)
|
||||
| core::ItemKind::Table(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ComponentState<'a> {
|
||||
fn resolve(&mut self, ns: Ns, idx: &mut Index<'a>) -> Result<u32, Error> {
|
||||
match ns {
|
||||
Ns::Func => self.funcs.resolve(idx, "func"),
|
||||
Ns::Table => self.tables.resolve(idx, "table"),
|
||||
Ns::Global => self.globals.resolve(idx, "global"),
|
||||
Ns::Memory => self.memories.resolve(idx, "memory"),
|
||||
Ns::Tag => self.tags.resolve(idx, "tag"),
|
||||
Ns::Type => self.types.resolve(idx, "type"),
|
||||
Ns::Component => self.components.resolve(idx, "component"),
|
||||
Ns::Module => self.modules.resolve(idx, "module"),
|
||||
Ns::Instance => self.instances.resolve(idx, "instance"),
|
||||
Ns::Value => self.values.resolve(idx, "instance"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Assign an index to the given field.
|
||||
fn register(&mut self, item: &ComponentField<'a>) -> Result<(), Error> {
|
||||
match item {
|
||||
ComponentField::Import(i) => match &i.item.kind {
|
||||
ItemKind::Module(_) => self.modules.register(i.item.id, "module")?,
|
||||
ItemKind::Component(_) => self.components.register(i.item.id, "component")?,
|
||||
ItemKind::Instance(_) => self.instances.register(i.item.id, "instance")?,
|
||||
ItemKind::Value(_) => self.values.register(i.item.id, "value")?,
|
||||
ItemKind::Func(_) => self.funcs.register(i.item.id, "func")?,
|
||||
},
|
||||
|
||||
ComponentField::Func(i) => self.funcs.register(i.id, "func")?,
|
||||
ComponentField::Type(i) => self.types.register(i.id, "type")?,
|
||||
ComponentField::Instance(i) => self.instances.register(i.id, "instance")?,
|
||||
ComponentField::Module(m) => self.modules.register(m.id, "nested module")?,
|
||||
ComponentField::Component(c) => self.components.register(c.id, "nested component")?,
|
||||
ComponentField::Alias(a) => self.register_alias(a)?,
|
||||
ComponentField::Start(s) => self.values.register(s.result, "value")?,
|
||||
|
||||
// These fields don't define any items in any index space.
|
||||
ComponentField::Export(_) => return Ok(()),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_alias(&mut self, alias: &Alias<'a>) -> Result<u32, Error> {
|
||||
match alias.kind {
|
||||
AliasKind::Module => self.modules.register(alias.id, "module"),
|
||||
AliasKind::Component => self.components.register(alias.id, "component"),
|
||||
AliasKind::Instance => self.instances.register(alias.id, "instance"),
|
||||
AliasKind::Value => self.values.register(alias.id, "value"),
|
||||
AliasKind::ExportKind(core::ExportKind::Func) => self.funcs.register(alias.id, "func"),
|
||||
AliasKind::ExportKind(core::ExportKind::Table) => {
|
||||
self.tables.register(alias.id, "table")
|
||||
}
|
||||
AliasKind::ExportKind(core::ExportKind::Memory) => {
|
||||
self.memories.register(alias.id, "memory")
|
||||
}
|
||||
AliasKind::ExportKind(core::ExportKind::Global) => {
|
||||
self.globals.register(alias.id, "global")
|
||||
}
|
||||
AliasKind::ExportKind(core::ExportKind::Tag) => self.tags.register(alias.id, "tag"),
|
||||
AliasKind::ExportKind(core::ExportKind::Type) => self.types.register(alias.id, "type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
enum Ns {
|
||||
Func,
|
||||
Table,
|
||||
Global,
|
||||
Memory,
|
||||
Tag,
|
||||
Type,
|
||||
Component,
|
||||
Module,
|
||||
Instance,
|
||||
Value,
|
||||
}
|
||||
|
||||
macro_rules! component_kw_conversions {
|
||||
($($kw:ident => $kind:ident)*) => ($(
|
||||
impl From<kw::$kw> for Ns {
|
||||
fn from(_: kw::$kw) -> Ns {
|
||||
Ns::$kind
|
||||
}
|
||||
}
|
||||
)*);
|
||||
}
|
||||
|
||||
component_kw_conversions! {
|
||||
func => Func
|
||||
module => Module
|
||||
component => Component
|
||||
instance => Instance
|
||||
value => Value
|
||||
table => Table
|
||||
memory => Memory
|
||||
global => Global
|
||||
tag => Tag
|
||||
r#type => Type
|
||||
}
|
||||
|
||||
impl From<DefTypeKind> for Ns {
|
||||
fn from(kind: DefTypeKind) -> Self {
|
||||
match kind {
|
||||
DefTypeKind::Module => Ns::Module,
|
||||
DefTypeKind::Component => Ns::Component,
|
||||
DefTypeKind::Instance => Ns::Instance,
|
||||
DefTypeKind::Value => Ns::Value,
|
||||
DefTypeKind::Func => Ns::Func,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::ExportKind> for Ns {
|
||||
fn from(kind: core::ExportKind) -> Self {
|
||||
match kind {
|
||||
core::ExportKind::Func => Ns::Func,
|
||||
core::ExportKind::Table => Ns::Table,
|
||||
core::ExportKind::Global => Ns::Global,
|
||||
core::ExportKind::Memory => Ns::Memory,
|
||||
core::ExportKind::Tag => Ns::Tag,
|
||||
core::ExportKind::Type => Ns::Type,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
use crate::component::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// A definition of a type.
|
||||
///
|
||||
/// typeexpr ::= <deftype>
|
||||
/// | <intertype>
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentTypeDef<'a> {
|
||||
/// The type of an entity.
|
||||
DefType(DefType<'a>),
|
||||
/// The type of a value.
|
||||
InterType(InterType<'a>),
|
||||
}
|
||||
|
||||
/// The type of an exported item from an component or instance.
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentExportType<'a> {
|
||||
/// Where this export was defined.
|
||||
pub span: Span,
|
||||
/// The name of this export.
|
||||
pub name: &'a str,
|
||||
/// The signature of the item that's exported.
|
||||
pub item: ItemSig<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ComponentExportType<'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(ComponentExportType { span, name, item })
|
||||
}
|
||||
}
|
||||
/// A type declaration in a component.
|
||||
///
|
||||
/// type ::= (type <id>? <typeexpr>)
|
||||
#[derive(Debug)]
|
||||
pub struct TypeField<'a> {
|
||||
/// Where this type was defined.
|
||||
pub span: Span,
|
||||
/// An optional identifer to refer to this `type` by as part of name
|
||||
/// resolution.
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// The type that we're declaring.
|
||||
pub def: ComponentTypeDef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for TypeField<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::r#type>()?.0;
|
||||
let id = parser.parse()?;
|
||||
let name = parser.parse()?;
|
||||
let def = if parser.peek2::<DefTypeKind>() {
|
||||
ComponentTypeDef::DefType(parser.parens(|p| p.parse())?)
|
||||
} else {
|
||||
ComponentTypeDef::InterType(parser.parse()?)
|
||||
};
|
||||
Ok(TypeField {
|
||||
span,
|
||||
id,
|
||||
name,
|
||||
def,
|
||||
})
|
||||
}
|
||||
}
|
||||
/// A reference to a type defined in this component.
|
||||
///
|
||||
/// This is the same as `TypeUse`, but accepts `$T` as shorthand for
|
||||
/// `(type $T)`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ComponentTypeUse<'a, T> {
|
||||
/// The type that we're referencing.
|
||||
Ref(ItemRef<'a, kw::r#type>),
|
||||
/// The inline type.
|
||||
Inline(T),
|
||||
}
|
||||
|
||||
impl<'a, T: Parse<'a>> Parse<'a> for ComponentTypeUse<'a, T> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek::<ItemRef<'a, kw::r#type>>() {
|
||||
Ok(ComponentTypeUse::Ref(parser.parse()?))
|
||||
} else {
|
||||
Ok(ComponentTypeUse::Inline(parser.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +1,14 @@
|
|||
use crate::ast::*;
|
||||
use crate::core::*;
|
||||
use crate::encode::Encode;
|
||||
use crate::token::*;
|
||||
|
||||
pub fn encode(module: &Module<'_>) -> Vec<u8> {
|
||||
match &module.kind {
|
||||
ModuleKind::Text(fields) => encode_fields(&module.id, &module.name, fields),
|
||||
ModuleKind::Binary(bytes) => bytes.iter().flat_map(|b| b.iter().cloned()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_fields(
|
||||
pub fn encode(
|
||||
module_id: &Option<Id<'_>>,
|
||||
module_name: &Option<NameAnnotation<'_>>,
|
||||
fields: &[ModuleField<'_>],
|
||||
) -> Vec<u8> {
|
||||
use crate::ast::CustomPlace::*;
|
||||
use crate::ast::CustomPlaceAnchor::*;
|
||||
use CustomPlace::*;
|
||||
use CustomPlaceAnchor::*;
|
||||
|
||||
let mut types = Vec::new();
|
||||
let mut imports = Vec::new();
|
||||
|
@ -27,9 +22,6 @@ fn encode_fields(
|
|||
let mut data = Vec::new();
|
||||
let mut tags = Vec::new();
|
||||
let mut customs = Vec::new();
|
||||
let mut instances = Vec::new();
|
||||
let mut modules = Vec::new();
|
||||
let mut aliases = Vec::new();
|
||||
for field in fields {
|
||||
match field {
|
||||
ModuleField::Type(i) => types.push(i),
|
||||
|
@ -44,9 +36,6 @@ fn encode_fields(
|
|||
ModuleField::Data(i) => data.push(i),
|
||||
ModuleField::Tag(i) => tags.push(i),
|
||||
ModuleField::Custom(i) => customs.push(i),
|
||||
ModuleField::Instance(i) => instances.push(i),
|
||||
ModuleField::NestedModule(i) => modules.push(i),
|
||||
ModuleField::Alias(a) => aliases.push(a),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,47 +49,8 @@ fn encode_fields(
|
|||
|
||||
e.custom_sections(BeforeFirst);
|
||||
|
||||
let mut items = fields
|
||||
.iter()
|
||||
.filter(|i| match i {
|
||||
ModuleField::Alias(_)
|
||||
| ModuleField::Type(_)
|
||||
| ModuleField::Import(_)
|
||||
| ModuleField::NestedModule(_)
|
||||
| ModuleField::Instance(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.peekable();
|
||||
|
||||
// A special path is used for now to handle non-module-linking modules to
|
||||
// work around WebAssembly/annotations#11
|
||||
if aliases.len() == 0 && modules.len() == 0 && instances.len() == 0 {
|
||||
e.section_list(1, Type, &types);
|
||||
e.section_list(2, Import, &imports);
|
||||
} else {
|
||||
while let Some(field) = items.next() {
|
||||
macro_rules! list {
|
||||
($code:expr, $name:ident) => {
|
||||
list!($code, $name, $name)
|
||||
};
|
||||
($code:expr, $field:ident, $custom:ident) => {
|
||||
if let ModuleField::$field(f) = field {
|
||||
let mut list = vec![f];
|
||||
while let Some(ModuleField::$field(f)) = items.peek() {
|
||||
list.push(f);
|
||||
items.next();
|
||||
}
|
||||
e.section_list($code, $custom, &list);
|
||||
}
|
||||
};
|
||||
}
|
||||
list!(1, Type);
|
||||
list!(2, Import);
|
||||
list!(14, NestedModule, Module);
|
||||
list!(15, Instance);
|
||||
list!(16, Alias);
|
||||
}
|
||||
}
|
||||
e.section_list(1, Type, &types);
|
||||
e.section_list(2, Import, &imports);
|
||||
|
||||
let functys = funcs.iter().map(|f| &f.ty).collect::<Vec<_>>();
|
||||
e.section_list(3, Func, &functys);
|
||||
|
@ -129,7 +79,7 @@ fn encode_fields(
|
|||
|
||||
return e.wasm;
|
||||
|
||||
fn contains_bulk_memory(funcs: &[&crate::ast::Func<'_>]) -> bool {
|
||||
fn contains_bulk_memory(funcs: &[&crate::core::Func<'_>]) -> bool {
|
||||
funcs
|
||||
.iter()
|
||||
.filter_map(|f| match &f.kind {
|
||||
|
@ -175,75 +125,6 @@ impl Encoder<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) trait Encode {
|
||||
fn encode(&self, e: &mut Vec<u8>);
|
||||
}
|
||||
|
||||
impl<T: Encode + ?Sized> Encode for &'_ T {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
T::encode(self, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for [T] {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.len().encode(e);
|
||||
for item in self {
|
||||
item.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Vec<T> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
<[T]>::encode(self, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for str {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.len().encode(e);
|
||||
e.extend_from_slice(self.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for usize {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
assert!(*self <= u32::max_value() as usize);
|
||||
(*self as u32).encode(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for u8 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for u32 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::unsigned(e, (*self).into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for i32 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::signed(e, (*self).into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for u64 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::unsigned(e, (*self).into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for i64 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::signed(e, *self).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for FunctionType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.params.len().encode(e);
|
||||
|
@ -271,19 +152,6 @@ impl Encode for ArrayType<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Encode for ModuleType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.imports.encode(e);
|
||||
self.exports.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for InstanceType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.exports.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for ExportType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
|
@ -306,14 +174,6 @@ impl Encode for Type<'_> {
|
|||
e.push(0x5e);
|
||||
array.encode(e)
|
||||
}
|
||||
TypeDef::Module(module) => {
|
||||
e.push(0x61);
|
||||
module.encode(e)
|
||||
}
|
||||
TypeDef::Instance(instance) => {
|
||||
e.push(0x62);
|
||||
instance.encode(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -324,13 +184,6 @@ impl Encode for Option<Id<'_>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Encode, U: Encode> Encode for (T, U) {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.0.encode(e);
|
||||
self.1.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for ValType<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
|
@ -435,13 +288,7 @@ impl<'a> Encode for StorageType<'a> {
|
|||
impl Encode for Import<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.module.encode(e);
|
||||
match self.field {
|
||||
Some(s) => s.encode(e),
|
||||
None => {
|
||||
e.push(0x00);
|
||||
e.push(0xff);
|
||||
}
|
||||
}
|
||||
self.field.encode(e);
|
||||
self.item.encode(e);
|
||||
}
|
||||
}
|
||||
|
@ -469,14 +316,6 @@ impl Encode for ItemSig<'_> {
|
|||
e.push(0x04);
|
||||
f.encode(e);
|
||||
}
|
||||
ItemKind::Module(m) => {
|
||||
e.push(0x05);
|
||||
m.encode(e);
|
||||
}
|
||||
ItemKind::Instance(i) => {
|
||||
e.push(0x06);
|
||||
i.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -499,32 +338,6 @@ impl Encode for Index<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Encode for IndexOrRef<'_, T> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.0.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Encode for ItemRef<'_, T> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match self {
|
||||
ItemRef::Outer { .. } => panic!("should be expanded previously"),
|
||||
ItemRef::Item {
|
||||
idx,
|
||||
exports,
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited,
|
||||
..
|
||||
} => {
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
assert!(*visited);
|
||||
assert!(exports.is_empty());
|
||||
idx.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for TableType<'a> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.elem.encode(e);
|
||||
|
@ -620,10 +433,8 @@ impl Encode for Global<'_> {
|
|||
impl Encode for Export<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
if let ItemRef::Item { kind, .. } = &self.index {
|
||||
kind.encode(e);
|
||||
}
|
||||
self.index.encode(e);
|
||||
self.kind.encode(e);
|
||||
self.item.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -635,8 +446,6 @@ impl Encode for ExportKind {
|
|||
ExportKind::Memory => e.push(0x02),
|
||||
ExportKind::Global => e.push(0x03),
|
||||
ExportKind::Tag => e.push(0x04),
|
||||
ExportKind::Module => e.push(0x05),
|
||||
ExportKind::Instance => e.push(0x06),
|
||||
ExportKind::Type => e.push(0x07),
|
||||
}
|
||||
}
|
||||
|
@ -647,11 +456,7 @@ impl Encode for Elem<'_> {
|
|||
match (&self.kind, &self.payload) {
|
||||
(
|
||||
ElemKind::Active {
|
||||
table:
|
||||
ItemRef::Item {
|
||||
idx: Index::Num(0, _),
|
||||
..
|
||||
},
|
||||
table: Index::Num(0, _),
|
||||
offset,
|
||||
},
|
||||
ElemPayload::Indices(_),
|
||||
|
@ -671,11 +476,7 @@ impl Encode for Elem<'_> {
|
|||
}
|
||||
(
|
||||
ElemKind::Active {
|
||||
table:
|
||||
ItemRef::Item {
|
||||
idx: Index::Num(0, _),
|
||||
..
|
||||
},
|
||||
table: Index::Num(0, _),
|
||||
offset,
|
||||
},
|
||||
ElemPayload::Exprs {
|
||||
|
@ -732,17 +533,16 @@ impl Encode for Data<'_> {
|
|||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.kind {
|
||||
DataKind::Passive => e.push(0x01),
|
||||
DataKind::Active {
|
||||
memory: Index::Num(0, _),
|
||||
offset,
|
||||
} => {
|
||||
e.push(0x00);
|
||||
offset.encode(e);
|
||||
}
|
||||
DataKind::Active { memory, offset } => {
|
||||
if let ItemRef::Item {
|
||||
idx: Index::Num(0, _),
|
||||
..
|
||||
} = memory
|
||||
{
|
||||
e.push(0x00);
|
||||
} else {
|
||||
e.push(0x02);
|
||||
memory.encode(e);
|
||||
}
|
||||
e.push(0x02);
|
||||
memory.encode(e);
|
||||
offset.encode(e);
|
||||
}
|
||||
}
|
||||
|
@ -798,11 +598,7 @@ impl Encode for Expression<'_> {
|
|||
impl Encode for BlockType<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
// block types using an index are encoded as an sleb, not a uleb
|
||||
if let Some(ItemRef::Item {
|
||||
idx: Index::Num(n, _),
|
||||
..
|
||||
}) = &self.ty.index
|
||||
{
|
||||
if let Some(Index::Num(n, _)) = &self.ty.index {
|
||||
return i64::from(*n).encode(e);
|
||||
}
|
||||
let ty = self
|
||||
|
@ -842,17 +638,14 @@ impl Encode for LaneArg {
|
|||
impl Encode for MemArg<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.memory {
|
||||
ItemRef::Item {
|
||||
idx: Index::Num(0, _),
|
||||
..
|
||||
} => {
|
||||
Index::Num(0, _) => {
|
||||
self.align.trailing_zeros().encode(e);
|
||||
self.offset.encode(e);
|
||||
}
|
||||
n => {
|
||||
_ => {
|
||||
(self.align.trailing_zeros() | (1 << 6)).encode(e);
|
||||
self.offset.encode(e);
|
||||
n.encode(e);
|
||||
self.memory.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -946,10 +739,6 @@ struct Names<'a> {
|
|||
table_idx: u32,
|
||||
tags: Vec<(u32, &'a str)>,
|
||||
tag_idx: u32,
|
||||
modules: Vec<(u32, &'a str)>,
|
||||
module_idx: u32,
|
||||
instances: Vec<(u32, &'a str)>,
|
||||
instance_idx: u32,
|
||||
types: Vec<(u32, &'a str)>,
|
||||
type_idx: u32,
|
||||
data: Vec<(u32, &'a str)>,
|
||||
|
@ -977,8 +766,6 @@ fn find_names<'a>(
|
|||
Type,
|
||||
Global,
|
||||
Func,
|
||||
Module,
|
||||
Instance,
|
||||
Memory,
|
||||
Table,
|
||||
Tag,
|
||||
|
@ -998,8 +785,6 @@ fn find_names<'a>(
|
|||
ItemKind::Memory(_) => Name::Memory,
|
||||
ItemKind::Global(_) => Name::Global,
|
||||
ItemKind::Tag(_) => Name::Tag,
|
||||
ItemKind::Instance(_) => Name::Instance,
|
||||
ItemKind::Module(_) => Name::Module,
|
||||
},
|
||||
&i.item.id,
|
||||
&i.item.name,
|
||||
|
@ -1008,26 +793,10 @@ fn find_names<'a>(
|
|||
ModuleField::Table(t) => (Name::Table, &t.id, &t.name),
|
||||
ModuleField::Memory(m) => (Name::Memory, &m.id, &m.name),
|
||||
ModuleField::Tag(t) => (Name::Tag, &t.id, &t.name),
|
||||
ModuleField::NestedModule(m) => (Name::Module, &m.id, &m.name),
|
||||
ModuleField::Instance(i) => (Name::Instance, &i.id, &i.name),
|
||||
ModuleField::Type(t) => (Name::Type, &t.id, &t.name),
|
||||
ModuleField::Elem(e) => (Name::Elem, &e.id, &e.name),
|
||||
ModuleField::Data(d) => (Name::Data, &d.id, &d.name),
|
||||
ModuleField::Func(f) => (Name::Func, &f.id, &f.name),
|
||||
ModuleField::Alias(a) => (
|
||||
match a.kind {
|
||||
ExportKind::Func => Name::Func,
|
||||
ExportKind::Table => Name::Table,
|
||||
ExportKind::Memory => Name::Memory,
|
||||
ExportKind::Global => Name::Global,
|
||||
ExportKind::Module => Name::Module,
|
||||
ExportKind::Instance => Name::Instance,
|
||||
ExportKind::Tag => Name::Tag,
|
||||
ExportKind::Type => Name::Type,
|
||||
},
|
||||
&a.id,
|
||||
&a.name,
|
||||
),
|
||||
ModuleField::Export(_) | ModuleField::Start(_) | ModuleField::Custom(_) => continue,
|
||||
};
|
||||
|
||||
|
@ -1037,8 +806,6 @@ fn find_names<'a>(
|
|||
Name::Table => (&mut ret.tables, &mut ret.table_idx),
|
||||
Name::Memory => (&mut ret.memories, &mut ret.memory_idx),
|
||||
Name::Global => (&mut ret.globals, &mut ret.global_idx),
|
||||
Name::Module => (&mut ret.modules, &mut ret.module_idx),
|
||||
Name::Instance => (&mut ret.instances, &mut ret.instance_idx),
|
||||
Name::Tag => (&mut ret.tags, &mut ret.tag_idx),
|
||||
Name::Type => (&mut ret.types, &mut ret.type_idx),
|
||||
Name::Elem => (&mut ret.elems, &mut ret.elem_idx),
|
||||
|
@ -1244,56 +1011,3 @@ impl Encode for StructAccess<'_> {
|
|||
self.field.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for NestedModule<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
let fields = match &self.kind {
|
||||
NestedModuleKind::Inline { fields, .. } => fields,
|
||||
_ => panic!("should only have inline modules in emission"),
|
||||
};
|
||||
|
||||
encode_fields(&self.id, &self.name, fields).encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Instance<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
assert!(self.exports.names.is_empty());
|
||||
let (module, args) = match &self.kind {
|
||||
InstanceKind::Inline { module, args } => (module, args),
|
||||
_ => panic!("should only have inline instances in emission"),
|
||||
};
|
||||
e.push(0x00);
|
||||
module.encode(e);
|
||||
args.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for InstanceArg<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.name.encode(e);
|
||||
if let ItemRef::Item { kind, .. } = &self.index {
|
||||
kind.encode(e);
|
||||
}
|
||||
self.index.encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Alias<'_> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
match &self.source {
|
||||
AliasSource::InstanceExport { instance, export } => {
|
||||
e.push(0x00);
|
||||
instance.encode(e);
|
||||
self.kind.encode(e);
|
||||
export.encode(e);
|
||||
}
|
||||
AliasSource::Outer { module, index } => {
|
||||
e.push(0x01);
|
||||
module.encode(e);
|
||||
self.kind.encode(e);
|
||||
index.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
use crate::ast::{self, annotation, kw};
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{self, Span};
|
||||
use crate::{annotation, kw};
|
||||
|
||||
/// A wasm custom section within a module.
|
||||
#[derive(Debug)]
|
||||
pub struct Custom<'a> {
|
||||
/// Where this `@custom` was defined.
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
|
||||
/// Name of the custom section.
|
||||
pub name: &'a str,
|
||||
|
@ -36,9 +37,6 @@ pub enum CustomPlace {
|
|||
pub enum CustomPlaceAnchor {
|
||||
Type,
|
||||
Import,
|
||||
Module,
|
||||
Instance,
|
||||
Alias,
|
||||
Func,
|
||||
Table,
|
||||
Memory,
|
||||
|
@ -55,7 +53,7 @@ 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>() {
|
||||
let place = if parser.peek::<token::LParen>() {
|
||||
parser.parens(|p| p.parse())?
|
||||
} else {
|
||||
CustomPlace::AfterLast
|
||||
|
@ -147,18 +145,6 @@ impl<'a> Parse<'a> for CustomPlaceAnchor {
|
|||
parser.parse::<kw::tag>()?;
|
||||
return Ok(CustomPlaceAnchor::Tag);
|
||||
}
|
||||
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"))
|
||||
}
|
|
@ -1,15 +1,18 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::kw;
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use crate::token::{Index, Span};
|
||||
|
||||
/// A entry in a WebAssembly module's export section.
|
||||
#[derive(Debug)]
|
||||
pub struct Export<'a> {
|
||||
/// Where this export was defined.
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// The name of this export from the module.
|
||||
pub name: &'a str,
|
||||
/// The kind of item being exported.
|
||||
pub kind: ExportKind,
|
||||
/// What's being exported from the module.
|
||||
pub index: ast::ItemRef<'a, ExportKind>,
|
||||
pub item: Index<'a>,
|
||||
}
|
||||
|
||||
/// Different kinds of elements that can be exported from a WebAssembly module,
|
||||
|
@ -22,17 +25,19 @@ pub enum ExportKind {
|
|||
Memory,
|
||||
Global,
|
||||
Tag,
|
||||
Module,
|
||||
Instance,
|
||||
Type,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Export<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let span = parser.parse::<kw::export>()?.0;
|
||||
let name = parser.parse()?;
|
||||
let (kind, item) = parser.parens(|p| Ok((p.parse()?, p.parse()?)))?;
|
||||
Ok(Export {
|
||||
span: parser.parse::<kw::export>()?.0,
|
||||
name: parser.parse()?,
|
||||
index: parser.parse()?,
|
||||
span,
|
||||
name,
|
||||
kind,
|
||||
item,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -55,12 +60,6 @@ impl<'a> Parse<'a> for ExportKind {
|
|||
} else if l.peek::<kw::tag>() {
|
||||
parser.parse::<kw::tag>()?;
|
||||
Ok(ExportKind::Tag)
|
||||
} 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)
|
||||
|
@ -70,6 +69,20 @@ impl<'a> Parse<'a> for ExportKind {
|
|||
}
|
||||
}
|
||||
|
||||
impl Peek for ExportKind {
|
||||
fn peek(cursor: Cursor<'_>) -> bool {
|
||||
kw::func::peek(cursor)
|
||||
|| kw::table::peek(cursor)
|
||||
|| kw::memory::peek(cursor)
|
||||
|| kw::global::peek(cursor)
|
||||
|| kw::tag::peek(cursor)
|
||||
|| kw::r#type::peek(cursor)
|
||||
}
|
||||
fn display() -> &'static str {
|
||||
"export kind"
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! kw_conversions {
|
||||
($($kw:ident => $kind:ident)*) => ($(
|
||||
impl From<kw::$kw> for ExportKind {
|
||||
|
@ -80,15 +93,13 @@ macro_rules! kw_conversions {
|
|||
|
||||
impl Default for kw::$kw {
|
||||
fn default() -> kw::$kw {
|
||||
kw::$kw(ast::Span::from_offset(0))
|
||||
kw::$kw(Span::from_offset(0))
|
||||
}
|
||||
}
|
||||
)*);
|
||||
}
|
||||
|
||||
kw_conversions! {
|
||||
instance => Instance
|
||||
module => Module
|
||||
func => Func
|
||||
table => Table
|
||||
global => Global
|
||||
|
@ -99,7 +110,7 @@ kw_conversions! {
|
|||
|
||||
/// A listing of inline `(export "foo")` statements on a WebAssembly item in
|
||||
/// its textual format.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InlineExport<'a> {
|
||||
/// The extra names to export an item as, if any.
|
||||
pub names: Vec<&'a str>,
|
|
@ -1,5 +1,8 @@
|
|||
use crate::ast::{self, kw, HeapType};
|
||||
use crate::core::*;
|
||||
use crate::encode::Encode;
|
||||
use crate::kw;
|
||||
use crate::parser::{Cursor, Parse, Parser, Result};
|
||||
use crate::token::*;
|
||||
use std::mem;
|
||||
|
||||
/// An expression, or a list of instructions, in the WebAssembly text format.
|
||||
|
@ -114,7 +117,7 @@ impl<'a> ExpressionParser<'a> {
|
|||
// 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>() {
|
||||
if !parser.is_empty() && !parser.peek::<LParen>() {
|
||||
return Err(parser.error("expected `(`"));
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +349,7 @@ impl<'a> ExpressionParser<'a> {
|
|||
if let Try::CatchOrDelegate = 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>>()?;
|
||||
let evt = parser.parse::<Index<'a>>()?;
|
||||
self.instrs.push(Instruction::Catch(evt));
|
||||
*i = Try::Catch;
|
||||
self.stack.push(Level::TryArm);
|
||||
|
@ -361,7 +364,7 @@ impl<'a> ExpressionParser<'a> {
|
|||
}
|
||||
// `delegate` has an index, and also ends the block like `end`.
|
||||
if parser.parse::<Option<kw::delegate>>()?.is_some() {
|
||||
let depth = parser.parse::<ast::Index<'a>>()?;
|
||||
let depth = parser.parse::<Index<'a>>()?;
|
||||
self.instrs.push(Instruction::Delegate(depth));
|
||||
*i = Try::Delegate;
|
||||
match self.paren(parser)? {
|
||||
|
@ -374,7 +377,7 @@ impl<'a> ExpressionParser<'a> {
|
|||
|
||||
if let Try::Catch = i {
|
||||
if parser.parse::<Option<kw::catch>>()?.is_some() {
|
||||
let evt = parser.parse::<ast::Index<'a>>()?;
|
||||
let evt = parser.parse::<Index<'a>>()?;
|
||||
self.instrs.push(Instruction::Catch(evt));
|
||||
*i = Try::Catch;
|
||||
self.stack.push(Level::TryArm);
|
||||
|
@ -436,17 +439,17 @@ macro_rules! instructions {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::binary::Encode for Instruction<'_> {
|
||||
impl Encode for Instruction<'_> {
|
||||
#[allow(non_snake_case)]
|
||||
fn encode(&self, v: &mut Vec<u8>) {
|
||||
match self {
|
||||
$(
|
||||
Instruction::$name $((instructions!(@first $($arg)*)))? => {
|
||||
Instruction::$name $((instructions!(@first x $($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);)?
|
||||
$(<instructions!(@ty $($arg)*) as Encode>::encode(arg, v);)?
|
||||
}
|
||||
encode($( instructions!(@first $($arg)*), )? v)
|
||||
encode($( instructions!(@first x $($arg)*), )? v)
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
@ -484,7 +487,7 @@ macro_rules! instructions {
|
|||
// 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);
|
||||
<u32 as Encode>::encode(&$simd, $dst);
|
||||
});
|
||||
(@encode $dst:ident $($bytes:tt)*) => ($dst.extend_from_slice(&[$($bytes)*]););
|
||||
|
||||
|
@ -501,21 +504,21 @@ instructions! {
|
|||
pub enum Instruction<'a> {
|
||||
Block(BlockType<'a>) : [0x02] : "block",
|
||||
If(BlockType<'a>) : [0x04] : "if",
|
||||
Else(Option<ast::Id<'a>>) : [0x05] : "else",
|
||||
Else(Option<Id<'a>>) : [0x05] : "else",
|
||||
Loop(BlockType<'a>) : [0x03] : "loop",
|
||||
End(Option<ast::Id<'a>>) : [0x0b] : "end",
|
||||
End(Option<Id<'a>>) : [0x0b] : "end",
|
||||
|
||||
Unreachable : [0x00] : "unreachable",
|
||||
Nop : [0x01] : "nop",
|
||||
Br(ast::Index<'a>) : [0x0c] : "br",
|
||||
BrIf(ast::Index<'a>) : [0x0d] : "br_if",
|
||||
Br(Index<'a>) : [0x0c] : "br",
|
||||
BrIf(Index<'a>) : [0x0d] : "br_if",
|
||||
BrTable(BrTableIndices<'a>) : [0x0e] : "br_table",
|
||||
Return : [0x0f] : "return",
|
||||
Call(ast::IndexOrRef<'a, kw::func>) : [0x10] : "call",
|
||||
Call(Index<'a>) : [0x10] : "call",
|
||||
CallIndirect(CallIndirect<'a>) : [0x11] : "call_indirect",
|
||||
|
||||
// tail-call proposal
|
||||
ReturnCall(ast::IndexOrRef<'a, kw::func>) : [0x12] : "return_call",
|
||||
ReturnCall(Index<'a>) : [0x12] : "return_call",
|
||||
ReturnCallIndirect(CallIndirect<'a>) : [0x13] : "return_call_indirect",
|
||||
|
||||
// function-references proposal
|
||||
|
@ -526,11 +529,11 @@ instructions! {
|
|||
|
||||
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",
|
||||
LocalGet(Index<'a>) : [0x20] : "local.get" | "get_local",
|
||||
LocalSet(Index<'a>) : [0x21] : "local.set" | "set_local",
|
||||
LocalTee(Index<'a>) : [0x22] : "local.tee" | "tee_local",
|
||||
GlobalGet(Index<'a>) : [0x23] : "global.get" | "get_global",
|
||||
GlobalSet(Index<'a>) : [0x24] : "global.set" | "set_global",
|
||||
|
||||
TableGet(TableArg<'a>) : [0x25] : "table.get",
|
||||
TableSet(TableArg<'a>) : [0x26] : "table.set",
|
||||
|
@ -565,8 +568,8 @@ instructions! {
|
|||
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",
|
||||
DataDrop(Index<'a>) : [0xfc, 0x09] : "data.drop",
|
||||
ElemDrop(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",
|
||||
|
@ -576,35 +579,35 @@ instructions! {
|
|||
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",
|
||||
RefFunc(Index<'a>) : [0xd2] : "ref.func",
|
||||
|
||||
// function-references proposal
|
||||
RefAsNonNull : [0xd3] : "ref.as_non_null",
|
||||
BrOnNull(ast::Index<'a>) : [0xd4] : "br_on_null",
|
||||
BrOnNonNull(ast::Index<'a>) : [0xd6] : "br_on_non_null",
|
||||
BrOnNull(Index<'a>) : [0xd4] : "br_on_null",
|
||||
BrOnNonNull(Index<'a>) : [0xd6] : "br_on_non_null",
|
||||
|
||||
// gc proposal: eqref
|
||||
RefEq : [0xd5] : "ref.eq",
|
||||
|
||||
// gc proposal (moz specific, will be removed)
|
||||
StructNew(ast::Index<'a>) : [0xfb, 0x0] : "struct.new",
|
||||
StructNew(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",
|
||||
StructNewWithRtt(Index<'a>) : [0xfb, 0x01] : "struct.new_with_rtt",
|
||||
StructNewDefaultWithRtt(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: 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",
|
||||
ArrayNewWithRtt(Index<'a>) : [0xfb, 0x11] : "array.new_with_rtt",
|
||||
ArrayNewDefaultWithRtt(Index<'a>) : [0xfb, 0x12] : "array.new_default_with_rtt",
|
||||
ArrayGet(Index<'a>) : [0xfb, 0x13] : "array.get",
|
||||
ArrayGetS(Index<'a>) : [0xfb, 0x14] : "array.get_s",
|
||||
ArrayGetU(Index<'a>) : [0xfb, 0x15] : "array.get_u",
|
||||
ArraySet(Index<'a>) : [0xfb, 0x16] : "array.set",
|
||||
ArrayLen(Index<'a>) : [0xfb, 0x17] : "array.len",
|
||||
|
||||
// gc proposal, i31
|
||||
I31New : [0xfb, 0x20] : "i31.new",
|
||||
|
@ -612,11 +615,11 @@ instructions! {
|
|||
I31GetU : [0xfb, 0x22] : "i31.get_u",
|
||||
|
||||
// gc proposal, rtt casting
|
||||
RTTCanon(ast::Index<'a>) : [0xfb, 0x30] : "rtt.canon",
|
||||
RTTSub(ast::Index<'a>) : [0xfb, 0x31] : "rtt.sub",
|
||||
RTTCanon(Index<'a>) : [0xfb, 0x30] : "rtt.canon",
|
||||
RTTSub(Index<'a>) : [0xfb, 0x31] : "rtt.sub",
|
||||
RefTest : [0xfb, 0x40] : "ref.test",
|
||||
RefCast : [0xfb, 0x41] : "ref.cast",
|
||||
BrOnCast(ast::Index<'a>) : [0xfb, 0x42] : "br_on_cast",
|
||||
BrOnCast(Index<'a>) : [0xfb, 0x42] : "br_on_cast",
|
||||
|
||||
// gc proposal, heap casting
|
||||
RefIsFunc : [0xfb, 0x50] : "ref.is_func",
|
||||
|
@ -625,14 +628,14 @@ instructions! {
|
|||
RefAsFunc : [0xfb, 0x58] : "ref.as_func",
|
||||
RefAsData : [0xfb, 0x59] : "ref.as_data",
|
||||
RefAsI31 : [0xfb, 0x5a] : "ref.as_i31",
|
||||
BrOnFunc(ast::Index<'a>) : [0xfb, 0x60] : "br_on_func",
|
||||
BrOnData(ast::Index<'a>) : [0xfb, 0x61] : "br_on_data",
|
||||
BrOnI31(ast::Index<'a>) : [0xfb, 0x62] : "br_on_i31",
|
||||
BrOnFunc(Index<'a>) : [0xfb, 0x60] : "br_on_func",
|
||||
BrOnData(Index<'a>) : [0xfb, 0x61] : "br_on_data",
|
||||
BrOnI31(Index<'a>) : [0xfb, 0x62] : "br_on_i31",
|
||||
|
||||
I32Const(i32) : [0x41] : "i32.const",
|
||||
I64Const(i64) : [0x42] : "i64.const",
|
||||
F32Const(ast::Float32) : [0x43] : "f32.const",
|
||||
F64Const(ast::Float64) : [0x44] : "f64.const",
|
||||
F32Const(Float32) : [0x43] : "f32.const",
|
||||
F64Const(Float64) : [0x44] : "f64.const",
|
||||
|
||||
I32Clz : [0x67] : "i32.clz",
|
||||
I32Ctz : [0x68] : "i32.ctz",
|
||||
|
@ -1120,30 +1123,30 @@ instructions! {
|
|||
|
||||
// Exception handling proposal
|
||||
Try(BlockType<'a>) : [0x06] : "try",
|
||||
Catch(ast::Index<'a>) : [0x07] : "catch",
|
||||
Throw(ast::Index<'a>) : [0x08] : "throw",
|
||||
Rethrow(ast::Index<'a>) : [0x09] : "rethrow",
|
||||
Delegate(ast::Index<'a>) : [0x18] : "delegate",
|
||||
Catch(Index<'a>) : [0x07] : "catch",
|
||||
Throw(Index<'a>) : [0x08] : "throw",
|
||||
Rethrow(Index<'a>) : [0x09] : "rethrow",
|
||||
Delegate(Index<'a>) : [0x18] : "delegate",
|
||||
CatchAll : [0x19] : "catch_all",
|
||||
|
||||
// Relaxed SIMD proposal
|
||||
I8x16SwizzleRelaxed : [0xfd, 0xa2]: "i8x16.swizzle_relaxed",
|
||||
I32x4TruncSatF32x4SRelaxed : [0xfd, 0xa5]: "i32x4.trunc_f32x4_s_relaxed",
|
||||
I32x4TruncSatF32x4URelaxed : [0xfd, 0xa6]: "i32x4.trunc_f32x4_u_relaxed",
|
||||
I32x4TruncSatF64x2SZeroRelaxed : [0xfd, 0xc5]: "i32x4.trunc_f64x2_s_zero_relaxed",
|
||||
I32x4TruncSatF64x2UZeroRelaxed : [0xfd, 0xc6]: "i32x4.trunc_f64x2_u_zero_relaxed",
|
||||
F32x4FmaRelaxed : [0xfd, 0xaf]: "f32x4.fma_relaxed",
|
||||
F32x4FmsRelaxed : [0xfd, 0xb0]: "f32x4.fms_relaxed",
|
||||
F64x4FmaRelaxed : [0xfd, 0xcf]: "f64x2.fma_relaxed",
|
||||
F64x4FmsRelaxed : [0xfd, 0xd0]: "f64x2.fms_relaxed",
|
||||
I8x16RelaxedSwizzle : [0xfd, 0xa2]: "i8x16.relaxed_swizzle",
|
||||
I32x4RelaxedTruncSatF32x4S : [0xfd, 0xa5]: "i32x4.relaxed_trunc_f32x4_s",
|
||||
I32x4RelaxedTruncSatF32x4U : [0xfd, 0xa6]: "i32x4.relaxed_trunc_f32x4_u",
|
||||
I32x4RelaxedTruncSatF64x2SZero : [0xfd, 0xc5]: "i32x4.relaxed_trunc_f64x2_s_zero",
|
||||
I32x4RelaxedTruncSatF64x2UZero : [0xfd, 0xc6]: "i32x4.relaxed_trunc_f64x2_u_zero",
|
||||
F32x4Fma : [0xfd, 0xaf]: "f32x4.fma",
|
||||
F32x4Fms : [0xfd, 0xb0]: "f32x4.fms",
|
||||
F64x4Fma : [0xfd, 0xcf]: "f64x2.fma",
|
||||
F64x4Fms : [0xfd, 0xd0]: "f64x2.fms",
|
||||
I8x16LaneSelect : [0xfd, 0xb2]: "i8x16.laneselect",
|
||||
I16x8LaneSelect : [0xfd, 0xb3]: "i16x8.laneselect",
|
||||
I32x4LaneSelect : [0xfd, 0xd2]: "i32x4.laneselect",
|
||||
I64x2LaneSelect : [0xfd, 0xd3]: "i64x2.laneselect",
|
||||
F32x4MinRelaxed : [0xfd, 0xb4]: "f32x4.min_relaxed",
|
||||
F32x4MaxRelaxed : [0xfd, 0xe2]: "f32x4.max_relaxed",
|
||||
F64x2MinRelaxed : [0xfd, 0xd4]: "f64x2.min_relaxed",
|
||||
F64x2MaxRelaxed : [0xfd, 0xee]: "f64x2.max_relaxed",
|
||||
F32x4RelaxedMin : [0xfd, 0xb4]: "f32x4.relaxed_min",
|
||||
F32x4RelaxedMax : [0xfd, 0xe2]: "f32x4.relaxed_max",
|
||||
F64x2RelaxedMin : [0xfd, 0xd4]: "f64x2.relaxed_min",
|
||||
F64x2RelaxedMax : [0xfd, 0xee]: "f64x2.relaxed_max",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1154,9 +1157,9 @@ instructions! {
|
|||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct BlockType<'a> {
|
||||
pub label: Option<ast::Id<'a>>,
|
||||
pub label_name: Option<ast::NameAnnotation<'a>>,
|
||||
pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>,
|
||||
pub label: Option<Id<'a>>,
|
||||
pub label_name: Option<NameAnnotation<'a>>,
|
||||
pub ty: TypeUse<'a, FunctionType<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for BlockType<'a> {
|
||||
|
@ -1165,7 +1168,7 @@ impl<'a> Parse<'a> for BlockType<'a> {
|
|||
label: parser.parse()?,
|
||||
label_name: parser.parse()?,
|
||||
ty: parser
|
||||
.parse::<ast::TypeUse<'a, ast::FunctionTypeNoNames<'a>>>()?
|
||||
.parse::<TypeUse<'a, FunctionTypeNoNames<'a>>>()?
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
|
@ -1175,14 +1178,14 @@ impl<'a> Parse<'a> for BlockType<'a> {
|
|||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct FuncBindType<'a> {
|
||||
pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>,
|
||||
pub ty: TypeUse<'a, 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>>>()?
|
||||
.parse::<TypeUse<'a, FunctionTypeNoNames<'a>>>()?
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
|
@ -1193,14 +1196,14 @@ impl<'a> Parse<'a> for FuncBindType<'a> {
|
|||
#[allow(missing_docs)]
|
||||
pub struct LetType<'a> {
|
||||
pub block: BlockType<'a>,
|
||||
pub locals: Vec<ast::Local<'a>>,
|
||||
pub locals: Vec<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)?,
|
||||
locals: Local::parse_remainder(parser)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1209,15 +1212,15 @@ impl<'a> Parse<'a> for LetType<'a> {
|
|||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
pub struct BrTableIndices<'a> {
|
||||
pub labels: Vec<ast::Index<'a>>,
|
||||
pub default: ast::Index<'a>,
|
||||
pub labels: Vec<Index<'a>>,
|
||||
pub default: 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>() {
|
||||
while parser.peek::<Index>() {
|
||||
labels.push(parser.parse()?);
|
||||
}
|
||||
let default = labels.pop().unwrap();
|
||||
|
@ -1264,7 +1267,7 @@ pub struct MemArg<'a> {
|
|||
/// The offset, in bytes of this access.
|
||||
pub offset: u64,
|
||||
/// The memory index we're accessing
|
||||
pub memory: ast::ItemRef<'a, kw::memory>,
|
||||
pub memory: Index<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MemArg<'a> {
|
||||
|
@ -1287,8 +1290,8 @@ impl<'a> MemArg<'a> {
|
|||
return Ok((None, c));
|
||||
}
|
||||
let num = &kw[1..];
|
||||
let num = if num.starts_with("0x") {
|
||||
f(c, &num[2..], 16)?
|
||||
let num = if let Some(stripped) = num.strip_prefix("0x") {
|
||||
f(c, stripped, 16)?
|
||||
} else {
|
||||
f(c, num, 10)?
|
||||
};
|
||||
|
@ -1310,9 +1313,8 @@ impl<'a> MemArg<'a> {
|
|||
}
|
||||
|
||||
let memory = parser
|
||||
.parse::<Option<ast::IndexOrRef<'a, kw::memory>>>()?
|
||||
.map(|i| i.0)
|
||||
.unwrap_or(idx_zero(parser.prev_span(), kw::memory));
|
||||
.parse::<Option<_>>()?
|
||||
.unwrap_or(Index::Num(0, parser.prev_span()));
|
||||
let offset = parse_u64("offset", parser)?.unwrap_or(0);
|
||||
let align = match parse_u32("align", parser)? {
|
||||
Some(n) if !n.is_power_of_two() => {
|
||||
|
@ -1329,16 +1331,6 @@ impl<'a> MemArg<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extra data associated with the `loadN_lane` and `storeN_lane` instructions.
|
||||
#[derive(Debug)]
|
||||
pub struct LoadOrStoreLane<'a> {
|
||||
|
@ -1385,7 +1377,7 @@ impl<'a> LoadOrStoreLane<'a> {
|
|||
MemArg {
|
||||
align: default_align,
|
||||
offset: 0,
|
||||
memory: idx_zero(parser.prev_span(), kw::memory),
|
||||
memory: Index::Num(0, parser.prev_span()),
|
||||
}
|
||||
},
|
||||
lane: LaneArg::parse(parser)?,
|
||||
|
@ -1397,24 +1389,18 @@ impl<'a> LoadOrStoreLane<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct CallIndirect<'a> {
|
||||
/// The table that this call is going to be indexing.
|
||||
pub table: ast::ItemRef<'a, kw::table>,
|
||||
pub table: Index<'a>,
|
||||
/// Type type signature that this `call_indirect` instruction is using.
|
||||
pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>,
|
||||
pub ty: TypeUse<'a, 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()?;
|
||||
}
|
||||
let table: Option<_> = parser.parse()?;
|
||||
let ty = parser.parse::<TypeUse<'a, FunctionTypeNoNames<'a>>>()?;
|
||||
Ok(CallIndirect {
|
||||
table: table.map(|i| i.0).unwrap_or(idx_zero(prev_span, kw::table)),
|
||||
table: table.unwrap_or(Index::Num(0, prev_span)),
|
||||
ty: ty.into(),
|
||||
})
|
||||
}
|
||||
|
@ -1424,21 +1410,20 @@ impl<'a> Parse<'a> for CallIndirect<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct TableInit<'a> {
|
||||
/// The index of the table we're copying into.
|
||||
pub table: ast::ItemRef<'a, kw::table>,
|
||||
pub table: Index<'a>,
|
||||
/// The index of the element segment we're copying into a table.
|
||||
pub elem: ast::Index<'a>,
|
||||
pub elem: 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))
|
||||
};
|
||||
let (elem, table) = if parser.peek2::<Index>() {
|
||||
let table = parser.parse()?;
|
||||
(parser.parse()?, table)
|
||||
} else {
|
||||
(parser.parse()?, Index::Num(0, prev_span))
|
||||
};
|
||||
Ok(TableInit { table, elem })
|
||||
}
|
||||
}
|
||||
|
@ -1447,18 +1432,18 @@ impl<'a> Parse<'a> for TableInit<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct TableCopy<'a> {
|
||||
/// The index of the destination table to copy into.
|
||||
pub dst: ast::ItemRef<'a, kw::table>,
|
||||
pub dst: Index<'a>,
|
||||
/// The index of the source table to copy from.
|
||||
pub src: ast::ItemRef<'a, kw::table>,
|
||||
pub src: Index<'a>,
|
||||
}
|
||||
|
||||
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),
|
||||
let (dst, src) = match parser.parse::<Option<_>>()? {
|
||||
Some(dst) => (dst, parser.parse()?),
|
||||
None => (
|
||||
idx_zero(parser.prev_span(), kw::table),
|
||||
idx_zero(parser.prev_span(), kw::table),
|
||||
Index::Num(0, parser.prev_span()),
|
||||
Index::Num(0, parser.prev_span()),
|
||||
),
|
||||
};
|
||||
Ok(TableCopy { dst, src })
|
||||
|
@ -1469,15 +1454,15 @@ impl<'a> Parse<'a> for TableCopy<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct TableArg<'a> {
|
||||
/// The index of the table argument.
|
||||
pub dst: ast::ItemRef<'a, kw::table>,
|
||||
pub dst: Index<'a>,
|
||||
}
|
||||
|
||||
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
|
||||
let dst = if let Some(dst) = parser.parse()? {
|
||||
dst
|
||||
} else {
|
||||
idx_zero(parser.prev_span(), kw::table)
|
||||
Index::Num(0, parser.prev_span())
|
||||
};
|
||||
Ok(TableArg { dst })
|
||||
}
|
||||
|
@ -1487,15 +1472,15 @@ impl<'a> Parse<'a> for TableArg<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct MemoryArg<'a> {
|
||||
/// The index of the memory space.
|
||||
pub mem: ast::ItemRef<'a, kw::memory>,
|
||||
pub mem: Index<'a>,
|
||||
}
|
||||
|
||||
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
|
||||
let mem = if let Some(mem) = parser.parse()? {
|
||||
mem
|
||||
} else {
|
||||
idx_zero(parser.prev_span(), kw::memory)
|
||||
Index::Num(0, parser.prev_span())
|
||||
};
|
||||
Ok(MemoryArg { mem })
|
||||
}
|
||||
|
@ -1505,21 +1490,20 @@ impl<'a> Parse<'a> for MemoryArg<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct MemoryInit<'a> {
|
||||
/// The index of the data segment we're copying into memory.
|
||||
pub data: ast::Index<'a>,
|
||||
pub data: Index<'a>,
|
||||
/// The index of the memory we're copying into,
|
||||
pub mem: ast::ItemRef<'a, kw::memory>,
|
||||
pub mem: Index<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for MemoryInit<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
let prev_span = parser.prev_span();
|
||||
let (data, mem) =
|
||||
if parser.peek::<ast::ItemRef<kw::memory>>() || parser.peek2::<ast::Index>() {
|
||||
let memory = parser.parse::<ast::IndexOrRef<_>>()?.0;
|
||||
(parser.parse()?, memory)
|
||||
} else {
|
||||
(parser.parse()?, idx_zero(prev_span, kw::memory))
|
||||
};
|
||||
let (data, mem) = if parser.peek2::<Index>() {
|
||||
let memory = parser.parse()?;
|
||||
(parser.parse()?, memory)
|
||||
} else {
|
||||
(parser.parse()?, Index::Num(0, prev_span))
|
||||
};
|
||||
Ok(MemoryInit { data, mem })
|
||||
}
|
||||
}
|
||||
|
@ -1528,18 +1512,18 @@ impl<'a> Parse<'a> for MemoryInit<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct MemoryCopy<'a> {
|
||||
/// The index of the memory we're copying from.
|
||||
pub src: ast::ItemRef<'a, kw::memory>,
|
||||
pub src: Index<'a>,
|
||||
/// The index of the memory we're copying to.
|
||||
pub dst: ast::ItemRef<'a, kw::memory>,
|
||||
pub dst: Index<'a>,
|
||||
}
|
||||
|
||||
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),
|
||||
let (src, dst) = match parser.parse()? {
|
||||
Some(dst) => (parser.parse()?, dst),
|
||||
None => (
|
||||
idx_zero(parser.prev_span(), kw::memory),
|
||||
idx_zero(parser.prev_span(), kw::memory),
|
||||
Index::Num(0, parser.prev_span()),
|
||||
Index::Num(0, parser.prev_span()),
|
||||
),
|
||||
};
|
||||
Ok(MemoryCopy { src, dst })
|
||||
|
@ -1550,9 +1534,9 @@ impl<'a> Parse<'a> for MemoryCopy<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct StructAccess<'a> {
|
||||
/// The index of the struct type we're accessing.
|
||||
pub r#struct: ast::Index<'a>,
|
||||
pub r#struct: Index<'a>,
|
||||
/// The index of the field of the struct we're accessing
|
||||
pub field: ast::Index<'a>,
|
||||
pub field: Index<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for StructAccess<'a> {
|
||||
|
@ -1573,8 +1557,8 @@ pub enum V128Const {
|
|||
I16x8([i16; 8]),
|
||||
I32x4([i32; 4]),
|
||||
I64x2([i64; 2]),
|
||||
F32x4([ast::Float32; 4]),
|
||||
F64x2([ast::Float64; 2]),
|
||||
F32x4([Float32; 4]),
|
||||
F64x2([Float64; 2]),
|
||||
}
|
||||
|
||||
impl V128Const {
|
||||
|
@ -1767,7 +1751,7 @@ impl<'a> Parse<'a> for I8x16Shuffle {
|
|||
#[derive(Debug)]
|
||||
pub struct SelectTypes<'a> {
|
||||
#[allow(missing_docs)]
|
||||
pub tys: Option<Vec<ast::ValType<'a>>>,
|
||||
pub tys: Option<Vec<ValType<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for SelectTypes<'a> {
|
|
@ -1,5 +1,7 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::core::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// A WebAssembly function to be inserted into a module.
|
||||
///
|
||||
|
@ -7,20 +9,20 @@ use crate::parser::{Parse, Parser, Result};
|
|||
#[derive(Debug)]
|
||||
pub struct Func<'a> {
|
||||
/// Where this `func` was defined.
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An identifier that this function is resolved with (optionally) for name
|
||||
/// resolution.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: ast::InlineExport<'a>,
|
||||
pub exports: 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>>,
|
||||
pub ty: TypeUse<'a, FunctionType<'a>>,
|
||||
}
|
||||
|
||||
/// Possible ways to define a function in the text format.
|
||||
|
@ -31,7 +33,7 @@ pub enum FuncKind<'a> {
|
|||
/// ```text
|
||||
/// (func (type 3) (import "foo" "bar"))
|
||||
/// ```
|
||||
Import(ast::InlineImport<'a>),
|
||||
Import(InlineImport<'a>),
|
||||
|
||||
/// Almost all functions, those defined inline in a wasm module.
|
||||
Inline {
|
||||
|
@ -39,7 +41,7 @@ pub enum FuncKind<'a> {
|
|||
locals: Vec<Local<'a>>,
|
||||
|
||||
/// The instructions of the function.
|
||||
expression: ast::Expression<'a>,
|
||||
expression: Expression<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -83,11 +85,11 @@ impl<'a> Parse<'a> for Func<'a> {
|
|||
pub struct Local<'a> {
|
||||
/// An identifier that this local is resolved with (optionally) for name
|
||||
/// resolution.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this local stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// The value type of this local.
|
||||
pub ty: ast::ValType<'a>,
|
||||
pub ty: ValType<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Local<'a> {
|
||||
|
@ -105,7 +107,11 @@ impl<'a> Local<'a> {
|
|||
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()? });
|
||||
locals.push(Local {
|
||||
id: None,
|
||||
name: None,
|
||||
ty: p.parse()?,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
|
@ -1,20 +1,22 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::core::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// A WebAssembly global in a module
|
||||
#[derive(Debug)]
|
||||
pub struct Global<'a> {
|
||||
/// Where this `global` was defined.
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An optional name to reference this global by
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: ast::InlineExport<'a>,
|
||||
pub exports: InlineExport<'a>,
|
||||
/// The type of this global, both its value type and whether it's mutable.
|
||||
pub ty: ast::GlobalType<'a>,
|
||||
pub ty: GlobalType<'a>,
|
||||
/// What kind of global this defined as.
|
||||
pub kind: GlobalKind<'a>,
|
||||
}
|
||||
|
@ -27,10 +29,10 @@ pub enum GlobalKind<'a> {
|
|||
/// ```text
|
||||
/// (global i32 (import "foo" "bar"))
|
||||
/// ```
|
||||
Import(ast::InlineImport<'a>),
|
||||
Import(InlineImport<'a>),
|
||||
|
||||
/// A global defined inline in the module itself
|
||||
Inline(ast::Expression<'a>),
|
||||
Inline(Expression<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Global<'a> {
|
|
@ -1,15 +1,17 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::core::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// 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,
|
||||
pub span: 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>,
|
||||
pub field: &'a str,
|
||||
/// The item that's being imported.
|
||||
pub item: ItemSig<'a>,
|
||||
}
|
||||
|
@ -33,13 +35,13 @@ impl<'a> Parse<'a> for Import<'a> {
|
|||
#[allow(missing_docs)]
|
||||
pub struct ItemSig<'a> {
|
||||
/// Where this item is defined in the source.
|
||||
pub span: ast::Span,
|
||||
pub span: 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>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name which, for functions, will be stored in the
|
||||
/// custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// What kind of item this is.
|
||||
pub kind: ItemKind<'a>,
|
||||
}
|
||||
|
@ -47,13 +49,11 @@ pub struct ItemSig<'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>),
|
||||
Tag(ast::TagType<'a>),
|
||||
Module(ast::TypeUse<'a, ast::ModuleType<'a>>),
|
||||
Instance(ast::TypeUse<'a, ast::InstanceType<'a>>),
|
||||
Func(TypeUse<'a, FunctionType<'a>>),
|
||||
Table(TableType<'a>),
|
||||
Memory(MemoryType),
|
||||
Global(GlobalType<'a>),
|
||||
Tag(TagType<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ItemSig<'a> {
|
||||
|
@ -99,22 +99,6 @@ impl<'a> Parse<'a> for ItemSig<'a> {
|
|||
name: None,
|
||||
kind: ItemKind::Tag(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())
|
||||
}
|
||||
|
@ -131,7 +115,7 @@ impl<'a> Parse<'a> for ItemSig<'a> {
|
|||
#[allow(missing_docs)]
|
||||
pub struct InlineImport<'a> {
|
||||
pub module: &'a str,
|
||||
pub field: Option<&'a str>,
|
||||
pub field: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for InlineImport<'a> {
|
||||
|
@ -160,11 +144,9 @@ impl Peek for InlineImport<'_> {
|
|||
Some((_, cursor)) => cursor,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// optional field
|
||||
let cursor = match cursor.string() {
|
||||
Some((_, cursor)) => cursor,
|
||||
None => cursor,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
cursor.rparen().is_some()
|
|
@ -1,18 +1,20 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::core::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Lookahead1, Parse, Parser, Peek, Result};
|
||||
use crate::token::*;
|
||||
|
||||
/// A defined WebAssembly memory instance inside of a module.
|
||||
#[derive(Debug)]
|
||||
pub struct Memory<'a> {
|
||||
/// Where this `memory` was defined
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An optional name to refer to this memory by.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: ast::InlineExport<'a>,
|
||||
pub exports: InlineExport<'a>,
|
||||
/// How this memory is defined in the module.
|
||||
pub kind: MemoryKind<'a>,
|
||||
}
|
||||
|
@ -23,12 +25,12 @@ pub enum MemoryKind<'a> {
|
|||
/// This memory is actually an inlined import definition.
|
||||
#[allow(missing_docs)]
|
||||
Import {
|
||||
import: ast::InlineImport<'a>,
|
||||
ty: ast::MemoryType,
|
||||
import: InlineImport<'a>,
|
||||
ty: MemoryType,
|
||||
},
|
||||
|
||||
/// A typical memory definition which simply says the limits of the memory
|
||||
Normal(ast::MemoryType),
|
||||
Normal(MemoryType),
|
||||
|
||||
/// The data of this memory, starting from 0, explicitly listed
|
||||
Inline {
|
||||
|
@ -57,7 +59,7 @@ impl<'a> Parse<'a> for Memory<'a> {
|
|||
import,
|
||||
ty: parser.parse()?,
|
||||
}
|
||||
} else if l.peek::<ast::LParen>() || parser.peek2::<ast::LParen>() {
|
||||
} else if l.peek::<LParen>() || parser.peek2::<LParen>() {
|
||||
let is_32 = if parser.parse::<Option<kw::i32>>()?.is_some() {
|
||||
true
|
||||
} else if parser.parse::<Option<kw::i64>>()?.is_some() {
|
||||
|
@ -93,13 +95,13 @@ impl<'a> Parse<'a> for Memory<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct Data<'a> {
|
||||
/// Where this `data` was defined
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
|
||||
/// The optional name of this data segment
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
|
||||
/// An optional name for this data stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
|
||||
/// Whether this data segment is passive or active
|
||||
pub kind: DataKind<'a>,
|
||||
|
@ -120,10 +122,10 @@ pub enum DataKind<'a> {
|
|||
/// memory on module instantiation.
|
||||
Active {
|
||||
/// The memory that this `Data` will be associated with.
|
||||
memory: ast::ItemRef<'a, kw::memory>,
|
||||
memory: Index<'a>,
|
||||
|
||||
/// Initial offset to load this data segment at
|
||||
offset: ast::Expression<'a>,
|
||||
offset: Expression<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -133,29 +135,25 @@ impl<'a> Parse<'a> for Data<'a> {
|
|||
let id = parser.parse()?;
|
||||
let name = 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]>() {
|
||||
let kind = 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
|
||||
let memory = if parser.peek::<u32>() {
|
||||
// FIXME: this is only here to accomodate
|
||||
// proposals/threads/imports.wast at this current moment in
|
||||
// time, this probably should get removed when the threads
|
||||
// proposal is rebased on the current spec.
|
||||
Index::Num(parser.parse()?, span)
|
||||
} else if parser.peek2::<kw::memory>() {
|
||||
parser.parens(|p| {
|
||||
p.parse::<kw::memory>()?;
|
||||
p.parse()
|
||||
})?
|
||||
} else {
|
||||
ast::ItemRef::Item {
|
||||
kind: kw::memory(parser.prev_span()),
|
||||
idx: ast::Index::Num(0, span),
|
||||
exports: Vec::new(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: false,
|
||||
}
|
||||
Index::Num(0, span)
|
||||
};
|
||||
let offset = parser.parens(|parser| {
|
||||
if parser.peek::<kw::offset>() {
|
||||
|
@ -167,7 +165,7 @@ impl<'a> Parse<'a> for Data<'a> {
|
|||
// single-instruction expression.
|
||||
let insn = parser.parse()?;
|
||||
if parser.is_empty() {
|
||||
return Ok(ast::Expression {
|
||||
return Ok(Expression {
|
||||
instrs: [insn].into(),
|
||||
});
|
||||
}
|
||||
|
@ -183,10 +181,10 @@ impl<'a> Parse<'a> for Data<'a> {
|
|||
// (data (offset ...))
|
||||
//
|
||||
// but alas
|
||||
let expr: ast::Expression = parser.parse()?;
|
||||
let expr: Expression = parser.parse()?;
|
||||
let mut instrs = Vec::from(expr.instrs);
|
||||
instrs.push(insn);
|
||||
Ok(ast::Expression {
|
||||
Ok(Expression {
|
||||
instrs: instrs.into(),
|
||||
})
|
||||
}
|
||||
|
@ -237,7 +235,7 @@ impl DataVal<'_> {
|
|||
|
||||
impl<'a> Parse<'a> for DataVal<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if !parser.peek::<ast::LParen>() {
|
||||
if !parser.peek::<LParen>() {
|
||||
return Ok(DataVal::String(parser.parse()?));
|
||||
}
|
||||
|
||||
|
@ -250,15 +248,9 @@ impl<'a> Parse<'a> for DataVal<'a> {
|
|||
|| 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())
|
||||
})?
|
||||
|| consume::<kw::f32, Float32, _>(p, l, r, |u, v| v.extend(&u.bits.to_le_bytes()))?
|
||||
|| consume::<kw::f64, Float64, _>(p, l, r, |u, v| v.extend(&u.bits.to_le_bytes()))?
|
||||
|| consume::<kw::v128, V128Const, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
|
||||
{
|
||||
Ok(DataVal::Integral(result))
|
||||
} else {
|
|
@ -0,0 +1,27 @@
|
|||
//! Types and support for parsing the core wasm text format.
|
||||
|
||||
mod custom;
|
||||
mod export;
|
||||
mod expr;
|
||||
mod func;
|
||||
mod global;
|
||||
mod import;
|
||||
mod memory;
|
||||
mod module;
|
||||
mod table;
|
||||
mod tag;
|
||||
mod types;
|
||||
pub use self::custom::*;
|
||||
pub use self::export::*;
|
||||
pub use self::expr::*;
|
||||
pub use self::func::*;
|
||||
pub use self::global::*;
|
||||
pub use self::import::*;
|
||||
pub use self::memory::*;
|
||||
pub use self::module::*;
|
||||
pub use self::table::*;
|
||||
pub use self::tag::*;
|
||||
pub use self::types::*;
|
||||
|
||||
pub(crate) mod binary;
|
||||
pub(crate) mod resolve;
|
|
@ -1,50 +1,19 @@
|
|||
use crate::ast::{self, annotation, kw};
|
||||
use crate::core::*;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, Index, NameAnnotation, Span};
|
||||
use crate::{annotation, kw};
|
||||
|
||||
pub use crate::resolve::Names;
|
||||
pub use crate::core::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.
|
||||
#[derive(Debug)]
|
||||
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.
|
||||
/// A parsed WebAssembly core module.
|
||||
#[derive(Debug)]
|
||||
pub struct Module<'a> {
|
||||
/// Where this `module` was defined
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An optional identifier this module is known by
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional `@name` annotation for this module
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// What kind of module this was parsed as.
|
||||
pub kind: ModuleKind<'a>,
|
||||
}
|
||||
|
@ -71,9 +40,9 @@ impl<'a> Module<'a> {
|
|||
/// 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.
|
||||
/// [`Index`](crate::token::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
|
||||
|
@ -84,7 +53,11 @@ impl<'a> Module<'a> {
|
|||
/// 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)
|
||||
let names = match &mut self.kind {
|
||||
ModuleKind::Text(fields) => crate::core::resolve::resolve(fields)?,
|
||||
ModuleKind::Binary(_blobs) => Default::default(),
|
||||
};
|
||||
Ok(names)
|
||||
}
|
||||
|
||||
/// Encodes this [`Module`] to its binary form.
|
||||
|
@ -113,10 +86,13 @@ impl<'a> Module<'a> {
|
|||
/// expansion-related errors.
|
||||
pub fn encode(&mut self) -> std::result::Result<Vec<u8>, crate::Error> {
|
||||
self.resolve()?;
|
||||
Ok(crate::binary::encode(self))
|
||||
Ok(match &self.kind {
|
||||
ModuleKind::Text(fields) => crate::core::binary::encode(&self.id, &self.name, fields),
|
||||
ModuleKind::Binary(blobs) => blobs.iter().flat_map(|b| b.iter().cloned()).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
fn validate(&self, parser: Parser<'_>) -> Result<()> {
|
||||
pub(crate) fn validate(&self, parser: Parser<'_>) -> Result<()> {
|
||||
let mut starts = 0;
|
||||
if let ModuleKind::Text(fields) = &self.kind {
|
||||
for item in fields.iter() {
|
||||
|
@ -162,25 +138,22 @@ impl<'a> Parse<'a> for Module<'a> {
|
|||
#[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>),
|
||||
Tag(ast::Tag<'a>),
|
||||
Custom(ast::Custom<'a>),
|
||||
Instance(ast::Instance<'a>),
|
||||
NestedModule(ast::NestedModule<'a>),
|
||||
Alias(ast::Alias<'a>),
|
||||
Type(Type<'a>),
|
||||
Import(Import<'a>),
|
||||
Func(Func<'a>),
|
||||
Table(Table<'a>),
|
||||
Memory(Memory<'a>),
|
||||
Global(Global<'a>),
|
||||
Export(Export<'a>),
|
||||
Start(Index<'a>),
|
||||
Elem(Elem<'a>),
|
||||
Data(Data<'a>),
|
||||
Tag(Tag<'a>),
|
||||
Custom(Custom<'a>),
|
||||
}
|
||||
|
||||
impl<'a> ModuleField<'a> {
|
||||
fn parse_remaining(parser: Parser<'a>) -> Result<Vec<ModuleField>> {
|
||||
pub(crate) fn parse_remaining(parser: Parser<'a>) -> Result<Vec<ModuleField>> {
|
||||
let mut fields = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
fields.push(parser.parens(ModuleField::parse)?);
|
||||
|
@ -214,7 +187,7 @@ impl<'a> Parse<'a> for ModuleField<'a> {
|
|||
}
|
||||
if parser.peek::<kw::start>() {
|
||||
parser.parse::<kw::start>()?;
|
||||
return Ok(ModuleField::Start(parser.parse::<ast::IndexOrRef<_>>()?.0));
|
||||
return Ok(ModuleField::Start(parser.parse()?));
|
||||
}
|
||||
if parser.peek::<kw::elem>() {
|
||||
return Ok(ModuleField::Elem(parser.parse()?));
|
||||
|
@ -228,15 +201,6 @@ impl<'a> Parse<'a> for ModuleField<'a> {
|
|||
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"))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use crate::ast::*;
|
||||
use crate::resolve::gensym;
|
||||
use crate::core::*;
|
||||
use crate::gensym;
|
||||
use crate::token::{Id, Index, Span};
|
||||
use std::mem;
|
||||
|
||||
pub fn run(fields: &mut Vec<ModuleField>) {
|
||||
|
@ -80,7 +81,7 @@ pub fn run(fields: &mut Vec<ModuleField>) {
|
|||
id: None,
|
||||
name: None,
|
||||
kind: DataKind::Active {
|
||||
memory: item_ref(kw::memory(m.span), id),
|
||||
memory: Index::Id(id),
|
||||
offset: Expression {
|
||||
instrs: Box::new([if is_32 {
|
||||
Instruction::I32Const(0)
|
||||
|
@ -139,7 +140,7 @@ pub fn run(fields: &mut Vec<ModuleField>) {
|
|||
id: None,
|
||||
name: None,
|
||||
kind: ElemKind::Active {
|
||||
table: item_ref(kw::table(t.span), id),
|
||||
table: Index::Id(id),
|
||||
offset: Expression {
|
||||
instrs: Box::new([Instruction::I32Const(0)]),
|
||||
},
|
||||
|
@ -196,62 +197,9 @@ pub fn run(fields: &mut Vec<ModuleField>) {
|
|||
}
|
||||
}
|
||||
|
||||
ModuleField::Instance(i) => {
|
||||
for name in i.exports.names.drain(..) {
|
||||
to_append.push(export(i.span, name, ExportKind::Instance, &mut i.id));
|
||||
}
|
||||
match &mut i.kind {
|
||||
InstanceKind::Import { import, ty } => {
|
||||
*item = ModuleField::Import(Import {
|
||||
span: i.span,
|
||||
module: import.module,
|
||||
field: import.field,
|
||||
item: ItemSig {
|
||||
span: i.span,
|
||||
id: i.id,
|
||||
name: None,
|
||||
kind: ItemKind::Instance(mem::replace(
|
||||
ty,
|
||||
TypeUse::new_with_index(Index::Num(0, Span::from_offset(0))),
|
||||
)),
|
||||
},
|
||||
});
|
||||
}
|
||||
InstanceKind::Inline { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::NestedModule(m) => {
|
||||
for name in m.exports.names.drain(..) {
|
||||
to_append.push(export(m.span, name, ExportKind::Module, &mut m.id));
|
||||
}
|
||||
match &mut m.kind {
|
||||
NestedModuleKind::Import { import, ty } => {
|
||||
*item = ModuleField::Import(Import {
|
||||
span: m.span,
|
||||
module: import.module,
|
||||
field: import.field,
|
||||
item: ItemSig {
|
||||
span: m.span,
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
kind: ItemKind::Module(mem::replace(
|
||||
ty,
|
||||
TypeUse::new_with_index(Index::Num(0, Span::from_offset(0))),
|
||||
)),
|
||||
},
|
||||
});
|
||||
}
|
||||
NestedModuleKind::Inline { fields, .. } => {
|
||||
run(fields);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ModuleField::Import(_)
|
||||
| ModuleField::Type(_)
|
||||
| ModuleField::Export(_)
|
||||
| ModuleField::Alias(_)
|
||||
| ModuleField::Start(_)
|
||||
| ModuleField::Elem(_)
|
||||
| ModuleField::Data(_)
|
||||
|
@ -279,16 +227,7 @@ fn export<'a>(
|
|||
ModuleField::Export(Export {
|
||||
span,
|
||||
name,
|
||||
index: item_ref(kind, id),
|
||||
kind,
|
||||
item: Index::Id(id),
|
||||
})
|
||||
}
|
||||
|
||||
fn item_ref<'a, K>(kind: K, id: impl Into<Index<'a>>) -> ItemRef<'a, K> {
|
||||
ItemRef::Item {
|
||||
kind,
|
||||
idx: id.into(),
|
||||
exports: Vec::new(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: false,
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
use crate::ast::*;
|
||||
use crate::Error;
|
||||
use crate::core::*;
|
||||
use crate::token::Index;
|
||||
use crate::{gensym, Error};
|
||||
|
||||
mod aliases;
|
||||
mod deinline_import_export;
|
||||
mod gensym;
|
||||
mod names;
|
||||
mod types;
|
||||
pub(crate) mod types;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
pub enum Ns {
|
||||
|
@ -13,33 +12,11 @@ pub enum Ns {
|
|||
Table,
|
||||
Global,
|
||||
Memory,
|
||||
Module,
|
||||
Instance,
|
||||
Tag,
|
||||
Type,
|
||||
}
|
||||
|
||||
impl Ns {
|
||||
fn from_export(kind: &ExportKind) -> Ns {
|
||||
match kind {
|
||||
ExportKind::Func => Ns::Func,
|
||||
ExportKind::Table => Ns::Table,
|
||||
ExportKind::Global => Ns::Global,
|
||||
ExportKind::Memory => Ns::Memory,
|
||||
ExportKind::Instance => Ns::Instance,
|
||||
ExportKind::Module => Ns::Module,
|
||||
ExportKind::Tag => Ns::Tag,
|
||||
ExportKind::Type => Ns::Type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve<'a>(module: &mut Module<'a>) -> Result<Names<'a>, Error> {
|
||||
let fields = match &mut module.kind {
|
||||
ModuleKind::Text(fields) => fields,
|
||||
_ => return Ok(Default::default()),
|
||||
};
|
||||
|
||||
pub fn resolve<'a>(fields: &mut Vec<ModuleField<'a>>) -> Result<Names<'a>, Error> {
|
||||
// Ensure that each resolution of a module is deterministic in the names
|
||||
// that it generates by resetting our thread-local symbol generator.
|
||||
gensym::reset();
|
||||
|
@ -51,8 +28,6 @@ pub fn resolve<'a>(module: &mut Module<'a>) -> Result<Names<'a>, Error> {
|
|||
// field.
|
||||
deinline_import_export::run(fields);
|
||||
|
||||
aliases::run(fields);
|
||||
|
||||
// With a canonical form of imports make sure that imports are all listed
|
||||
// first.
|
||||
let mut last = None;
|
||||
|
@ -77,15 +52,15 @@ pub fn resolve<'a>(module: &mut Module<'a>) -> Result<Names<'a>, Error> {
|
|||
|
||||
// Perform name resolution over all `Index` items to resolve them all to
|
||||
// indices instead of symbolic names.
|
||||
let resolver = names::resolve(module.id, fields)?;
|
||||
let resolver = names::resolve(fields)?;
|
||||
Ok(Names { resolver })
|
||||
}
|
||||
|
||||
/// Representation of the results of name resolution for a module.
|
||||
///
|
||||
/// This structure is returned from the
|
||||
/// [`Module::resolve`](crate::Module::resolve) function and can be used to
|
||||
/// resolve your own name arguments if you have any.
|
||||
/// [`Module::resolve`](crate::core::Module::resolve) function and can be used
|
||||
/// to resolve your own name arguments if you have any.
|
||||
#[derive(Default)]
|
||||
pub struct Names<'a> {
|
||||
resolver: names::Resolver<'a>,
|
|
@ -1,21 +1,12 @@
|
|||
use crate::ast::*;
|
||||
use crate::resolve::Ns;
|
||||
use crate::core::resolve::Ns;
|
||||
use crate::core::*;
|
||||
use crate::names::{resolve_error, Namespace};
|
||||
use crate::token::{Id, Index};
|
||||
use crate::Error;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub fn resolve<'a>(
|
||||
id: Option<Id<'a>>,
|
||||
fields: &mut Vec<ModuleField<'a>>,
|
||||
) -> Result<Resolver<'a>, Error> {
|
||||
let mut names = HashMap::new();
|
||||
let mut parents = Parents {
|
||||
prev: None,
|
||||
cur_id: id,
|
||||
depth: 0,
|
||||
names: &mut names,
|
||||
};
|
||||
pub fn resolve<'a>(fields: &mut Vec<ModuleField<'a>>) -> Result<Resolver<'a>, Error> {
|
||||
let mut resolver = Resolver::default();
|
||||
resolver.process(&mut parents, fields)?;
|
||||
resolver.process(fields)?;
|
||||
Ok(resolver)
|
||||
}
|
||||
|
||||
|
@ -32,21 +23,14 @@ pub struct Resolver<'a> {
|
|||
memories: Namespace<'a>,
|
||||
types: Namespace<'a>,
|
||||
tags: Namespace<'a>,
|
||||
modules: Namespace<'a>,
|
||||
instances: Namespace<'a>,
|
||||
datas: Namespace<'a>,
|
||||
elems: Namespace<'a>,
|
||||
fields: Namespace<'a>,
|
||||
type_info: Vec<TypeInfo<'a>>,
|
||||
implicit_instances: HashSet<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
fn process(
|
||||
&mut self,
|
||||
parents: &mut Parents<'a, '_>,
|
||||
fields: &mut Vec<ModuleField<'a>>,
|
||||
) -> Result<(), Error> {
|
||||
fn process(&mut self, fields: &mut Vec<ModuleField<'a>>) -> Result<(), Error> {
|
||||
// Number everything in the module, recording what names correspond to
|
||||
// what indices.
|
||||
for field in fields.iter_mut() {
|
||||
|
@ -56,37 +40,24 @@ impl<'a> Resolver<'a> {
|
|||
// Then we can replace all our `Index::Id` instances with `Index::Num`
|
||||
// in the AST. Note that this also recurses into nested modules.
|
||||
for field in fields.iter_mut() {
|
||||
self.resolve_field(field, parents)?;
|
||||
self.resolve_field(field)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register(&mut self, item: &ModuleField<'a>) -> Result<(), Error> {
|
||||
match item {
|
||||
ModuleField::Import(i) => {
|
||||
// Account for implicit instances created by two-level imports
|
||||
// first. At this time they never have a name.
|
||||
if i.field.is_some() {
|
||||
if self.implicit_instances.insert(i.module) {
|
||||
self.instances.register(None, "instance")?;
|
||||
}
|
||||
}
|
||||
match &i.item.kind {
|
||||
ItemKind::Func(_) => self.funcs.register(i.item.id, "func")?,
|
||||
ItemKind::Memory(_) => self.memories.register(i.item.id, "memory")?,
|
||||
ItemKind::Table(_) => self.tables.register(i.item.id, "table")?,
|
||||
ItemKind::Global(_) => self.globals.register(i.item.id, "global")?,
|
||||
ItemKind::Tag(_) => self.tags.register(i.item.id, "tag")?,
|
||||
ItemKind::Module(_) => self.modules.register(i.item.id, "module")?,
|
||||
ItemKind::Instance(_) => self.instances.register(i.item.id, "instance")?,
|
||||
}
|
||||
}
|
||||
ModuleField::Import(i) => match &i.item.kind {
|
||||
ItemKind::Func(_) => self.funcs.register(i.item.id, "func")?,
|
||||
ItemKind::Memory(_) => self.memories.register(i.item.id, "memory")?,
|
||||
ItemKind::Table(_) => self.tables.register(i.item.id, "table")?,
|
||||
ItemKind::Global(_) => self.globals.register(i.item.id, "global")?,
|
||||
ItemKind::Tag(_) => self.tags.register(i.item.id, "tag")?,
|
||||
},
|
||||
ModuleField::Global(i) => self.globals.register(i.id, "global")?,
|
||||
ModuleField::Memory(i) => self.memories.register(i.id, "memory")?,
|
||||
ModuleField::Func(i) => self.funcs.register(i.id, "func")?,
|
||||
ModuleField::Table(i) => self.tables.register(i.id, "table")?,
|
||||
ModuleField::NestedModule(m) => self.modules.register(m.id, "module")?,
|
||||
ModuleField::Instance(i) => self.instances.register(i.id, "instance")?,
|
||||
|
||||
ModuleField::Type(i) => {
|
||||
match &i.def {
|
||||
|
@ -103,10 +74,7 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
TypeDef::Instance(_)
|
||||
| TypeDef::Array(_)
|
||||
| TypeDef::Func(_)
|
||||
| TypeDef::Module(_) => {}
|
||||
TypeDef::Array(_) | TypeDef::Func(_) => {}
|
||||
}
|
||||
|
||||
// Record function signatures as we see them to so we can
|
||||
|
@ -126,19 +94,6 @@ impl<'a> Resolver<'a> {
|
|||
ModuleField::Elem(e) => self.elems.register(e.id, "elem")?,
|
||||
ModuleField::Data(d) => self.datas.register(d.id, "data")?,
|
||||
ModuleField::Tag(t) => self.tags.register(t.id, "tag")?,
|
||||
ModuleField::Alias(a) => match a.kind {
|
||||
ExportKind::Func => self.funcs.register(a.id, "func")?,
|
||||
ExportKind::Table => self.tables.register(a.id, "table")?,
|
||||
ExportKind::Memory => self.memories.register(a.id, "memory")?,
|
||||
ExportKind::Global => self.globals.register(a.id, "global")?,
|
||||
ExportKind::Instance => self.instances.register(a.id, "instance")?,
|
||||
ExportKind::Module => self.modules.register(a.id, "module")?,
|
||||
ExportKind::Tag => self.tags.register(a.id, "tag")?,
|
||||
ExportKind::Type => {
|
||||
self.type_info.push(TypeInfo::Other);
|
||||
self.types.register(a.id, "type")?
|
||||
}
|
||||
},
|
||||
|
||||
// These fields don't define any items in any index space.
|
||||
ModuleField::Export(_) | ModuleField::Start(_) | ModuleField::Custom(_) => {
|
||||
|
@ -149,11 +104,7 @@ impl<'a> Resolver<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_field(
|
||||
&self,
|
||||
field: &mut ModuleField<'a>,
|
||||
parents: &mut Parents<'a, '_>,
|
||||
) -> Result<(), Error> {
|
||||
fn resolve_field(&self, field: &mut ModuleField<'a>) -> Result<(), Error> {
|
||||
match field {
|
||||
ModuleField::Import(i) => {
|
||||
self.resolve_item_sig(&mut i.item)?;
|
||||
|
@ -169,8 +120,6 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
}
|
||||
TypeDef::Array(array) => self.resolve_storagetype(&mut array.ty)?,
|
||||
TypeDef::Module(m) => m.resolve(self)?,
|
||||
TypeDef::Instance(i) => i.resolve(self)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -225,7 +174,7 @@ impl<'a> Resolver<'a> {
|
|||
ModuleField::Elem(e) => {
|
||||
match &mut e.kind {
|
||||
ElemKind::Active { table, offset } => {
|
||||
self.resolve_item_ref(table)?;
|
||||
self.resolve(table, Ns::Table)?;
|
||||
self.resolve_expr(offset)?;
|
||||
}
|
||||
ElemKind::Passive { .. } | ElemKind::Declared { .. } => {}
|
||||
|
@ -233,7 +182,7 @@ impl<'a> Resolver<'a> {
|
|||
match &mut e.payload {
|
||||
ElemPayload::Indices(elems) => {
|
||||
for idx in elems {
|
||||
self.resolve_item_ref(idx)?;
|
||||
self.resolve(idx, Ns::Func)?;
|
||||
}
|
||||
}
|
||||
ElemPayload::Exprs { exprs, ty } => {
|
||||
|
@ -248,19 +197,29 @@ impl<'a> Resolver<'a> {
|
|||
|
||||
ModuleField::Data(d) => {
|
||||
if let DataKind::Active { memory, offset } = &mut d.kind {
|
||||
self.resolve_item_ref(memory)?;
|
||||
self.resolve(memory, Ns::Memory)?;
|
||||
self.resolve_expr(offset)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ModuleField::Start(i) => {
|
||||
self.resolve_item_ref(i)?;
|
||||
self.resolve(i, Ns::Func)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ModuleField::Export(e) => {
|
||||
self.resolve_item_ref(&mut e.index)?;
|
||||
self.resolve(
|
||||
&mut e.item,
|
||||
match e.kind {
|
||||
ExportKind::Func => Ns::Func,
|
||||
ExportKind::Table => Ns::Table,
|
||||
ExportKind::Memory => Ns::Memory,
|
||||
ExportKind::Global => Ns::Global,
|
||||
ExportKind::Tag => Ns::Tag,
|
||||
ExportKind::Type => Ns::Type,
|
||||
},
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -281,25 +240,6 @@ impl<'a> Resolver<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
ModuleField::Instance(i) => {
|
||||
if let InstanceKind::Inline { module, args } = &mut i.kind {
|
||||
self.resolve_item_ref(module)?;
|
||||
for arg in args {
|
||||
self.resolve_item_ref(&mut arg.index)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ModuleField::NestedModule(m) => {
|
||||
let fields = match &mut m.kind {
|
||||
NestedModuleKind::Inline { fields } => fields,
|
||||
NestedModuleKind::Import { .. } => panic!("should only be inline"),
|
||||
};
|
||||
Resolver::default().process(&mut parents.push(self, m.id), fields)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ModuleField::Table(t) => {
|
||||
if let TableKind::Normal(t) = &mut t.kind {
|
||||
self.resolve_heaptype(&mut t.elem.heap)?;
|
||||
|
@ -307,29 +247,6 @@ impl<'a> Resolver<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
ModuleField::Alias(a) => {
|
||||
match &mut a.source {
|
||||
AliasSource::InstanceExport { instance, .. } => {
|
||||
self.resolve_item_ref(instance)?;
|
||||
}
|
||||
AliasSource::Outer { module, index } => {
|
||||
match (index, module) {
|
||||
// If both indices are numeric then don't try to
|
||||
// resolve anything since we could fail to walk up
|
||||
// the parent chain, producing a wat2wasm error that
|
||||
// should probably be a wasm validation error.
|
||||
(Index::Num(..), Index::Num(..)) => {}
|
||||
(index, module) => {
|
||||
parents
|
||||
.resolve(module)?
|
||||
.resolve(index, Ns::from_export(&a.kind))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ModuleField::Memory(_) | ModuleField::Custom(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
@ -369,12 +286,6 @@ impl<'a> Resolver<'a> {
|
|||
self.resolve_type_use(t)?;
|
||||
}
|
||||
ItemKind::Global(t) => self.resolve_valtype(&mut t.ty)?,
|
||||
ItemKind::Instance(t) => {
|
||||
self.resolve_type_use(t)?;
|
||||
}
|
||||
ItemKind::Module(m) => {
|
||||
self.resolve_type_use(m)?;
|
||||
}
|
||||
ItemKind::Table(t) => {
|
||||
self.resolve_heaptype(&mut t.elem.heap)?;
|
||||
}
|
||||
|
@ -391,7 +302,7 @@ impl<'a> Resolver<'a> {
|
|||
T: TypeReference<'a>,
|
||||
{
|
||||
let idx = ty.index.as_mut().unwrap();
|
||||
let idx = self.resolve_item_ref(idx)?;
|
||||
self.resolve(idx, Ns::Type)?;
|
||||
|
||||
// If the type was listed inline *and* it was specified via a type index
|
||||
// we need to assert they're the same.
|
||||
|
@ -416,137 +327,10 @@ impl<'a> Resolver<'a> {
|
|||
Ns::Table => self.tables.resolve(idx, "table"),
|
||||
Ns::Global => self.globals.resolve(idx, "global"),
|
||||
Ns::Memory => self.memories.resolve(idx, "memory"),
|
||||
Ns::Instance => self.instances.resolve(idx, "instance"),
|
||||
Ns::Module => self.modules.resolve(idx, "module"),
|
||||
Ns::Tag => self.tags.resolve(idx, "tag"),
|
||||
Ns::Type => self.types.resolve(idx, "type"),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_item_ref<'b, K>(&self, item: &'b mut ItemRef<'a, K>) -> Result<&'b Index<'a>, Error>
|
||||
where
|
||||
K: Into<ExportKind> + Copy,
|
||||
{
|
||||
match item {
|
||||
ItemRef::Item {
|
||||
idx,
|
||||
kind,
|
||||
exports,
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited,
|
||||
} => {
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
{
|
||||
if !*visited {
|
||||
return Err(Error::new(
|
||||
idx.span(),
|
||||
format!("BUG: this index wasn't visited"),
|
||||
));
|
||||
}
|
||||
}
|
||||
debug_assert!(exports.len() == 0);
|
||||
self.resolve(
|
||||
idx,
|
||||
match (*kind).into() {
|
||||
ExportKind::Func => Ns::Func,
|
||||
ExportKind::Table => Ns::Table,
|
||||
ExportKind::Global => Ns::Global,
|
||||
ExportKind::Memory => Ns::Memory,
|
||||
ExportKind::Instance => Ns::Instance,
|
||||
ExportKind::Module => Ns::Module,
|
||||
ExportKind::Tag => Ns::Tag,
|
||||
ExportKind::Type => Ns::Type,
|
||||
},
|
||||
)?;
|
||||
Ok(idx)
|
||||
}
|
||||
// should be expanded by now
|
||||
ItemRef::Outer { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Namespace<'a> {
|
||||
names: HashMap<Id<'a>, u32>,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
impl<'a> Namespace<'a> {
|
||||
fn register(&mut self, name: Option<Id<'a>>, desc: &str) -> Result<u32, Error> {
|
||||
let index = self.alloc();
|
||||
if let Some(name) = name {
|
||||
if let Some(_prev) = self.names.insert(name, index) {
|
||||
// FIXME: temporarily allow duplicately-named data and element
|
||||
// segments. This is a sort of dumb hack to get the spec test
|
||||
// suite working (ironically).
|
||||
//
|
||||
// So as background, the text format disallows duplicate
|
||||
// identifiers, causing a parse error if they're found. There
|
||||
// are two tests currently upstream, however, data.wast and
|
||||
// elem.wast, which *look* like they have duplicately named
|
||||
// element and data segments. These tests, however, are using
|
||||
// pre-bulk-memory syntax where a bare identifier was the
|
||||
// table/memory being initialized. In post-bulk-memory this
|
||||
// identifier is the name of the segment. Since we implement
|
||||
// post-bulk-memory features that means that we're parsing the
|
||||
// memory/table-to-initialize as the name of the segment.
|
||||
//
|
||||
// This is technically incorrect behavior but no one is
|
||||
// hopefully relying on this too much. To get the spec tests
|
||||
// passing we ignore errors for elem/data segments. Once the
|
||||
// spec tests get updated enough we can remove this condition
|
||||
// and return errors for them.
|
||||
if desc != "elem" && desc != "data" {
|
||||
return Err(Error::new(
|
||||
name.span(),
|
||||
format!("duplicate {} identifier", desc),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
fn alloc(&mut self) -> u32 {
|
||||
let index = self.count;
|
||||
self.count += 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
fn register_specific(&mut self, name: Id<'a>, index: u32, desc: &str) -> Result<(), Error> {
|
||||
if let Some(_prev) = self.names.insert(name, index) {
|
||||
return Err(Error::new(
|
||||
name.span(),
|
||||
format!("duplicate identifier for {}", desc),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve(&self, idx: &mut Index<'a>, desc: &str) -> Result<u32, Error> {
|
||||
let id = match idx {
|
||||
Index::Num(n, _) => return Ok(*n),
|
||||
Index::Id(id) => id,
|
||||
};
|
||||
if let Some(&n) = self.names.get(id) {
|
||||
*idx = Index::Num(n, id.span());
|
||||
return Ok(n);
|
||||
}
|
||||
Err(resolve_error(*id, desc))
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_error(id: Id<'_>, ns: &str) -> Error {
|
||||
assert!(
|
||||
!id.is_gensym(),
|
||||
"symbol generated by `wast` itself cannot be resolved {:?}",
|
||||
id
|
||||
);
|
||||
Error::new(
|
||||
id.span(),
|
||||
format!("failed to find {} named `${}`", ns, id.name()),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -634,23 +418,23 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
|
|||
}
|
||||
|
||||
fn resolve_instr(&mut self, instr: &mut Instruction<'a>) -> Result<(), Error> {
|
||||
use crate::ast::Instruction::*;
|
||||
use Instruction::*;
|
||||
|
||||
if let Some(m) = instr.memarg_mut() {
|
||||
self.resolver.resolve_item_ref(&mut m.memory)?;
|
||||
self.resolver.resolve(&mut m.memory, Ns::Memory)?;
|
||||
}
|
||||
|
||||
match instr {
|
||||
MemorySize(i) | MemoryGrow(i) | MemoryFill(i) => {
|
||||
self.resolver.resolve_item_ref(&mut i.mem)?;
|
||||
self.resolver.resolve(&mut i.mem, Ns::Memory)?;
|
||||
}
|
||||
MemoryInit(i) => {
|
||||
self.resolver.datas.resolve(&mut i.data, "data")?;
|
||||
self.resolver.resolve_item_ref(&mut i.mem)?;
|
||||
self.resolver.resolve(&mut i.mem, Ns::Memory)?;
|
||||
}
|
||||
MemoryCopy(i) => {
|
||||
self.resolver.resolve_item_ref(&mut i.src)?;
|
||||
self.resolver.resolve_item_ref(&mut i.dst)?;
|
||||
self.resolver.resolve(&mut i.src, Ns::Memory)?;
|
||||
self.resolver.resolve(&mut i.dst, Ns::Memory)?;
|
||||
}
|
||||
DataDrop(i) => {
|
||||
self.resolver.datas.resolve(i, "data")?;
|
||||
|
@ -658,23 +442,23 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
|
|||
|
||||
TableInit(i) => {
|
||||
self.resolver.elems.resolve(&mut i.elem, "elem")?;
|
||||
self.resolver.resolve_item_ref(&mut i.table)?;
|
||||
self.resolver.resolve(&mut i.table, Ns::Table)?;
|
||||
}
|
||||
ElemDrop(i) => {
|
||||
self.resolver.elems.resolve(i, "elem")?;
|
||||
}
|
||||
|
||||
TableCopy(i) => {
|
||||
self.resolver.resolve_item_ref(&mut i.dst)?;
|
||||
self.resolver.resolve_item_ref(&mut i.src)?;
|
||||
self.resolver.resolve(&mut i.dst, Ns::Table)?;
|
||||
self.resolver.resolve(&mut i.src, Ns::Table)?;
|
||||
}
|
||||
|
||||
TableFill(i) | TableSet(i) | TableGet(i) | TableSize(i) | TableGrow(i) => {
|
||||
self.resolver.resolve_item_ref(&mut i.dst)?;
|
||||
self.resolver.resolve(&mut i.dst, Ns::Table)?;
|
||||
}
|
||||
|
||||
GlobalSet(i) | GlobalGet(i) => {
|
||||
self.resolver.resolve_item_ref(&mut i.0)?;
|
||||
self.resolver.resolve(i, Ns::Global)?;
|
||||
}
|
||||
|
||||
LocalSet(i) | LocalGet(i) | LocalTee(i) => {
|
||||
|
@ -698,11 +482,11 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
|
|||
}
|
||||
|
||||
Call(i) | RefFunc(i) | ReturnCall(i) => {
|
||||
self.resolver.resolve_item_ref(&mut i.0)?;
|
||||
self.resolver.resolve(i, Ns::Func)?;
|
||||
}
|
||||
|
||||
CallIndirect(c) | ReturnCallIndirect(c) => {
|
||||
self.resolver.resolve_item_ref(&mut c.table)?;
|
||||
self.resolver.resolve(&mut c.table, Ns::Table)?;
|
||||
self.resolver.resolve_type_use(&mut c.ty)?;
|
||||
}
|
||||
|
||||
|
@ -864,96 +648,6 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Parents<'a, 'b> {
|
||||
prev: Option<ParentNode<'a, 'b>>,
|
||||
cur_id: Option<Id<'a>>,
|
||||
depth: usize,
|
||||
names: &'b mut HashMap<Id<'a>, usize>,
|
||||
}
|
||||
|
||||
struct ParentNode<'a, 'b> {
|
||||
resolver: &'b Resolver<'a>,
|
||||
id: Option<Id<'a>>,
|
||||
prev: Option<&'b ParentNode<'a, 'b>>,
|
||||
prev_depth: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Parents<'a, 'b> {
|
||||
fn push<'c>(&'c mut self, resolver: &'c Resolver<'a>, id: Option<Id<'a>>) -> Parents<'a, 'c>
|
||||
where
|
||||
'b: 'c,
|
||||
{
|
||||
let prev_depth = if let Some(id) = self.cur_id {
|
||||
self.names.insert(id, self.depth)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Parents {
|
||||
prev: Some(ParentNode {
|
||||
prev: self.prev.as_ref(),
|
||||
resolver,
|
||||
id: self.cur_id,
|
||||
prev_depth,
|
||||
}),
|
||||
cur_id: id,
|
||||
depth: self.depth + 1,
|
||||
names: &mut *self.names,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve(&self, index: &mut Index<'a>) -> Result<&'b Resolver<'a>, Error> {
|
||||
let mut i = match *index {
|
||||
Index::Num(n, _) => n,
|
||||
Index::Id(id) => match self.names.get(&id) {
|
||||
Some(idx) => (self.depth - *idx - 1) as u32,
|
||||
None => return Err(resolve_error(id, "parent module")),
|
||||
},
|
||||
};
|
||||
*index = Index::Num(i, index.span());
|
||||
let mut cur = match self.prev.as_ref() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
index.span(),
|
||||
"cannot use `outer` alias in root module".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
while i > 0 {
|
||||
cur = match cur.prev {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
index.span(),
|
||||
"alias to `outer` module index too large".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
i -= 1;
|
||||
}
|
||||
Ok(cur.resolver)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Drop for Parents<'a, 'b> {
|
||||
fn drop(&mut self) {
|
||||
let (id, prev_depth) = match &self.prev {
|
||||
Some(n) => (n.id, n.prev_depth),
|
||||
None => return,
|
||||
};
|
||||
if let Some(id) = id {
|
||||
match prev_depth {
|
||||
Some(i) => {
|
||||
self.names.insert(id, i);
|
||||
}
|
||||
None => {
|
||||
self.names.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TypeInfo<'a> {
|
||||
Func {
|
||||
params: Box<[ValType<'a>]>,
|
||||
|
@ -1023,40 +717,3 @@ impl<'a> TypeReference<'a> for FunctionType<'a> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for InstanceType<'a> {
|
||||
fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error> {
|
||||
drop(cx);
|
||||
Err(Error::new(
|
||||
idx.span(),
|
||||
format!("cannot specify instance type as a reference and inline"),
|
||||
))
|
||||
}
|
||||
|
||||
fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> {
|
||||
for export in self.exports.iter_mut() {
|
||||
cx.resolve_item_sig(&mut export.item)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeReference<'a> for ModuleType<'a> {
|
||||
fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error> {
|
||||
drop(cx);
|
||||
Err(Error::new(
|
||||
idx.span(),
|
||||
format!("cannot specify module type as a reference and inline"),
|
||||
))
|
||||
}
|
||||
|
||||
fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> {
|
||||
for i in self.imports.iter_mut() {
|
||||
cx.resolve_item_sig(&mut i.item)?;
|
||||
}
|
||||
for e in self.exports.iter_mut() {
|
||||
cx.resolve_item_sig(&mut e.item)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use crate::ast::*;
|
||||
use crate::resolve::gensym;
|
||||
use crate::core::*;
|
||||
use crate::gensym;
|
||||
use crate::token::{Index, Span};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn expand<'a>(fields: &mut Vec<ModuleField<'a>>) {
|
||||
|
@ -8,16 +9,11 @@ pub fn expand<'a>(fields: &mut Vec<ModuleField<'a>>) {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Expander<'a> {
|
||||
// See the comment in `process` for why this exists.
|
||||
process_imports_early: bool,
|
||||
|
||||
pub(crate) struct Expander<'a> {
|
||||
// Maps used to "intern" types. These maps are populated as type annotations
|
||||
// are seen and inline type annotations use previously defined ones if
|
||||
// there's a match.
|
||||
func_type_to_idx: HashMap<FuncKey<'a>, Index<'a>>,
|
||||
instance_type_to_idx: HashMap<InstanceKey<'a>, Index<'a>>,
|
||||
module_type_to_idx: HashMap<ModuleKey<'a>, Index<'a>>,
|
||||
|
||||
/// Fields, during processing, which should be prepended to the
|
||||
/// currently-being-processed field. This should always be empty after
|
||||
|
@ -27,22 +23,6 @@ struct Expander<'a> {
|
|||
|
||||
impl<'a> Expander<'a> {
|
||||
fn process(&mut self, fields: &mut Vec<ModuleField<'a>>) {
|
||||
// For the given list of fields this determines whether imports are
|
||||
// processed as part of `expand_header` or as part of `expand`. The
|
||||
// reason for this distinction is that pre-module-linking types were
|
||||
// always sorted to the front of the module so new types were always
|
||||
// appended to the end. After module-linking, however, types are
|
||||
// interspersed with imports and order matters. This means that imports
|
||||
// can't use intern'd types which appear later.
|
||||
//
|
||||
// This is a bit of a hack and ideally something that needs to be
|
||||
// addressed in the upstream spec. WebAssembly/module-linking#25
|
||||
// represents this issue.
|
||||
self.process_imports_early = fields.iter().any(|f| match f {
|
||||
ModuleField::Alias(_) | ModuleField::NestedModule(_) | ModuleField::Instance(_) => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
// Next we expand "header" fields which are those like types and
|
||||
// imports. In this context "header" is defined by the previous
|
||||
// `process_imports_early` annotation.
|
||||
|
@ -75,20 +55,9 @@ impl<'a> Expander<'a> {
|
|||
TypeDef::Func(f) => {
|
||||
f.key().insert(self, Index::Id(id));
|
||||
}
|
||||
TypeDef::Instance(i) => {
|
||||
i.expand(self);
|
||||
i.key().insert(self, Index::Id(id));
|
||||
}
|
||||
TypeDef::Module(m) => {
|
||||
m.expand(self);
|
||||
m.key().insert(self, Index::Id(id));
|
||||
}
|
||||
TypeDef::Array(_) | TypeDef::Struct(_) => {}
|
||||
}
|
||||
}
|
||||
ModuleField::Import(i) if self.process_imports_early => {
|
||||
self.expand_item_sig(&mut i.item);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -99,10 +68,7 @@ impl<'a> Expander<'a> {
|
|||
ModuleField::Type(_) => {}
|
||||
|
||||
ModuleField::Import(i) => {
|
||||
// Only expand here if not expanded above
|
||||
if !self.process_imports_early {
|
||||
self.expand_item_sig(&mut i.item);
|
||||
}
|
||||
self.expand_item_sig(&mut i.item);
|
||||
}
|
||||
ModuleField::Func(f) => {
|
||||
self.expand_type_use(&mut f.ty);
|
||||
|
@ -135,15 +101,8 @@ impl<'a> Expander<'a> {
|
|||
self.expand_type_use(ty);
|
||||
}
|
||||
},
|
||||
ModuleField::NestedModule(m) => {
|
||||
if let NestedModuleKind::Inline { fields } = &mut m.kind {
|
||||
Expander::default().process(fields);
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::Alias(_)
|
||||
| ModuleField::Instance(_)
|
||||
| ModuleField::Table(_)
|
||||
ModuleField::Table(_)
|
||||
| ModuleField::Memory(_)
|
||||
| ModuleField::Start(_)
|
||||
| ModuleField::Export(_)
|
||||
|
@ -156,14 +115,6 @@ impl<'a> Expander<'a> {
|
|||
ItemKind::Func(t) | ItemKind::Tag(TagType::Exception(t)) => {
|
||||
self.expand_type_use(t);
|
||||
}
|
||||
ItemKind::Instance(t) => {
|
||||
self.expand_type_use(t);
|
||||
t.inline.take();
|
||||
}
|
||||
ItemKind::Module(m) => {
|
||||
self.expand_type_use(m);
|
||||
m.inline.take();
|
||||
}
|
||||
ItemKind::Global(_) | ItemKind::Table(_) | ItemKind::Memory(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -228,13 +179,7 @@ impl<'a> Expander<'a> {
|
|||
T: TypeReference<'a>,
|
||||
{
|
||||
if let Some(idx) = &item.index {
|
||||
match idx {
|
||||
ItemRef::Item { idx, exports, .. } => {
|
||||
debug_assert!(exports.len() == 0);
|
||||
return idx.clone();
|
||||
}
|
||||
ItemRef::Outer { .. } => unreachable!(),
|
||||
}
|
||||
return idx.clone();
|
||||
}
|
||||
let key = match item.inline.as_mut() {
|
||||
Some(ty) => {
|
||||
|
@ -243,15 +188,9 @@ impl<'a> Expander<'a> {
|
|||
}
|
||||
None => T::default().key(),
|
||||
};
|
||||
let span = Span::from_offset(0); // FIXME: don't manufacture
|
||||
let span = Span::from_offset(0); // FIXME(#613): don't manufacture
|
||||
let idx = self.key_to_idx(span, key);
|
||||
item.index = Some(ItemRef::Item {
|
||||
idx,
|
||||
kind: kw::r#type(span),
|
||||
exports: Vec::new(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: true,
|
||||
});
|
||||
item.index = Some(idx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
@ -277,19 +216,19 @@ impl<'a> Expander<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
trait TypeReference<'a>: Default {
|
||||
pub(crate) trait TypeReference<'a>: Default {
|
||||
type Key: TypeKey<'a>;
|
||||
fn key(&self) -> Self::Key;
|
||||
fn expand(&mut self, cx: &mut Expander<'a>);
|
||||
}
|
||||
|
||||
trait TypeKey<'a> {
|
||||
pub(crate) trait TypeKey<'a> {
|
||||
fn lookup(&self, cx: &Expander<'a>) -> Option<Index<'a>>;
|
||||
fn to_def(&self, span: Span) -> TypeDef<'a>;
|
||||
fn insert(&self, cx: &mut Expander<'a>, id: Index<'a>);
|
||||
}
|
||||
|
||||
type FuncKey<'a> = (Box<[ValType<'a>]>, Box<[ValType<'a>]>);
|
||||
pub(crate) type FuncKey<'a> = (Box<[ValType<'a>]>, Box<[ValType<'a>]>);
|
||||
|
||||
impl<'a> TypeReference<'a> for FunctionType<'a> {
|
||||
type Key = FuncKey<'a>;
|
||||
|
@ -319,159 +258,3 @@ impl<'a> TypeKey<'a> for FuncKey<'a> {
|
|||
cx.func_type_to_idx.entry(self.clone()).or_insert(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// A list of the exports of a module as well as the signature they export.
|
||||
type InstanceKey<'a> = Vec<(&'a str, Item<'a>)>;
|
||||
|
||||
impl<'a> TypeReference<'a> for InstanceType<'a> {
|
||||
type Key = InstanceKey<'a>;
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
self.exports
|
||||
.iter()
|
||||
.map(|export| (export.name, Item::new(&export.item)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
for export in self.exports.iter_mut() {
|
||||
cx.expand_item_sig(&mut export.item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeKey<'a> for InstanceKey<'a> {
|
||||
fn lookup(&self, cx: &Expander<'a>) -> Option<Index<'a>> {
|
||||
cx.instance_type_to_idx.get(self).cloned()
|
||||
}
|
||||
|
||||
fn to_def(&self, span: Span) -> TypeDef<'a> {
|
||||
let exports = self
|
||||
.iter()
|
||||
.map(|(name, item)| ExportType {
|
||||
span,
|
||||
name,
|
||||
item: item.to_sig(span),
|
||||
})
|
||||
.collect();
|
||||
TypeDef::Instance(InstanceType { exports })
|
||||
}
|
||||
|
||||
fn insert(&self, cx: &mut Expander<'a>, idx: Index<'a>) {
|
||||
cx.instance_type_to_idx.entry(self.clone()).or_insert(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// The first element of this pair is the list of imports in the module, and the
|
||||
// second element is the list of exports.
|
||||
type ModuleKey<'a> = (
|
||||
Vec<(&'a str, Option<&'a str>, Item<'a>)>,
|
||||
Vec<(&'a str, Item<'a>)>,
|
||||
);
|
||||
|
||||
impl<'a> TypeReference<'a> for ModuleType<'a> {
|
||||
type Key = ModuleKey<'a>;
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
let imports = self
|
||||
.imports
|
||||
.iter()
|
||||
.map(|import| (import.module, import.field, Item::new(&import.item)))
|
||||
.collect();
|
||||
let exports = self
|
||||
.exports
|
||||
.iter()
|
||||
.map(|export| (export.name, Item::new(&export.item)))
|
||||
.collect();
|
||||
(imports, exports)
|
||||
}
|
||||
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
for export in self.exports.iter_mut() {
|
||||
cx.expand_item_sig(&mut export.item);
|
||||
}
|
||||
for import in self.imports.iter_mut() {
|
||||
cx.expand_item_sig(&mut import.item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TypeKey<'a> for ModuleKey<'a> {
|
||||
fn lookup(&self, cx: &Expander<'a>) -> Option<Index<'a>> {
|
||||
cx.module_type_to_idx.get(self).cloned()
|
||||
}
|
||||
|
||||
fn to_def(&self, span: Span) -> TypeDef<'a> {
|
||||
let imports = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|(module, field, item)| Import {
|
||||
span,
|
||||
module,
|
||||
field: *field,
|
||||
item: item.to_sig(span),
|
||||
})
|
||||
.collect();
|
||||
let exports = self
|
||||
.1
|
||||
.iter()
|
||||
.map(|(name, item)| ExportType {
|
||||
span,
|
||||
name,
|
||||
item: item.to_sig(span),
|
||||
})
|
||||
.collect();
|
||||
TypeDef::Module(ModuleType { imports, exports })
|
||||
}
|
||||
|
||||
fn insert(&self, cx: &mut Expander<'a>, idx: Index<'a>) {
|
||||
cx.module_type_to_idx.entry(self.clone()).or_insert(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// A lookalike to `ItemKind` except without all non-relevant information for
|
||||
// hashing. This is used as a hash key for instance/module type lookup.
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
enum Item<'a> {
|
||||
Func(Index<'a>),
|
||||
Table(TableType<'a>),
|
||||
Memory(MemoryType),
|
||||
Global(GlobalType<'a>),
|
||||
Tag(Index<'a>),
|
||||
Module(Index<'a>),
|
||||
Instance(Index<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Item<'a> {
|
||||
fn new(item: &ItemSig<'a>) -> Item<'a> {
|
||||
match &item.kind {
|
||||
ItemKind::Func(f) => Item::Func(*f.index.as_ref().unwrap().unwrap_index()),
|
||||
ItemKind::Instance(f) => Item::Instance(*f.index.as_ref().unwrap().unwrap_index()),
|
||||
ItemKind::Module(f) => Item::Module(*f.index.as_ref().unwrap().unwrap_index()),
|
||||
ItemKind::Tag(TagType::Exception(f)) => {
|
||||
Item::Tag(*f.index.as_ref().unwrap().unwrap_index())
|
||||
}
|
||||
ItemKind::Table(t) => Item::Table(t.clone()),
|
||||
ItemKind::Memory(t) => Item::Memory(t.clone()),
|
||||
ItemKind::Global(t) => Item::Global(t.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_sig(&self, span: Span) -> ItemSig<'a> {
|
||||
let kind = match self {
|
||||
Item::Func(index) => ItemKind::Func(TypeUse::new_with_index(*index)),
|
||||
Item::Tag(index) => ItemKind::Tag(TagType::Exception(TypeUse::new_with_index(*index))),
|
||||
Item::Instance(index) => ItemKind::Instance(TypeUse::new_with_index(*index)),
|
||||
Item::Module(index) => ItemKind::Module(TypeUse::new_with_index(*index)),
|
||||
Item::Table(t) => ItemKind::Table(t.clone()),
|
||||
Item::Memory(t) => ItemKind::Memory(t.clone()),
|
||||
Item::Global(t) => ItemKind::Global(t.clone()),
|
||||
};
|
||||
ItemSig {
|
||||
span,
|
||||
id: None,
|
||||
name: None,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,20 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::core::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, Index, LParen, NameAnnotation, Span};
|
||||
|
||||
/// A WebAssembly `table` directive in a module.
|
||||
#[derive(Debug)]
|
||||
pub struct Table<'a> {
|
||||
/// Where this table was defined.
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An optional name to refer to this table by.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// If present, inline export annotations which indicate names this
|
||||
/// definition should be exported under.
|
||||
pub exports: ast::InlineExport<'a>,
|
||||
pub exports: InlineExport<'a>,
|
||||
/// How this table is textually defined in the module.
|
||||
pub kind: TableKind<'a>,
|
||||
}
|
||||
|
@ -23,17 +25,17 @@ pub enum TableKind<'a> {
|
|||
/// This table is actually an inlined import definition.
|
||||
#[allow(missing_docs)]
|
||||
Import {
|
||||
import: ast::InlineImport<'a>,
|
||||
ty: ast::TableType<'a>,
|
||||
import: InlineImport<'a>,
|
||||
ty: TableType<'a>,
|
||||
},
|
||||
|
||||
/// A typical memory definition which simply says the limits of the table
|
||||
Normal(ast::TableType<'a>),
|
||||
Normal(TableType<'a>),
|
||||
|
||||
/// The elem segments of this table, starting from 0, explicitly listed
|
||||
Inline {
|
||||
/// The element type of this table.
|
||||
elem: ast::RefType<'a>,
|
||||
elem: 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>,
|
||||
|
@ -53,11 +55,11 @@ impl<'a> Parse<'a> for Table<'a> {
|
|||
// * `(import "a" "b") limits`
|
||||
// * `limits`
|
||||
let mut l = parser.lookahead1();
|
||||
let kind = if l.peek::<ast::RefType>() {
|
||||
let kind = if l.peek::<RefType>() {
|
||||
let elem = parser.parse()?;
|
||||
let payload = parser.parens(|p| {
|
||||
p.parse::<kw::elem>()?;
|
||||
let ty = if parser.peek::<ast::LParen>() {
|
||||
let ty = if parser.peek::<LParen>() {
|
||||
Some(elem)
|
||||
} else {
|
||||
None
|
||||
|
@ -89,11 +91,11 @@ impl<'a> Parse<'a> for Table<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct Elem<'a> {
|
||||
/// Where this `elem` was defined.
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An optional name by which to refer to this segment.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this element stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'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.
|
||||
|
@ -114,9 +116,9 @@ pub enum ElemKind<'a> {
|
|||
/// An active segment associated with a table.
|
||||
Active {
|
||||
/// The table this `elem` is initializing.
|
||||
table: ast::ItemRef<'a, kw::table>,
|
||||
table: Index<'a>,
|
||||
/// The offset within `table` that we'll initialize at.
|
||||
offset: ast::Expression<'a>,
|
||||
offset: Expression<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -124,15 +126,15 @@ pub enum ElemKind<'a> {
|
|||
#[derive(Debug)]
|
||||
pub enum ElemPayload<'a> {
|
||||
/// This element segment has a contiguous list of function indices
|
||||
Indices(Vec<ast::ItemRef<'a, kw::func>>),
|
||||
Indices(Vec<Index<'a>>),
|
||||
|
||||
/// 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>,
|
||||
ty: RefType<'a>,
|
||||
/// The expressions in this segment.
|
||||
exprs: Vec<ast::Expression<'a>>,
|
||||
exprs: Vec<Expression<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -142,19 +144,23 @@ impl<'a> Parse<'a> for Elem<'a> {
|
|||
let id = parser.parse()?;
|
||||
let name = 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
|
||||
let kind = if parser.peek::<kw::declare>() {
|
||||
parser.parse::<kw::declare>()?;
|
||||
ElemKind::Declared
|
||||
} else if parser.peek::<u32>() || (parser.peek::<LParen>() && !parser.peek::<RefType>()) {
|
||||
let table = if parser.peek::<u32>() {
|
||||
// FIXME: this is only here to accomodate
|
||||
// proposals/threads/imports.wast at this current moment in
|
||||
// time, this probably should get removed when the threads
|
||||
// proposal is rebased on the current spec.
|
||||
Index::Num(parser.parse()?, span)
|
||||
} else if parser.peek2::<kw::table>() {
|
||||
parser.parens(|p| {
|
||||
p.parse::<kw::table>()?;
|
||||
p.parse()
|
||||
})?
|
||||
} else {
|
||||
ast::ItemRef::Item {
|
||||
kind: kw::table(parser.prev_span()),
|
||||
idx: ast::Index::Num(0, span),
|
||||
exports: Vec::new(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: false,
|
||||
}
|
||||
Index::Num(0, span)
|
||||
};
|
||||
let offset = parser.parens(|parser| {
|
||||
if parser.peek::<kw::offset>() {
|
||||
|
@ -163,9 +169,6 @@ impl<'a> Parse<'a> for Elem<'a> {
|
|||
parser.parse()
|
||||
})?;
|
||||
ElemKind::Active { table, offset }
|
||||
} else if parser.peek::<kw::declare>() {
|
||||
parser.parse::<kw::declare>()?;
|
||||
ElemKind::Declared
|
||||
} else {
|
||||
ElemKind::Passive
|
||||
};
|
||||
|
@ -187,19 +190,19 @@ impl<'a> Parse<'a> for ElemPayload<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ElemPayload<'a> {
|
||||
fn parse_tail(parser: Parser<'a>, ty: Option<ast::RefType<'a>>) -> Result<Self> {
|
||||
fn parse_tail(parser: Parser<'a>, ty: Option<RefType<'a>>) -> Result<Self> {
|
||||
let (must_use_indices, ty) = match ty {
|
||||
None => {
|
||||
parser.parse::<Option<kw::func>>()?;
|
||||
(true, ast::RefType::func())
|
||||
(true, RefType::func())
|
||||
}
|
||||
Some(ty) => (false, ty),
|
||||
};
|
||||
if let ast::HeapType::Func = ty.heap {
|
||||
if must_use_indices || parser.peek::<ast::IndexOrRef<kw::func>>() {
|
||||
if let HeapType::Func = ty.heap {
|
||||
if must_use_indices || parser.peek::<Index<'_>>() {
|
||||
let mut elems = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
elems.push(parser.parse::<ast::IndexOrRef<_>>()?.0);
|
||||
elems.push(parser.parse()?);
|
||||
}
|
||||
return Ok(ElemPayload::Indices(elems));
|
||||
}
|
||||
|
@ -214,7 +217,7 @@ impl<'a> ElemPayload<'a> {
|
|||
// Without `item` this is "sugar" for a single-instruction
|
||||
// expression.
|
||||
let insn = parser.parse()?;
|
||||
Ok(ast::Expression {
|
||||
Ok(Expression {
|
||||
instrs: [insn].into(),
|
||||
})
|
||||
}
|
|
@ -1,17 +1,19 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::core::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::{Id, NameAnnotation, Span};
|
||||
|
||||
/// A WebAssembly tag directive, part of the exception handling proposal.
|
||||
#[derive(Debug)]
|
||||
pub struct Tag<'a> {
|
||||
/// Where this tag was defined
|
||||
pub span: ast::Span,
|
||||
pub span: Span,
|
||||
/// An optional name by which to refer to this tag in name resolution.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// Optional export directives for this tag.
|
||||
pub exports: ast::InlineExport<'a>,
|
||||
pub exports: InlineExport<'a>,
|
||||
/// The type of tag that is defined.
|
||||
pub ty: TagType<'a>,
|
||||
/// What kind of tag this is defined as.
|
||||
|
@ -23,7 +25,7 @@ pub struct Tag<'a> {
|
|||
pub enum TagType<'a> {
|
||||
/// An exception tag, where the payload is the type signature of the tag
|
||||
/// (constructor parameters, etc).
|
||||
Exception(ast::TypeUse<'a, ast::FunctionType<'a>>),
|
||||
Exception(TypeUse<'a, FunctionType<'a>>),
|
||||
}
|
||||
|
||||
/// Different kinds of tags that can be defined in a module.
|
||||
|
@ -34,7 +36,7 @@ pub enum TagKind<'a> {
|
|||
/// ```text
|
||||
/// (tag (type 0) (import "foo" "bar"))
|
||||
/// ```
|
||||
Import(ast::InlineImport<'a>),
|
||||
Import(InlineImport<'a>),
|
||||
|
||||
/// A tag defined inline in the module itself
|
||||
Inline(),
|
|
@ -1,5 +1,7 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::core::*;
|
||||
use crate::kw;
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use crate::token::{Id, Index, LParen, NameAnnotation, Span};
|
||||
use std::mem;
|
||||
|
||||
/// The value types for a wasm module.
|
||||
|
@ -12,7 +14,7 @@ pub enum ValType<'a> {
|
|||
F64,
|
||||
V128,
|
||||
Ref(RefType<'a>),
|
||||
Rtt(Option<u32>, ast::Index<'a>),
|
||||
Rtt(Option<u32>, Index<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ValType<'a> {
|
||||
|
@ -35,7 +37,7 @@ impl<'a> Parse<'a> for ValType<'a> {
|
|||
Ok(ValType::V128)
|
||||
} else if l.peek::<RefType>() {
|
||||
Ok(ValType::Ref(parser.parse()?))
|
||||
} else if l.peek::<ast::LParen>() {
|
||||
} else if l.peek::<LParen>() {
|
||||
parser.parens(|p| {
|
||||
let mut l = p.lookahead1();
|
||||
if l.peek::<kw::rtt>() {
|
||||
|
@ -58,7 +60,7 @@ impl<'a> Peek for ValType<'a> {
|
|||
|| kw::f32::peek(cursor)
|
||||
|| kw::f64::peek(cursor)
|
||||
|| kw::v128::peek(cursor)
|
||||
|| (ast::LParen::peek(cursor) && kw::rtt::peek2(cursor))
|
||||
|| (LParen::peek(cursor) && kw::rtt::peek2(cursor))
|
||||
|| RefType::peek(cursor)
|
||||
}
|
||||
fn display() -> &'static str {
|
||||
|
@ -89,7 +91,7 @@ pub enum HeapType<'a> {
|
|||
I31,
|
||||
/// A reference to a function, struct, or array: ref T. This is part of the
|
||||
/// GC proposal.
|
||||
Index(ast::Index<'a>),
|
||||
Index(Index<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for HeapType<'a> {
|
||||
|
@ -113,7 +115,7 @@ impl<'a> Parse<'a> for HeapType<'a> {
|
|||
} else if l.peek::<kw::i31>() {
|
||||
parser.parse::<kw::i31>()?;
|
||||
Ok(HeapType::I31)
|
||||
} else if l.peek::<ast::Index>() {
|
||||
} else if l.peek::<Index>() {
|
||||
Ok(HeapType::Index(parser.parse()?))
|
||||
} else {
|
||||
Err(l.error())
|
||||
|
@ -129,7 +131,7 @@ impl<'a> Peek for HeapType<'a> {
|
|||
|| kw::eq::peek(cursor)
|
||||
|| kw::data::peek(cursor)
|
||||
|| kw::i31::peek(cursor)
|
||||
|| (ast::LParen::peek(cursor) && kw::r#type::peek2(cursor))
|
||||
|| (LParen::peek(cursor) && kw::r#type::peek2(cursor))
|
||||
}
|
||||
fn display() -> &'static str {
|
||||
"heaptype"
|
||||
|
@ -218,7 +220,7 @@ impl<'a> Parse<'a> for RefType<'a> {
|
|||
} else if l.peek::<kw::i31ref>() {
|
||||
parser.parse::<kw::i31ref>()?;
|
||||
Ok(RefType::i31())
|
||||
} else if l.peek::<ast::LParen>() {
|
||||
} else if l.peek::<LParen>() {
|
||||
parser.parens(|p| {
|
||||
let mut l = parser.lookahead1();
|
||||
if l.peek::<kw::r#ref>() {
|
||||
|
@ -253,7 +255,7 @@ impl<'a> Peek for RefType<'a> {
|
|||
|| kw::eqref::peek(cursor)
|
||||
|| kw::dataref::peek(cursor)
|
||||
|| kw::i31ref::peek(cursor)
|
||||
|| (ast::LParen::peek(cursor) && kw::r#ref::peek2(cursor))
|
||||
|| (LParen::peek(cursor) && kw::r#ref::peek2(cursor))
|
||||
}
|
||||
fn display() -> &'static str {
|
||||
"reftype"
|
||||
|
@ -414,13 +416,7 @@ impl<'a> Parse<'a> for MemoryType {
|
|||
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>,
|
||||
)],
|
||||
>,
|
||||
pub params: Box<[(Option<Id<'a>>, Option<NameAnnotation<'a>>, ValType<'a>)]>,
|
||||
/// The results types of a function.
|
||||
pub results: Box<[ValType<'a>]>,
|
||||
}
|
||||
|
@ -559,7 +555,7 @@ impl<'a> Parse<'a> for StructType<'a> {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct StructField<'a> {
|
||||
/// An optional identifier for name resolution.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// Whether this field may be mutated or not.
|
||||
pub mutable: bool,
|
||||
/// The storage type stored in this field.
|
||||
|
@ -606,102 +602,15 @@ impl<'a> Parse<'a> for ArrayType<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
// See comments in `nested_module.rs` for why this is tested here.
|
||||
if parser.parens_depth() > 100 {
|
||||
return Err(parser.error("module type nesting too deep"));
|
||||
}
|
||||
|
||||
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> {
|
||||
// See comments in `nested_module.rs` for why this is tested here.
|
||||
if parser.parens_depth() > 100 {
|
||||
return Err(parser.error("instance type nesting too deep"));
|
||||
}
|
||||
|
||||
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,
|
||||
pub span: Span,
|
||||
/// The name of this export.
|
||||
pub name: &'a str,
|
||||
/// The signature of the item that's exported.
|
||||
pub item: ast::ItemSig<'a>,
|
||||
pub item: ItemSig<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for ExportType<'a> {
|
||||
|
@ -722,22 +631,18 @@ pub enum TypeDef<'a> {
|
|||
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,
|
||||
pub span: Span,
|
||||
/// An optional identifer to refer to this `type` by as part of name
|
||||
/// resolution.
|
||||
pub id: Option<ast::Id<'a>>,
|
||||
pub id: Option<Id<'a>>,
|
||||
/// An optional name for this function stored in the custom `name` section.
|
||||
pub name: Option<ast::NameAnnotation<'a>>,
|
||||
pub name: Option<NameAnnotation<'a>>,
|
||||
/// The type that we're declaring.
|
||||
pub def: TypeDef<'a>,
|
||||
}
|
||||
|
@ -758,12 +663,6 @@ impl<'a> Parse<'a> for Type<'a> {
|
|||
} 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())
|
||||
}
|
||||
|
@ -781,7 +680,7 @@ impl<'a> Parse<'a> for Type<'a> {
|
|||
#[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>>,
|
||||
pub index: Option<Index<'a>>,
|
||||
/// The inline type, if present.
|
||||
pub inline: Option<T>,
|
||||
}
|
||||
|
@ -789,15 +688,9 @@ pub struct TypeUse<'a, 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> {
|
||||
pub fn new_with_index(idx: Index<'a>) -> TypeUse<'a, T> {
|
||||
TypeUse {
|
||||
index: Some(ast::ItemRef::Item {
|
||||
idx,
|
||||
kind: kw::r#type::default(),
|
||||
exports: Vec::new(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: true,
|
||||
}),
|
||||
index: Some(idx),
|
||||
inline: None,
|
||||
}
|
||||
}
|
||||
|
@ -806,7 +699,10 @@ impl<'a, T> TypeUse<'a, T> {
|
|||
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()?)
|
||||
Some(parser.parens(|p| {
|
||||
p.parse::<kw::r#type>()?;
|
||||
p.parse()
|
||||
})?)
|
||||
} else {
|
||||
None
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
pub(crate) trait Encode {
|
||||
fn encode(&self, e: &mut Vec<u8>);
|
||||
}
|
||||
|
||||
impl<T: Encode + ?Sized> Encode for &'_ T {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
T::encode(self, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for [T] {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.len().encode(e);
|
||||
for item in self {
|
||||
item.encode(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Vec<T> {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
<[T]>::encode(self, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for str {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.len().encode(e);
|
||||
e.extend_from_slice(self.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for usize {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
assert!(*self <= u32::max_value() as usize);
|
||||
(*self as u32).encode(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for u8 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
e.push(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for u32 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::unsigned(e, (*self).into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for i32 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::signed(e, (*self).into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for u64 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::unsigned(e, (*self).into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for i64 {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
leb128::write::signed(e, *self).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode, U: Encode> Encode for (T, U) {
|
||||
fn encode(&self, e: &mut Vec<u8>) {
|
||||
self.0.encode(e);
|
||||
self.1.encode(e);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
use crate::lexer::LexError;
|
||||
use crate::token::Span;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// A convenience error type to tie together all the detailed errors produced by
|
||||
/// this crate.
|
||||
///
|
||||
/// This type can be created from a [`LexError`]. This also contains
|
||||
/// storage for file/text information so a nice error can be rendered along the
|
||||
/// same lines of rustc's own error messages (minus the color).
|
||||
///
|
||||
/// This type is typically suitable for use in public APIs for consumers of this
|
||||
/// crate.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
inner: Box<ErrorInner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ErrorInner {
|
||||
text: Option<Text>,
|
||||
file: Option<PathBuf>,
|
||||
span: Span,
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Text {
|
||||
line: usize,
|
||||
col: usize,
|
||||
snippet: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorKind {
|
||||
Lex(LexError),
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn lex(span: Span, content: &str, kind: LexError) -> Error {
|
||||
let mut ret = Error {
|
||||
inner: Box::new(ErrorInner {
|
||||
text: None,
|
||||
file: None,
|
||||
span,
|
||||
kind: ErrorKind::Lex(kind),
|
||||
}),
|
||||
};
|
||||
ret.set_text(content);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pub(crate) fn parse(span: Span, content: &str, message: String) -> Error {
|
||||
let mut ret = Error {
|
||||
inner: Box::new(ErrorInner {
|
||||
text: None,
|
||||
file: None,
|
||||
span,
|
||||
kind: ErrorKind::Custom(message),
|
||||
}),
|
||||
};
|
||||
ret.set_text(content);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Creates a new error with the given `message` which is targeted at the
|
||||
/// given `span`
|
||||
///
|
||||
/// Note that you'll want to ensure that `set_text` or `set_path` is called
|
||||
/// on the resulting error to improve the rendering of the error message.
|
||||
pub fn new(span: Span, message: String) -> Error {
|
||||
Error {
|
||||
inner: Box::new(ErrorInner {
|
||||
text: None,
|
||||
file: None,
|
||||
span,
|
||||
kind: ErrorKind::Custom(message),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `Span` for this error.
|
||||
pub fn span(&self) -> Span {
|
||||
self.inner.span
|
||||
}
|
||||
|
||||
/// To provide a more useful error this function can be used to extract
|
||||
/// relevant textual information about this error into the error itself.
|
||||
///
|
||||
/// The `contents` here should be the full text of the original file being
|
||||
/// parsed, and this will extract a sub-slice as necessary to render in the
|
||||
/// `Display` implementation later on.
|
||||
pub fn set_text(&mut self, contents: &str) {
|
||||
if self.inner.text.is_some() {
|
||||
return;
|
||||
}
|
||||
self.inner.text = Some(Text::new(contents, self.inner.span));
|
||||
}
|
||||
|
||||
/// To provide a more useful error this function can be used to set
|
||||
/// the file name that this error is associated with.
|
||||
///
|
||||
/// The `path` here will be stored in this error and later rendered in the
|
||||
/// `Display` implementation.
|
||||
pub fn set_path(&mut self, path: &Path) {
|
||||
if self.inner.file.is_some() {
|
||||
return;
|
||||
}
|
||||
self.inner.file = Some(path.to_path_buf());
|
||||
}
|
||||
|
||||
/// Returns the underlying `LexError`, if any, that describes this error.
|
||||
pub fn lex_error(&self) -> Option<&LexError> {
|
||||
match &self.inner.kind {
|
||||
ErrorKind::Lex(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying message, if any, that describes this error.
|
||||
pub fn message(&self) -> String {
|
||||
match &self.inner.kind {
|
||||
ErrorKind::Lex(e) => e.to_string(),
|
||||
ErrorKind::Custom(e) => e.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let err = match &self.inner.kind {
|
||||
ErrorKind::Lex(e) => e as &dyn fmt::Display,
|
||||
ErrorKind::Custom(e) => e as &dyn fmt::Display,
|
||||
};
|
||||
let text = match &self.inner.text {
|
||||
Some(text) => text,
|
||||
None => {
|
||||
return write!(f, "{} at byte offset {}", err, self.inner.span.offset);
|
||||
}
|
||||
};
|
||||
let file = self
|
||||
.inner
|
||||
.file
|
||||
.as_ref()
|
||||
.and_then(|p| p.to_str())
|
||||
.unwrap_or("<anon>");
|
||||
write!(
|
||||
f,
|
||||
"\
|
||||
{err}
|
||||
--> {file}:{line}:{col}
|
||||
|
|
||||
{line:4} | {text}
|
||||
| {marker:>0$}",
|
||||
text.col + 1,
|
||||
file = file,
|
||||
line = text.line + 1,
|
||||
col = text.col + 1,
|
||||
err = err,
|
||||
text = text.snippet,
|
||||
marker = "^",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl Text {
|
||||
fn new(content: &str, span: Span) -> Text {
|
||||
let (line, col) = span.linecol_in(content);
|
||||
let contents = content.lines().nth(line).unwrap_or("");
|
||||
let mut snippet = String::new();
|
||||
for ch in contents.chars() {
|
||||
match ch {
|
||||
// Replace tabs with spaces to render consistently
|
||||
'\t' => {
|
||||
snippet.push_str(" ");
|
||||
}
|
||||
// these codepoints change how text is rendered so for clarity
|
||||
// in error messages they're dropped.
|
||||
'\u{202a}' | '\u{202b}' | '\u{202d}' | '\u{202e}' | '\u{2066}' | '\u{2067}'
|
||||
| '\u{2068}' | '\u{206c}' | '\u{2069}' => {}
|
||||
|
||||
c => snippet.push(c),
|
||||
}
|
||||
}
|
||||
// Use the `unicode-width` crate to figure out how wide the snippet, up
|
||||
// to our "column", actually is. That'll tell us how many spaces to
|
||||
// place before the `^` character that points at the problem
|
||||
let col = snippet.get(..col).map(|s| s.width()).unwrap_or(col);
|
||||
Text { line, col, snippet }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ast::{Id, Span};
|
||||
use crate::token::{Id, Span};
|
||||
use std::cell::Cell;
|
||||
|
||||
thread_local!(static NEXT: Cell<u32> = Cell::new(0));
|
|
@ -24,7 +24,8 @@
|
|||
//!
|
||||
//! [`Lexer`]: crate::lexer::Lexer
|
||||
|
||||
use crate::{Error, Span};
|
||||
use crate::token::Span;
|
||||
use crate::Error;
|
||||
use std::borrow::Cow;
|
||||
use std::char;
|
||||
use std::fmt;
|
||||
|
@ -32,7 +33,7 @@ use std::str;
|
|||
|
||||
/// A structure used to lex the s-expression syntax of WAT files.
|
||||
///
|
||||
/// This structure is used to generate [`Source`] items, which should account for
|
||||
/// This structure is used to generate [`Token`] items, which should account for
|
||||
/// every single byte of the input as we iterate over it. A [`LexError`] is
|
||||
/// returned for any non-lexable text.
|
||||
#[derive(Clone)]
|
||||
|
@ -483,10 +484,10 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
|
||||
fn number(&self, src: &'a str) -> Option<Token<'a>> {
|
||||
let (sign, num) = if src.starts_with('+') {
|
||||
(Some(SignToken::Plus), &src[1..])
|
||||
} else if src.starts_with('-') {
|
||||
(Some(SignToken::Minus), &src[1..])
|
||||
let (sign, num) = if let Some(stripped) = src.strip_prefix('+') {
|
||||
(Some(SignToken::Plus), stripped)
|
||||
} else if let Some(stripped) = src.strip_prefix('-') {
|
||||
(Some(SignToken::Minus), stripped)
|
||||
} else {
|
||||
(None, src)
|
||||
};
|
||||
|
@ -507,8 +508,8 @@ impl<'a> Lexer<'a> {
|
|||
negative,
|
||||
},
|
||||
}))));
|
||||
} else if num.starts_with("nan:0x") {
|
||||
let mut it = num[6..].chars();
|
||||
} else if let Some(stripped) = num.strip_prefix("nan:0x") {
|
||||
let mut it = stripped.chars();
|
||||
let to_parse = skip_undescores(&mut it, false, char::is_ascii_hexdigit)?;
|
||||
if it.next().is_some() {
|
||||
return None;
|
||||
|
@ -524,9 +525,9 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
|
||||
// Figure out if we're a hex number or not
|
||||
let (mut it, hex, test_valid) = if num.starts_with("0x") {
|
||||
let (mut it, hex, test_valid) = if let Some(stripped) = num.strip_prefix("0x") {
|
||||
(
|
||||
num[2..].chars(),
|
||||
stripped.chars(),
|
||||
true,
|
||||
char::is_ascii_hexdigit as fn(&char) -> bool,
|
||||
)
|
||||
|
|
|
@ -20,9 +20,10 @@
|
|||
//! around working with a [`Parser`](`parser::Parser`) to parse streams of
|
||||
//! tokens.
|
||||
//!
|
||||
//! * [`Module`] - this contains an Abstract Syntax Tree (AST) of the
|
||||
//! WebAssembly Text format (WAT) as well as the unofficial WAST format. This
|
||||
//! also has a [`Module::encode`] method to emit a module in its binary form.
|
||||
//! * [`Module`](crate::core::Module) - this contains an Abstract Syntax Tree
|
||||
//! (AST) of the WebAssembly Text format (WAT) as well as the unofficial WAST
|
||||
//! format. This also has a [`Module::encode`](crate::core::Module::encode)
|
||||
//! method to emit a module in its binary form.
|
||||
//!
|
||||
//! # Stability and WebAssembly Features
|
||||
//!
|
||||
|
@ -47,209 +48,463 @@
|
|||
|
||||
#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
|
||||
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
/// 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::token::Span);
|
||||
|
||||
#[cfg(feature = "wasm-module")]
|
||||
mod binary;
|
||||
#[cfg(feature = "wasm-module")]
|
||||
mod resolve;
|
||||
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, "`")))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mod ast;
|
||||
pub use self::ast::*;
|
||||
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::core::Expression<'a>,
|
||||
/// rhs: wast::core::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::token::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::token::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, "`")
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod token;
|
||||
|
||||
/// A convenience error type to tie together all the detailed errors produced by
|
||||
/// this crate.
|
||||
///
|
||||
/// This type can be created from a [`lexer::LexError`] or [`parser::Error`].
|
||||
/// This also contains storage for file/text information so a nice error can be
|
||||
/// rendered along the same lines of rustc's own error messages (minus the
|
||||
/// color).
|
||||
///
|
||||
/// This type is typically suitable for use in public APIs for consumers of this
|
||||
/// crate.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
inner: Box<ErrorInner>,
|
||||
mod encode;
|
||||
mod error;
|
||||
mod gensym;
|
||||
mod names;
|
||||
pub use self::error::*;
|
||||
|
||||
macro_rules! id {
|
||||
($($t:tt)*) => ($($t)*)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ErrorInner {
|
||||
text: Option<Text>,
|
||||
file: Option<PathBuf>,
|
||||
span: Span,
|
||||
kind: ErrorKind,
|
||||
#[cfg(feature = "wasm-module")]
|
||||
id! {
|
||||
mod assert_expr;
|
||||
mod wast;
|
||||
mod wat;
|
||||
pub use self::assert_expr::*;
|
||||
pub use self::wast::*;
|
||||
pub use self::wat::*;
|
||||
|
||||
// Support for core wasm parsing
|
||||
pub mod core;
|
||||
|
||||
// Support for component model parsing
|
||||
pub mod component;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Text {
|
||||
line: usize,
|
||||
col: usize,
|
||||
snippet: String,
|
||||
/// 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_exception);
|
||||
custom_keyword!(assert_exhaustion);
|
||||
custom_keyword!(assert_invalid);
|
||||
custom_keyword!(assert_malformed);
|
||||
custom_keyword!(assert_return);
|
||||
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!(component);
|
||||
custom_keyword!(data);
|
||||
custom_keyword!(dataref);
|
||||
custom_keyword!(declare);
|
||||
custom_keyword!(delegate);
|
||||
custom_keyword!(r#do = "do");
|
||||
custom_keyword!(elem);
|
||||
custom_keyword!(end);
|
||||
custom_keyword!(tag);
|
||||
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!(v128);
|
||||
custom_keyword!(value);
|
||||
custom_keyword!(s8);
|
||||
custom_keyword!(s16);
|
||||
custom_keyword!(s32);
|
||||
custom_keyword!(s64);
|
||||
custom_keyword!(u8);
|
||||
custom_keyword!(u16);
|
||||
custom_keyword!(u32);
|
||||
custom_keyword!(u64);
|
||||
custom_keyword!(char);
|
||||
custom_keyword!(case);
|
||||
custom_keyword!(defaults_to = "defaults-to");
|
||||
custom_keyword!(record);
|
||||
custom_keyword!(string);
|
||||
custom_keyword!(bool_ = "bool");
|
||||
custom_keyword!(float32);
|
||||
custom_keyword!(float64);
|
||||
custom_keyword!(variant);
|
||||
custom_keyword!(unit);
|
||||
custom_keyword!(flags);
|
||||
custom_keyword!(option);
|
||||
custom_keyword!(tuple);
|
||||
custom_keyword!(list);
|
||||
custom_keyword!(union);
|
||||
custom_keyword!(expected);
|
||||
custom_keyword!(canon_lift = "canon.lift");
|
||||
custom_keyword!(canon_lower = "canon.lower");
|
||||
custom_keyword!(enum_ = "enum");
|
||||
custom_keyword!(string_utf8 = "string=utf8");
|
||||
custom_keyword!(string_utf16 = "string=utf16");
|
||||
custom_keyword!(string_latin1_utf16 = "string=latin1+utf16");
|
||||
custom_keyword!(into);
|
||||
custom_keyword!(with);
|
||||
custom_keyword!(core);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorKind {
|
||||
Lex(lexer::LexError),
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn lex(span: Span, content: &str, kind: lexer::LexError) -> Error {
|
||||
let mut ret = Error {
|
||||
inner: Box::new(ErrorInner {
|
||||
text: None,
|
||||
file: None,
|
||||
span,
|
||||
kind: ErrorKind::Lex(kind),
|
||||
}),
|
||||
};
|
||||
ret.set_text(content);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn parse(span: Span, content: &str, message: String) -> Error {
|
||||
let mut ret = Error {
|
||||
inner: Box::new(ErrorInner {
|
||||
text: None,
|
||||
file: None,
|
||||
span,
|
||||
kind: ErrorKind::Custom(message),
|
||||
}),
|
||||
};
|
||||
ret.set_text(content);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Creates a new error with the given `message` which is targeted at the
|
||||
/// given `span`
|
||||
///
|
||||
/// Note that you'll want to ensure that `set_text` or `set_path` is called
|
||||
/// on the resulting error to improve the rendering of the error message.
|
||||
pub fn new(span: Span, message: String) -> Error {
|
||||
Error {
|
||||
inner: Box::new(ErrorInner {
|
||||
text: None,
|
||||
file: None,
|
||||
span,
|
||||
kind: ErrorKind::Custom(message),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `Span` for this error.
|
||||
pub fn span(&self) -> Span {
|
||||
self.inner.span
|
||||
}
|
||||
|
||||
/// To provide a more useful error this function can be used to extract
|
||||
/// relevant textual information about this error into the error itself.
|
||||
///
|
||||
/// The `contents` here should be the full text of the original file being
|
||||
/// parsed, and this will extract a sub-slice as necessary to render in the
|
||||
/// `Display` implementation later on.
|
||||
pub fn set_text(&mut self, contents: &str) {
|
||||
if self.inner.text.is_some() {
|
||||
return;
|
||||
}
|
||||
self.inner.text = Some(Text::new(contents, self.inner.span));
|
||||
}
|
||||
|
||||
/// To provide a more useful error this function can be used to set
|
||||
/// the file name that this error is associated with.
|
||||
///
|
||||
/// The `path` here will be stored in this error and later rendered in the
|
||||
/// `Display` implementation.
|
||||
pub fn set_path(&mut self, path: &Path) {
|
||||
if self.inner.file.is_some() {
|
||||
return;
|
||||
}
|
||||
self.inner.file = Some(path.to_path_buf());
|
||||
}
|
||||
|
||||
/// Returns the underlying `LexError`, if any, that describes this error.
|
||||
pub fn lex_error(&self) -> Option<&lexer::LexError> {
|
||||
match &self.inner.kind {
|
||||
ErrorKind::Lex(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying message, if any, that describes this error.
|
||||
pub fn message(&self) -> String {
|
||||
match &self.inner.kind {
|
||||
ErrorKind::Lex(e) => e.to_string(),
|
||||
ErrorKind::Custom(e) => e.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let err = match &self.inner.kind {
|
||||
ErrorKind::Lex(e) => e as &dyn fmt::Display,
|
||||
ErrorKind::Custom(e) => e as &dyn fmt::Display,
|
||||
};
|
||||
let text = match &self.inner.text {
|
||||
Some(text) => text,
|
||||
None => {
|
||||
return write!(f, "{} at byte offset {}", err, self.inner.span.offset);
|
||||
}
|
||||
};
|
||||
let file = self
|
||||
.inner
|
||||
.file
|
||||
.as_ref()
|
||||
.and_then(|p| p.to_str())
|
||||
.unwrap_or("<anon>");
|
||||
write!(
|
||||
f,
|
||||
"\
|
||||
{err}
|
||||
--> {file}:{line}:{col}
|
||||
|
|
||||
{line:4} | {text}
|
||||
| {marker:>0$}",
|
||||
text.col + 1,
|
||||
file = file,
|
||||
line = text.line + 1,
|
||||
col = text.col + 1,
|
||||
err = err,
|
||||
text = text.snippet,
|
||||
marker = "^",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl Text {
|
||||
fn new(content: &str, span: Span) -> Text {
|
||||
let (line, col) = span.linecol_in(content);
|
||||
let contents = content.lines().nth(line).unwrap_or("");
|
||||
let mut snippet = String::new();
|
||||
for ch in contents.chars() {
|
||||
match ch {
|
||||
// Replace tabs with spaces to render consistently
|
||||
'\t' => {
|
||||
snippet.push_str(" ");
|
||||
}
|
||||
// these codepoints change how text is rendered so for clarity
|
||||
// in error messages they're dropped.
|
||||
'\u{202a}' | '\u{202b}' | '\u{202d}' | '\u{202e}' | '\u{2066}' | '\u{2067}'
|
||||
| '\u{2068}' | '\u{206c}' | '\u{2069}' => {}
|
||||
|
||||
c => snippet.push(c),
|
||||
}
|
||||
}
|
||||
// Use the `unicode-width` crate to figure out how wide the snippet, up
|
||||
// to our "column", actually is. That'll tell us how many spaces to
|
||||
// place before the `^` character that points at the problem
|
||||
let col = snippet.get(..col).map(|s| s.width()).unwrap_or(col);
|
||||
Text { line, col, snippet }
|
||||
}
|
||||
/// Common annotations used to parse WebAssembly text files.
|
||||
pub mod annotation {
|
||||
annotation!(custom);
|
||||
annotation!(name);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
use crate::token::{Id, Index};
|
||||
use crate::Error;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Namespace<'a> {
|
||||
names: HashMap<Id<'a>, u32>,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
impl<'a> Namespace<'a> {
|
||||
pub fn register(&mut self, name: Option<Id<'a>>, desc: &str) -> Result<u32, Error> {
|
||||
let index = self.alloc();
|
||||
if let Some(name) = name {
|
||||
if let Some(_prev) = self.names.insert(name, index) {
|
||||
// FIXME: temporarily allow duplicately-named data and element
|
||||
// segments. This is a sort of dumb hack to get the spec test
|
||||
// suite working (ironically).
|
||||
//
|
||||
// So as background, the text format disallows duplicate
|
||||
// identifiers, causing a parse error if they're found. There
|
||||
// are two tests currently upstream, however, data.wast and
|
||||
// elem.wast, which *look* like they have duplicately named
|
||||
// element and data segments. These tests, however, are using
|
||||
// pre-bulk-memory syntax where a bare identifier was the
|
||||
// table/memory being initialized. In post-bulk-memory this
|
||||
// identifier is the name of the segment. Since we implement
|
||||
// post-bulk-memory features that means that we're parsing the
|
||||
// memory/table-to-initialize as the name of the segment.
|
||||
//
|
||||
// This is technically incorrect behavior but no one is
|
||||
// hopefully relying on this too much. To get the spec tests
|
||||
// passing we ignore errors for elem/data segments. Once the
|
||||
// spec tests get updated enough we can remove this condition
|
||||
// and return errors for them.
|
||||
if desc != "elem" && desc != "data" {
|
||||
return Err(Error::new(
|
||||
name.span(),
|
||||
format!("duplicate {} identifier", desc),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
pub fn alloc(&mut self) -> u32 {
|
||||
let index = self.count;
|
||||
self.count += 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn register_specific(&mut self, name: Id<'a>, index: u32, desc: &str) -> Result<(), Error> {
|
||||
if let Some(_prev) = self.names.insert(name, index) {
|
||||
return Err(Error::new(
|
||||
name.span(),
|
||||
format!("duplicate identifier for {}", desc),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resolve(&self, idx: &mut Index<'a>, desc: &str) -> Result<u32, Error> {
|
||||
let id = match idx {
|
||||
Index::Num(n, _) => return Ok(*n),
|
||||
Index::Id(id) => id,
|
||||
};
|
||||
if let Some(&n) = self.names.get(id) {
|
||||
*idx = Index::Num(n, id.span());
|
||||
return Ok(n);
|
||||
}
|
||||
Err(resolve_error(*id, desc))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_error(id: Id<'_>, ns: &str) -> Error {
|
||||
assert!(
|
||||
!id.is_gensym(),
|
||||
"symbol generated by `wast` itself cannot be resolved {:?}",
|
||||
id
|
||||
);
|
||||
Error::new(
|
||||
id.span(),
|
||||
format!("failed to find {} named `${}`", ns, id.name()),
|
||||
)
|
||||
}
|
|
@ -24,7 +24,8 @@
|
|||
//! [`Parse`](crate::parser::Parse) trait:
|
||||
//!
|
||||
//! ```
|
||||
//! use wast::{kw, Import, Func};
|
||||
//! use wast::kw;
|
||||
//! use wast::core::{Import, Func};
|
||||
//! use wast::parser::{Parser, Parse, Result};
|
||||
//!
|
||||
//! // Fields of a WebAssembly which only allow imports and functions, and all
|
||||
|
@ -64,7 +65,8 @@
|
|||
//! likely also draw inspiration from the excellent examples in the `syn` crate.
|
||||
|
||||
use crate::lexer::{Float, Integer, Lexer, Token};
|
||||
use crate::{Error, Span};
|
||||
use crate::token::Span;
|
||||
use crate::Error;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
@ -118,7 +120,7 @@ pub fn parse<'a, T: Parse<'a>>(buf: &'a ParseBuffer<'a>) -> Result<T> {
|
|||
/// The [`Parse`] trait is main abstraction you'll be working with when defining
|
||||
/// custom parser or custom syntax for your WebAssembly text format (or when
|
||||
/// using the official format items). Almost all items in the
|
||||
/// [`ast`](crate::ast) module implement the [`Parse`] trait, and you'll
|
||||
/// [`core`](crate::core) module implement the [`Parse`] trait, and you'll
|
||||
/// commonly use this with:
|
||||
///
|
||||
/// * The top-level [`parse`] function to parse an entire input.
|
||||
|
@ -139,7 +141,7 @@ pub fn parse<'a, T: Parse<'a>>(buf: &'a ParseBuffer<'a>) -> Result<T> {
|
|||
/// (import "foo" "bar" (func (type 0)))
|
||||
/// ```
|
||||
///
|
||||
/// but the [`Import`](crate::ast::Import) type parser looks like:
|
||||
/// but the [`Import`](crate::core::Import) type parser looks like:
|
||||
///
|
||||
/// ```
|
||||
/// # use wast::kw;
|
||||
|
@ -170,7 +172,8 @@ pub fn parse<'a, T: Parse<'a>>(buf: &'a ParseBuffer<'a>) -> Result<T> {
|
|||
/// before all functions. An example [`Parse`] implementation might look like:
|
||||
///
|
||||
/// ```
|
||||
/// use wast::{Import, Func, kw};
|
||||
/// use wast::core::{Import, Func};
|
||||
/// use wast::kw;
|
||||
/// use wast::parser::{Parser, Parse, Result};
|
||||
///
|
||||
/// // Fields of a WebAssembly which only allow imports and functions, and all
|
||||
|
@ -268,7 +271,7 @@ pub trait Peek {
|
|||
|
||||
/// A convenience type definition for `Result` where the error is hardwired to
|
||||
/// [`Error`].
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// A low-level buffer of tokens which represents a completely lexed file.
|
||||
///
|
||||
|
@ -475,7 +478,7 @@ impl<'a> Parser<'a> {
|
|||
/// and a [`RefType`]
|
||||
///
|
||||
/// ```
|
||||
/// # use wast::*;
|
||||
/// # use wast::core::*;
|
||||
/// # use wast::parser::*;
|
||||
/// struct TableType<'a> {
|
||||
/// limits: Limits,
|
||||
|
@ -493,9 +496,9 @@ impl<'a> Parser<'a> {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`Limits`]: crate::ast::Limits
|
||||
/// [`TableType`]: crate::ast::TableType
|
||||
/// [`RefType`]: crate::ast::RefType
|
||||
/// [`Limits`]: crate::core::Limits
|
||||
/// [`TableType`]: crate::core::TableType
|
||||
/// [`RefType`]: crate::core::RefType
|
||||
pub fn parse<T: Parse<'a>>(self) -> Result<T> {
|
||||
T::parse(self)
|
||||
}
|
||||
|
@ -552,7 +555,7 @@ impl<'a> Parser<'a> {
|
|||
/// ```
|
||||
///
|
||||
/// [spec]: https://webassembly.github.io/spec/core/text/types.html#limits
|
||||
/// [`Limits`]: crate::ast::Limits
|
||||
/// [`Limits`]: crate::core::Limits
|
||||
pub fn peek<T: Peek>(self) -> bool {
|
||||
T::peek(self.cursor())
|
||||
}
|
||||
|
@ -568,6 +571,17 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Same as the [`Parser::peek2`] method, except checks the next next token,
|
||||
/// not the next token.
|
||||
pub fn peek3<T: Peek>(self) -> bool {
|
||||
let mut cursor = self.cursor();
|
||||
if cursor.advance_token().is_some() && cursor.advance_token().is_some() {
|
||||
T::peek(cursor)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper structure to perform a sequence of `peek` operations and if
|
||||
/// they all fail produce a nice error message.
|
||||
///
|
||||
|
@ -592,7 +606,7 @@ impl<'a> Parser<'a> {
|
|||
/// parsing an [`Index`] we can do:
|
||||
///
|
||||
/// ```
|
||||
/// # use wast::*;
|
||||
/// # use wast::token::*;
|
||||
/// # use wast::parser::*;
|
||||
/// enum Index<'a> {
|
||||
/// Num(u32),
|
||||
|
@ -615,8 +629,8 @@ impl<'a> Parser<'a> {
|
|||
/// ```
|
||||
///
|
||||
/// [spec]: https://webassembly.github.io/spec/core/text/modules.html#indices
|
||||
/// [`Index`]: crate::ast::Index
|
||||
/// [`Id`]: crate::ast::Id
|
||||
/// [`Index`]: crate::token::Index
|
||||
/// [`Id`]: crate::token::Id
|
||||
pub fn lookahead1(self) -> Lookahead1<'a> {
|
||||
Lookahead1 {
|
||||
attempts: Vec::new(),
|
||||
|
@ -646,7 +660,8 @@ impl<'a> Parser<'a> {
|
|||
/// the exact definition, but it's close enough!
|
||||
///
|
||||
/// ```
|
||||
/// # use wast::*;
|
||||
/// # use wast::kw;
|
||||
/// # use wast::core::*;
|
||||
/// # use wast::parser::*;
|
||||
/// struct Module<'a> {
|
||||
/// fields: Vec<ModuleField<'a>>,
|
||||
|
@ -708,7 +723,7 @@ impl<'a> Parser<'a> {
|
|||
/// A low-level parsing method you probably won't use.
|
||||
///
|
||||
/// This is used to implement parsing of the most primitive types in the
|
||||
/// [`ast`](crate::ast) module. You probably don't want to use this, but
|
||||
/// [`core`](crate::core) module. You probably don't want to use this, but
|
||||
/// probably want to use something like [`Parser::parse`] or
|
||||
/// [`Parser::parens`].
|
||||
pub fn step<F, T>(self, f: F) -> Result<T>
|
||||
|
@ -793,7 +808,8 @@ impl<'a> Parser<'a> {
|
|||
/// to get an idea of how this works:
|
||||
///
|
||||
/// ```
|
||||
/// # use wast::*;
|
||||
/// # use wast::kw;
|
||||
/// # use wast::token::NameAnnotation;
|
||||
/// # use wast::parser::*;
|
||||
/// struct Module<'a> {
|
||||
/// name: Option<NameAnnotation<'a>>,
|
||||
|
@ -824,7 +840,8 @@ impl<'a> Parser<'a> {
|
|||
/// registered *before* we parse the parentheses of the annotation.
|
||||
///
|
||||
/// ```
|
||||
/// # use wast::*;
|
||||
/// # use wast::{kw, annotation};
|
||||
/// # use wast::core::Custom;
|
||||
/// # use wast::parser::*;
|
||||
/// struct Module<'a> {
|
||||
/// fields: Vec<ModuleField<'a>>,
|
||||
|
|
|
@ -1,335 +0,0 @@
|
|||
use crate::ast::*;
|
||||
use crate::resolve::gensym;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
|
||||
pub fn run(fields: &mut Vec<ModuleField>) {
|
||||
let mut cur = 0;
|
||||
let mut cx = Expander::default();
|
||||
|
||||
// Note that insertion here is somewhat tricky. We're injecting aliases
|
||||
// which will affect the index spaces for each kind of item being aliased.
|
||||
// In the final binary aliases will come before all locally defined items,
|
||||
// notably via the sorting in binary emission of this crate. To account for
|
||||
// this index space behavior we need to ensure that aliases all appear at
|
||||
// the right place in the module.
|
||||
//
|
||||
// The general algorithm here is that aliases discovered in the "header" of
|
||||
// the module, e.g. imports/aliases/types/etc, are all inserted preceding
|
||||
// the field that the alias is found within. After the header, however, the
|
||||
// position of the header is recorded and all future aliases will be
|
||||
// inserted at that location.
|
||||
//
|
||||
// This ends up meaning that aliases found in globals/functions/tables/etc
|
||||
// will precede all of those definitions, being positioned at a point that
|
||||
// should come after all the instances that are defined as well. Overall
|
||||
// this isn't the cleanest algorithm and probably isn't the final form of
|
||||
// those. It's hoped that discussion on WebAssembly/module-linking#25 might
|
||||
// lead to a good solution.
|
||||
let mut insertion_point = None;
|
||||
while cur < fields.len() {
|
||||
let field = &mut fields[cur];
|
||||
match field {
|
||||
ModuleField::Alias(_)
|
||||
| ModuleField::Type(_)
|
||||
| ModuleField::Import(_)
|
||||
| ModuleField::NestedModule(_)
|
||||
| ModuleField::Instance(_) => {}
|
||||
_ if insertion_point.is_none() => insertion_point = Some(cur),
|
||||
_ => {}
|
||||
}
|
||||
cx.process(field);
|
||||
if insertion_point.is_none() {
|
||||
for item in cx.to_prepend.drain(..) {
|
||||
fields.insert(cur, item);
|
||||
cur += 1;
|
||||
}
|
||||
}
|
||||
cur += 1;
|
||||
}
|
||||
if let Some(mut i) = insertion_point {
|
||||
for item in cx.to_prepend.drain(..) {
|
||||
fields.insert(i, item);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
assert!(cx.to_prepend.is_empty());
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Expander<'a> {
|
||||
to_prepend: Vec<ModuleField<'a>>,
|
||||
instances: HashMap<(Index<'a>, &'a str, ExportKind), Index<'a>>,
|
||||
parents: HashMap<(Index<'a>, Index<'a>, ExportKind), Index<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Expander<'a> {
|
||||
fn process(&mut self, field: &mut ModuleField<'a>) {
|
||||
match field {
|
||||
ModuleField::Alias(a) => {
|
||||
let id = gensym::fill(a.span, &mut a.id);
|
||||
match &mut a.source {
|
||||
AliasSource::InstanceExport { instance, export } => {
|
||||
self.expand(instance);
|
||||
self.instances
|
||||
.insert((*instance.unwrap_index(), export, a.kind), id.into());
|
||||
}
|
||||
AliasSource::Outer { module, index } => {
|
||||
self.parents.insert((*module, *index, a.kind), id.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::Instance(i) => {
|
||||
if let InstanceKind::Inline { module, args } = &mut i.kind {
|
||||
self.expand(module);
|
||||
for arg in args {
|
||||
self.expand(&mut arg.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::Elem(e) => {
|
||||
if let ElemKind::Active { table, offset, .. } = &mut e.kind {
|
||||
self.expand(table);
|
||||
self.expand_expr(offset);
|
||||
}
|
||||
match &mut e.payload {
|
||||
ElemPayload::Indices(funcs) => {
|
||||
for func in funcs {
|
||||
self.expand(func);
|
||||
}
|
||||
}
|
||||
ElemPayload::Exprs { exprs, .. } => {
|
||||
for expr in exprs {
|
||||
self.expand_expr(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::Data(e) => {
|
||||
if let DataKind::Active { memory, offset, .. } = &mut e.kind {
|
||||
self.expand(memory);
|
||||
self.expand_expr(offset);
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::Export(e) => self.expand(&mut e.index),
|
||||
|
||||
ModuleField::Func(f) => {
|
||||
self.expand_type_use(&mut f.ty);
|
||||
if let FuncKind::Inline { expression, .. } = &mut f.kind {
|
||||
self.expand_expr(expression);
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::Import(i) => self.expand_item_sig(&mut i.item),
|
||||
|
||||
ModuleField::Global(g) => {
|
||||
if let GlobalKind::Inline(expr) = &mut g.kind {
|
||||
self.expand_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
ModuleField::Start(s) => self.expand(s),
|
||||
|
||||
ModuleField::Tag(t) => match &mut t.ty {
|
||||
TagType::Exception(t) => self.expand_type_use(t),
|
||||
},
|
||||
|
||||
ModuleField::NestedModule(m) => match &mut m.kind {
|
||||
NestedModuleKind::Import { ty, .. } => self.expand_type_use(ty),
|
||||
NestedModuleKind::Inline { fields } => run(fields),
|
||||
},
|
||||
|
||||
ModuleField::Type(t) => match &mut t.def {
|
||||
TypeDef::Func(f) => f.expand(self),
|
||||
TypeDef::Struct(_) => {}
|
||||
TypeDef::Array(_) => {}
|
||||
TypeDef::Module(m) => m.expand(self),
|
||||
TypeDef::Instance(i) => i.expand(self),
|
||||
},
|
||||
|
||||
ModuleField::Custom(_) | ModuleField::Memory(_) | ModuleField::Table(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_item_sig(&mut self, sig: &mut ItemSig<'a>) {
|
||||
match &mut sig.kind {
|
||||
ItemKind::Func(t) => self.expand_type_use(t),
|
||||
ItemKind::Module(t) => self.expand_type_use(t),
|
||||
ItemKind::Instance(t) => self.expand_type_use(t),
|
||||
ItemKind::Table(_) => {}
|
||||
ItemKind::Memory(_) => {}
|
||||
ItemKind::Global(_) => {}
|
||||
ItemKind::Tag(t) => match t {
|
||||
TagType::Exception(t) => self.expand_type_use(t),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_type_use<T: Expand<'a>>(&mut self, ty: &mut TypeUse<'a, T>) {
|
||||
if let Some(index) = &mut ty.index {
|
||||
self.expand(index);
|
||||
}
|
||||
if let Some(inline) = &mut ty.inline {
|
||||
inline.expand(self);
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_expr(&mut self, expr: &mut Expression<'a>) {
|
||||
for instr in expr.instrs.iter_mut() {
|
||||
self.expand_instr(instr);
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_instr(&mut self, instr: &mut Instruction<'a>) {
|
||||
use Instruction::*;
|
||||
|
||||
if let Some(m) = instr.memarg_mut() {
|
||||
self.expand(&mut m.memory);
|
||||
}
|
||||
|
||||
match instr {
|
||||
Call(i) | ReturnCall(i) | RefFunc(i) => self.expand(&mut i.0),
|
||||
CallIndirect(i) | ReturnCallIndirect(i) => {
|
||||
self.expand(&mut i.table);
|
||||
self.expand_type_use(&mut i.ty);
|
||||
}
|
||||
TableInit(i) => self.expand(&mut i.table),
|
||||
MemoryInit(i) => self.expand(&mut i.mem),
|
||||
TableCopy(i) => {
|
||||
self.expand(&mut i.src);
|
||||
self.expand(&mut i.dst);
|
||||
}
|
||||
MemoryCopy(i) => {
|
||||
self.expand(&mut i.src);
|
||||
self.expand(&mut i.dst);
|
||||
}
|
||||
GlobalSet(g) | GlobalGet(g) => self.expand(&mut g.0),
|
||||
TableGet(t) | TableSet(t) | TableFill(t) | TableSize(t) | TableGrow(t) => {
|
||||
self.expand(&mut t.dst)
|
||||
}
|
||||
|
||||
MemorySize(m) | MemoryGrow(m) | MemoryFill(m) => self.expand(&mut m.mem),
|
||||
|
||||
Let(t) => self.expand_type_use(&mut t.block.ty),
|
||||
|
||||
Block(bt) | If(bt) | Loop(bt) | Try(bt) => self.expand_type_use(&mut bt.ty),
|
||||
|
||||
FuncBind(t) => self.expand_type_use(&mut t.ty),
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand<T>(&mut self, item: &mut ItemRef<'a, T>)
|
||||
where
|
||||
T: Into<ExportKind> + Copy,
|
||||
{
|
||||
match item {
|
||||
ItemRef::Outer { kind, module, idx } => {
|
||||
let key = (*module, *idx, (*kind).into());
|
||||
let idx = match self.parents.entry(key) {
|
||||
Entry::Occupied(e) => *e.get(),
|
||||
Entry::Vacant(v) => {
|
||||
let span = idx.span();
|
||||
let id = gensym::gen(span);
|
||||
self.to_prepend.push(ModuleField::Alias(Alias {
|
||||
span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
source: AliasSource::Outer {
|
||||
module: *module,
|
||||
index: *idx,
|
||||
},
|
||||
kind: (*kind).into(),
|
||||
}));
|
||||
*v.insert(Index::Id(id))
|
||||
}
|
||||
};
|
||||
*item = ItemRef::Item {
|
||||
kind: *kind,
|
||||
idx,
|
||||
exports: Vec::new(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: true,
|
||||
};
|
||||
}
|
||||
ItemRef::Item {
|
||||
kind,
|
||||
idx,
|
||||
exports,
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited,
|
||||
} => {
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
{
|
||||
*visited = true;
|
||||
}
|
||||
let mut cur = *idx;
|
||||
let len = exports.len();
|
||||
for (i, export) in exports.drain(..).enumerate() {
|
||||
let kind = if i < len - 1 {
|
||||
ExportKind::Instance
|
||||
} else {
|
||||
(*kind).into()
|
||||
};
|
||||
let key = (cur, export, kind);
|
||||
cur = match self.instances.entry(key) {
|
||||
Entry::Occupied(e) => *e.get(),
|
||||
Entry::Vacant(v) => {
|
||||
let span = idx.span();
|
||||
let id = gensym::gen(span);
|
||||
self.to_prepend.push(ModuleField::Alias(Alias {
|
||||
span,
|
||||
id: Some(id),
|
||||
name: None,
|
||||
kind,
|
||||
source: AliasSource::InstanceExport {
|
||||
instance: ItemRef::Item {
|
||||
kind: kw::instance(span),
|
||||
idx: cur,
|
||||
exports: Vec::new(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: true,
|
||||
},
|
||||
export,
|
||||
},
|
||||
}));
|
||||
*v.insert(Index::Id(id))
|
||||
}
|
||||
};
|
||||
}
|
||||
*idx = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Expand<'a> {
|
||||
fn expand(&mut self, cx: &mut Expander<'a>);
|
||||
}
|
||||
|
||||
impl<'a> Expand<'a> for FunctionType<'a> {
|
||||
fn expand(&mut self, _cx: &mut Expander<'a>) {}
|
||||
}
|
||||
|
||||
impl<'a> Expand<'a> for ModuleType<'a> {
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
for i in self.imports.iter_mut() {
|
||||
cx.expand_item_sig(&mut i.item);
|
||||
}
|
||||
for e in self.exports.iter_mut() {
|
||||
cx.expand_item_sig(&mut e.item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Expand<'a> for InstanceType<'a> {
|
||||
fn expand(&mut self, cx: &mut Expander<'a>) {
|
||||
for e in self.exports.iter_mut() {
|
||||
cx.expand_item_sig(&mut e.item);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,8 @@
|
|||
use crate::ast::{annotation, kw};
|
||||
//! Common tokens that implement the [`Parse`] trait which are otherwise not
|
||||
//! associated specifically with the wasm text format per se (useful in other
|
||||
//! contexts too perhaps).
|
||||
|
||||
use crate::annotation;
|
||||
use crate::lexer::FloatVal;
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use std::fmt;
|
||||
|
@ -217,65 +221,19 @@ impl Hash for Index<'_> {
|
|||
}
|
||||
|
||||
/// 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>,
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: bool,
|
||||
},
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
}
|
||||
pub struct ItemRef<'a, K> {
|
||||
pub kind: K,
|
||||
pub idx: Index<'a>,
|
||||
}
|
||||
|
||||
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,
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: false,
|
||||
})
|
||||
}
|
||||
let idx = parser.parse()?;
|
||||
Ok(ItemRef { kind, idx })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -293,39 +251,6 @@ impl<'a, K: Peek> Peek for ItemRef<'a, K> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
#[cfg(wast_check_exhaustive)]
|
||||
visited: false,
|
||||
}))
|
||||
} 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> {
|
|
@ -1,6 +1,8 @@
|
|||
use crate::ast::{self, kw};
|
||||
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
|
||||
use crate::{AssertExpression, NanPattern, V128Pattern};
|
||||
use crate::core::Expression;
|
||||
use crate::kw;
|
||||
use crate::parser::{self, Cursor, Parse, ParseBuffer, Parser, Peek, Result};
|
||||
use crate::token::{Id, Span};
|
||||
use crate::{AssertExpression, Error, Wat};
|
||||
|
||||
/// A parsed representation of a `*.wast` file.
|
||||
///
|
||||
|
@ -24,8 +26,8 @@ impl<'a> Parse<'a> for Wast<'a> {
|
|||
directives.push(parser.parens(|p| p.parse())?);
|
||||
}
|
||||
} else {
|
||||
let module = parser.parse::<ast::Wat>()?.module;
|
||||
directives.push(WastDirective::Module(module));
|
||||
let module = parser.parse::<Wat>()?;
|
||||
directives.push(WastDirective::Wat(QuoteWat::Wat(module)));
|
||||
}
|
||||
Ok(Wast { directives })
|
||||
}
|
||||
|
@ -39,7 +41,11 @@ impl Peek for WastDirectiveToken {
|
|||
Some((kw, _)) => kw,
|
||||
None => return false,
|
||||
};
|
||||
kw.starts_with("assert_") || kw == "module" || kw == "register" || kw == "invoke"
|
||||
kw.starts_with("assert_")
|
||||
|| kw == "module"
|
||||
|| kw == "component"
|
||||
|| kw == "register"
|
||||
|| kw == "invoke"
|
||||
}
|
||||
|
||||
fn display() -> &'static str {
|
||||
|
@ -54,61 +60,59 @@ impl Peek for WastDirectiveToken {
|
|||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
pub enum WastDirective<'a> {
|
||||
Module(ast::Module<'a>),
|
||||
QuoteModule {
|
||||
span: ast::Span,
|
||||
source: Vec<&'a [u8]>,
|
||||
},
|
||||
Wat(QuoteWat<'a>),
|
||||
AssertMalformed {
|
||||
span: ast::Span,
|
||||
module: QuoteModule<'a>,
|
||||
span: Span,
|
||||
module: QuoteWat<'a>,
|
||||
message: &'a str,
|
||||
},
|
||||
AssertInvalid {
|
||||
span: ast::Span,
|
||||
module: QuoteModule<'a>,
|
||||
span: Span,
|
||||
module: QuoteWat<'a>,
|
||||
message: &'a str,
|
||||
},
|
||||
Register {
|
||||
span: ast::Span,
|
||||
span: Span,
|
||||
name: &'a str,
|
||||
module: Option<ast::Id<'a>>,
|
||||
module: Option<Id<'a>>,
|
||||
},
|
||||
Invoke(WastInvoke<'a>),
|
||||
AssertTrap {
|
||||
span: ast::Span,
|
||||
span: Span,
|
||||
exec: WastExecute<'a>,
|
||||
message: &'a str,
|
||||
},
|
||||
AssertReturn {
|
||||
span: ast::Span,
|
||||
span: Span,
|
||||
exec: WastExecute<'a>,
|
||||
results: Vec<ast::AssertExpression<'a>>,
|
||||
results: Vec<AssertExpression<'a>>,
|
||||
},
|
||||
AssertExhaustion {
|
||||
span: ast::Span,
|
||||
span: Span,
|
||||
call: WastInvoke<'a>,
|
||||
message: &'a str,
|
||||
},
|
||||
AssertUnlinkable {
|
||||
span: ast::Span,
|
||||
module: ast::Module<'a>,
|
||||
span: Span,
|
||||
module: Wat<'a>,
|
||||
message: &'a str,
|
||||
},
|
||||
AssertException {
|
||||
span: ast::Span,
|
||||
span: Span,
|
||||
exec: WastExecute<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
impl WastDirective<'_> {
|
||||
/// Returns the location in the source that this directive was defined at
|
||||
pub fn span(&self) -> ast::Span {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
WastDirective::Module(m) => m.span,
|
||||
WastDirective::Wat(QuoteWat::Wat(Wat::Module(m))) => m.span,
|
||||
WastDirective::Wat(QuoteWat::Wat(Wat::Component(c))) => c.span,
|
||||
WastDirective::Wat(QuoteWat::QuoteModule(span, _)) => *span,
|
||||
WastDirective::Wat(QuoteWat::QuoteComponent(span, _)) => *span,
|
||||
WastDirective::AssertMalformed { span, .. }
|
||||
| WastDirective::Register { span, .. }
|
||||
| WastDirective::QuoteModule { span, .. }
|
||||
| WastDirective::AssertTrap { span, .. }
|
||||
| WastDirective::AssertReturn { span, .. }
|
||||
| WastDirective::AssertExhaustion { span, .. }
|
||||
|
@ -123,18 +127,8 @@ impl WastDirective<'_> {
|
|||
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()?))
|
||||
}
|
||||
if l.peek::<kw::module>() || l.peek::<kw::component>() {
|
||||
Ok(WastDirective::Wat(parser.parse()?))
|
||||
} else if l.peek::<kw::assert_malformed>() {
|
||||
let span = parser.parse::<kw::assert_malformed>()?.0;
|
||||
Ok(WastDirective::AssertMalformed {
|
||||
|
@ -177,69 +171,6 @@ impl<'a> Parse<'a> for WastDirective<'a> {
|
|||
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 {
|
||||
|
@ -251,7 +182,7 @@ impl<'a> Parse<'a> for WastDirective<'a> {
|
|||
let span = parser.parse::<kw::assert_unlinkable>()?.0;
|
||||
Ok(WastDirective::AssertUnlinkable {
|
||||
span,
|
||||
module: parser.parens(|p| p.parse())?,
|
||||
module: parser.parens(parse_wat)?,
|
||||
message: parser.parse()?,
|
||||
})
|
||||
} else if l.peek::<kw::assert_exception>() {
|
||||
|
@ -270,9 +201,9 @@ impl<'a> Parse<'a> for WastDirective<'a> {
|
|||
#[derive(Debug)]
|
||||
pub enum WastExecute<'a> {
|
||||
Invoke(WastInvoke<'a>),
|
||||
Module(ast::Module<'a>),
|
||||
Wat(Wat<'a>),
|
||||
Get {
|
||||
module: Option<ast::Id<'a>>,
|
||||
module: Option<Id<'a>>,
|
||||
global: &'a str,
|
||||
},
|
||||
}
|
||||
|
@ -282,8 +213,8 @@ impl<'a> Parse<'a> for WastExecute<'a> {
|
|||
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::module>() || l.peek::<kw::component>() {
|
||||
Ok(WastExecute::Wat(parse_wat(parser)?))
|
||||
} else if l.peek::<kw::get>() {
|
||||
parser.parse::<kw::get>()?;
|
||||
Ok(WastExecute::Get {
|
||||
|
@ -296,13 +227,27 @@ impl<'a> Parse<'a> for WastExecute<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_wat<'a>(parser: Parser<'a>) -> Result<Wat<'a>> {
|
||||
// Note that this doesn't use `Parse for Wat` since the `parser` provided
|
||||
// has already peeled back the first layer of parentheses while `Parse for
|
||||
// Wat` expects to be the top layer which means it also tries to peel off
|
||||
// the parens. Instead we can skip the sugar that `Wat` has for simply a
|
||||
// list of fields (no `(module ...)` container) and just parse the `Module`
|
||||
// itself.
|
||||
if parser.peek::<kw::component>() {
|
||||
Ok(Wat::Component(parser.parse()?))
|
||||
} else {
|
||||
Ok(Wat::Module(parser.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
pub struct WastInvoke<'a> {
|
||||
pub span: ast::Span,
|
||||
pub module: Option<ast::Id<'a>>,
|
||||
pub span: Span,
|
||||
pub module: Option<Id<'a>>,
|
||||
pub name: &'a str,
|
||||
pub args: Vec<ast::Expression<'a>>,
|
||||
pub args: Vec<Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for WastInvoke<'a> {
|
||||
|
@ -325,60 +270,61 @@ impl<'a> Parse<'a> for WastInvoke<'a> {
|
|||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
pub enum QuoteModule<'a> {
|
||||
Module(ast::Module<'a>),
|
||||
Quote(Vec<&'a [u8]>),
|
||||
pub enum QuoteWat<'a> {
|
||||
Wat(Wat<'a>),
|
||||
QuoteModule(Span, Vec<(Span, &'a [u8])>),
|
||||
QuoteComponent(Span, Vec<(Span, &'a [u8])>),
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for QuoteModule<'a> {
|
||||
impl QuoteWat<'_> {
|
||||
/// Encodes this module to bytes, either by encoding the module directly or
|
||||
/// parsing the contents and then encoding it.
|
||||
pub fn encode(&mut self) -> Result<Vec<u8>, Error> {
|
||||
let (source, prefix) = match self {
|
||||
QuoteWat::Wat(m) => return m.encode(),
|
||||
QuoteWat::QuoteModule(_, source) => (source, None),
|
||||
QuoteWat::QuoteComponent(_, source) => (source, Some("(component")),
|
||||
};
|
||||
let mut ret = String::new();
|
||||
for (span, src) in source {
|
||||
match std::str::from_utf8(src) {
|
||||
Ok(s) => ret.push_str(s),
|
||||
Err(_) => {
|
||||
return Err(Error::new(*span, "malformed UTF-8 encoding".to_string()));
|
||||
}
|
||||
}
|
||||
ret.push_str(" ");
|
||||
}
|
||||
if let Some(prefix) = prefix {
|
||||
ret.insert_str(0, prefix);
|
||||
ret.push_str(")");
|
||||
}
|
||||
let buf = ParseBuffer::new(&ret)?;
|
||||
let mut wat = parser::parse::<Wat<'_>>(&buf)?;
|
||||
wat.encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for QuoteWat<'a> {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
if parser.peek2::<kw::quote>() {
|
||||
parser.parse::<kw::module>()?;
|
||||
parser.parse::<kw::quote>()?;
|
||||
let ctor = if parser.peek::<kw::component>() {
|
||||
parser.parse::<kw::component>()?;
|
||||
QuoteWat::QuoteComponent
|
||||
} else {
|
||||
parser.parse::<kw::module>()?;
|
||||
QuoteWat::QuoteModule
|
||||
};
|
||||
let span = parser.parse::<kw::quote>()?.0;
|
||||
let mut src = Vec::new();
|
||||
while !parser.is_empty() {
|
||||
src.push(parser.parse()?);
|
||||
let span = parser.cur_span();
|
||||
let string = parser.parse()?;
|
||||
src.push((span, string));
|
||||
}
|
||||
Ok(QuoteModule::Quote(src))
|
||||
Ok(ctor(span, src))
|
||||
} else {
|
||||
Ok(QuoteModule::Module(parser.parse()?))
|
||||
Ok(QuoteWat::Wat(parse_wat(parser)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 { .. }
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
use crate::component::Component;
|
||||
use crate::core::{Module, ModuleField, ModuleKind};
|
||||
use crate::kw;
|
||||
use crate::parser::{Parse, Parser, Result};
|
||||
use crate::token::Span;
|
||||
|
||||
/// 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.
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Wat<'a> {
|
||||
Module(Module<'a>),
|
||||
Component(Component<'a>),
|
||||
}
|
||||
|
||||
impl Wat<'_> {
|
||||
fn validate(&self, parser: Parser<'_>) -> Result<()> {
|
||||
match self {
|
||||
Wat::Module(m) => m.validate(parser),
|
||||
Wat::Component(c) => c.validate(parser),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes this `Wat` to binary form. This calls either [`Module::encode`]
|
||||
/// or [`Component::encode`].
|
||||
pub fn encode(&mut self) -> std::result::Result<Vec<u8>, crate::Error> {
|
||||
match self {
|
||||
Wat::Module(m) => m.encode(),
|
||||
Wat::Component(c) => c.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 wat = if parser.peek2::<kw::module>() {
|
||||
Wat::Module(parser.parens(|parser| parser.parse())?)
|
||||
} else if parser.peek2::<kw::component>() {
|
||||
Wat::Component(parser.parens(|parser| parser.parse())?)
|
||||
} else {
|
||||
let fields = ModuleField::parse_remaining(parser)?;
|
||||
Wat::Module(Module {
|
||||
span: Span { offset: 0 },
|
||||
id: None,
|
||||
name: None,
|
||||
kind: ModuleKind::Text(fields),
|
||||
})
|
||||
};
|
||||
wat.validate(parser)?;
|
||||
Ok(wat)
|
||||
}
|
||||
}
|
|
@ -95,14 +95,10 @@ fn assert_local_name(name: &str, wat: &str) -> anyhow::Result<()> {
|
|||
|
||||
fn get_name_section(wasm: &[u8]) -> anyhow::Result<NameSectionReader<'_>> {
|
||||
for payload in Parser::new(0).parse_all(&wasm) {
|
||||
if let Payload::CustomSection {
|
||||
name: "name",
|
||||
data,
|
||||
data_offset,
|
||||
range: _,
|
||||
} = payload?
|
||||
{
|
||||
return Ok(NameSectionReader::new(data, data_offset)?);
|
||||
if let Payload::CustomSection(c) = payload? {
|
||||
if c.name() == "name" {
|
||||
return Ok(NameSectionReader::new(c.data(), c.data_offset())?);
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("no name section found");
|
||||
|
@ -110,7 +106,7 @@ fn get_name_section(wasm: &[u8]) -> anyhow::Result<NameSectionReader<'_>> {
|
|||
|
||||
#[test]
|
||||
fn custom_section_order() -> anyhow::Result<()> {
|
||||
let wasm = wat::parse_str(
|
||||
let bytes = wat::parse_str(
|
||||
r#"
|
||||
(module
|
||||
(@custom "A" "aaa")
|
||||
|
@ -131,33 +127,71 @@ fn custom_section_order() -> anyhow::Result<()> {
|
|||
"#,
|
||||
)?;
|
||||
macro_rules! assert_matches {
|
||||
($a:expr, $b:pat $(,)?) => {
|
||||
($a:expr, $b:pat $(if $cond:expr)? $(,)?) => {
|
||||
match &$a {
|
||||
$b => {}
|
||||
$b $(if $cond)? => {}
|
||||
a => panic!("`{:?}` doesn't match `{}`", a, stringify!($b)),
|
||||
}
|
||||
};
|
||||
}
|
||||
let wasm = Parser::new(0)
|
||||
.parse_all(&wasm)
|
||||
.parse_all(&bytes)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
assert_matches!(wasm[0], Payload::Version { .. });
|
||||
assert_matches!(wasm[1], Payload::CustomSection { name: "K", .. });
|
||||
assert_matches!(wasm[2], Payload::CustomSection { name: "F", .. });
|
||||
assert_matches!(
|
||||
wasm[1],
|
||||
Payload::CustomSection(c) if c.name() == "K"
|
||||
);
|
||||
assert_matches!(
|
||||
wasm[2],
|
||||
Payload::CustomSection(c) if c.name() == "F"
|
||||
);
|
||||
assert_matches!(wasm[3], Payload::TypeSection(_));
|
||||
assert_matches!(wasm[4], Payload::CustomSection { name: "E", .. });
|
||||
assert_matches!(wasm[5], Payload::CustomSection { name: "C", .. });
|
||||
assert_matches!(wasm[6], Payload::CustomSection { name: "J", .. });
|
||||
assert_matches!(
|
||||
wasm[4],
|
||||
Payload::CustomSection(c) if c.name() == "E"
|
||||
);
|
||||
assert_matches!(
|
||||
wasm[5],
|
||||
Payload::CustomSection(c) if c.name() == "C"
|
||||
);
|
||||
assert_matches!(
|
||||
wasm[6],
|
||||
Payload::CustomSection(c) if c.name() == "J"
|
||||
);
|
||||
assert_matches!(wasm[7], Payload::FunctionSection(_));
|
||||
assert_matches!(wasm[8], Payload::CustomSection { name: "B", .. });
|
||||
assert_matches!(wasm[9], Payload::CustomSection { name: "I", .. });
|
||||
assert_matches!(
|
||||
wasm[8],
|
||||
Payload::CustomSection(c) if c.name() == "B"
|
||||
);
|
||||
assert_matches!(
|
||||
wasm[9],
|
||||
Payload::CustomSection(c) if c.name() == "I"
|
||||
);
|
||||
assert_matches!(wasm[10], Payload::TableSection(_));
|
||||
assert_matches!(wasm[11], Payload::CodeSectionStart { .. });
|
||||
assert_matches!(wasm[12], Payload::CodeSectionEntry { .. });
|
||||
assert_matches!(wasm[13], Payload::CustomSection { name: "H", .. });
|
||||
assert_matches!(wasm[14], Payload::CustomSection { name: "G", .. });
|
||||
assert_matches!(wasm[15], Payload::CustomSection { name: "A", .. });
|
||||
assert_matches!(wasm[16], Payload::CustomSection { name: "D", .. });
|
||||
assert_matches!(wasm[17], Payload::End);
|
||||
assert_matches!(
|
||||
wasm[13],
|
||||
Payload::CustomSection(c) if c.name() == "H"
|
||||
);
|
||||
assert_matches!(
|
||||
wasm[14],
|
||||
Payload::CustomSection(c) if c.name() == "G"
|
||||
);
|
||||
assert_matches!(
|
||||
wasm[15],
|
||||
Payload::CustomSection(c) if c.name() == "A"
|
||||
);
|
||||
assert_matches!(
|
||||
wasm[16],
|
||||
Payload::CustomSection(c) if c.name() == "D"
|
||||
);
|
||||
|
||||
match &wasm[17] {
|
||||
Payload::End(x) if *x == bytes.len() => {}
|
||||
p => panic!("`{:?}` doesn't match expected length of {}", p, bytes.len()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ fn parse_comments() -> anyhow::Result<()> {
|
|||
"#,
|
||||
)?;
|
||||
|
||||
let d: Documented<wast::Module> = parser::parse(&buf)?;
|
||||
let d: Documented<wast::core::Module> = parser::parse(&buf)?;
|
||||
assert_eq!(d.comments.comments, vec![" hello", " again "]);
|
||||
drop(d.item);
|
||||
|
||||
|
@ -66,7 +66,7 @@ multiple;)
|
|||
"#,
|
||||
)?;
|
||||
|
||||
let d: Documented<wast::Func> = parser::parse(&buf)?;
|
||||
let d: Documented<wast::core::Func> = parser::parse(&buf)?;
|
||||
assert_eq!(
|
||||
d.comments.comments,
|
||||
vec![" this", " is\non\nmultiple", " lines"]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"dec2dec4738c8ff5e0176744a1e16619ee371a8a1616e055eab15cd97ad26e83","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"86861dc59a785c0eb143e1798668e29076e25f69d7e802b5425054862c635be3","src/lib.rs":"b272ee59a5a1b713625cdf4443e5055d888966b9ec78014e01087895e9fc09bc"},"package":"ab98ed25494f97c69f28758617f27c3e92e5336040b5c3a14634f2dd3fe61830"}
|
||||
{"files":{"Cargo.toml":"2faa554ccc82ab2ffb485ac05f9738f3c6675e0b051f4b8ec3e92883fbd21f64","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"86861dc59a785c0eb143e1798668e29076e25f69d7e802b5425054862c635be3","src/lib.rs":"806f68f35b3c309b7dbdb24f14da254a9f7f5b14eef3b8015c95c3e9753e3bb0"},"package":"48b3b9b3e39e66c7fd3f8be785e74444d216260f491e93369e317ed6482ff80f"}
|
|
@ -10,15 +10,19 @@
|
|||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
name = "wat"
|
||||
version = "1.0.41"
|
||||
version = "1.0.43"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
description = "Rust parser for the WebAssembly Text format, WAT\n"
|
||||
description = """
|
||||
Rust parser for the WebAssembly Text format, WAT
|
||||
"""
|
||||
homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat"
|
||||
documentation = "https://docs.rs/wat"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies.wast]
|
||||
version = "39.0.0"
|
||||
version = "41.0.0"
|
||||
|
|
|
@ -218,7 +218,7 @@ pub fn parse_str(wat: impl AsRef<str>) -> Result<Vec<u8>> {
|
|||
fn _parse_str(wat: &str) -> Result<Vec<u8>> {
|
||||
let buf = ParseBuffer::new(&wat).map_err(|e| Error::cvt(e, wat))?;
|
||||
let mut ast = parser::parse::<wast::Wat>(&buf).map_err(|e| Error::cvt(e, wat))?;
|
||||
Ok(ast.module.encode().map_err(|e| Error::cvt(e, wat))?)
|
||||
Ok(ast.encode().map_err(|e| Error::cvt(e, wat))?)
|
||||
}
|
||||
|
||||
/// A convenience type definition for `Result` where the error is [`Error`]
|
||||
|
|
Загрузка…
Ссылка в новой задаче