Bug 1335652 - wasm exceptions part 7: vendor the latest wat r=rhunt

Differential Revision: https://phabricator.services.mozilla.com/D99594
This commit is contained in:
Asumu Takikawa 2020-12-15 20:52:05 +00:00
parent d7a0965c77
commit bd345c38b9
14 changed files with 362 additions and 85 deletions

8
Cargo.lock generated
View File

@ -5641,18 +5641,18 @@ source = "git+https://github.com/mozilla-spidermonkey/wasm-tools?rev=1b7763faa48
[[package]] [[package]]
name = "wast" name = "wast"
version = "26.0.1" version = "29.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3f174eed73e885ede6c8fcc3fbea8c3757afa521840676496cde56bb742ddab" checksum = "dcf2268937131d63c3d833242bf5e075406f9ed868b4265f3280e15dac29ac18"
dependencies = [ dependencies = [
"leb128", "leb128",
] ]
[[package]] [[package]]
name = "wat" name = "wat"
version = "1.0.27" version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b2dccbce4d0e14875091846e110a2369267b18ddd0d6423479b88dad914d71" checksum = "0d11a88d953b298172d218d18f22853f4e6e12873b62755d05617b864d312c68"
dependencies = [ dependencies = [
"wast", "wast",
] ]

View File

@ -20,5 +20,5 @@ smoosh = ['jsrust_shared/smoosh']
jsrust_shared = { path = "./shared" } jsrust_shared = { path = "./shared" }
# Workaround for https://github.com/rust-lang/rust/issues/58393 # Workaround for https://github.com/rust-lang/rust/issues/58393
mozglue-static = { path = "../../../mozglue/static/rust" } mozglue-static = { path = "../../../mozglue/static/rust" }
wat = { version = "1.0.27" } wat = { version = "1.0.30" }
wasmparser = { version = "0.48.2" } wasmparser = { version = "0.48.2" }

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
[package] [package]
edition = "2018" edition = "2018"
name = "wast" name = "wast"
version = "26.0.1" version = "29.0.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"] 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\n"
homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast" homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast"

View File

@ -84,7 +84,9 @@ enum If<'a> {
enum Try<'a> { enum Try<'a> {
/// Next thing to parse is the `do` block. /// Next thing to parse is the `do` block.
Do(Instruction<'a>), Do(Instruction<'a>),
/// Next thing to parse is the `catch` block. /// Next thing to parse is `catch`/`catch_all`, or `unwind`.
CatchOrUnwind,
/// Next thing to parse is a `catch` block or `catch_all`.
Catch, Catch,
/// This `try` statement has finished parsing and if anything remains it's a /// This `try` statement has finished parsing and if anything remains it's a
/// syntax error. /// syntax error.
@ -195,8 +197,8 @@ impl<'a> ExpressionParser<'a> {
Level::Try(Try::Do(_)) => { Level::Try(Try::Do(_)) => {
return Err(parser.error("previous `try` had no `do`")); return Err(parser.error("previous `try` had no `do`"));
} }
Level::Try(Try::Catch) => { Level::Try(Try::CatchOrUnwind) => {
return Err(parser.error("previous `try` had no `catch`")); return Err(parser.error("previous `try` had no `catch`, `catch_all`, or `unwind`"));
} }
Level::Try(_) => { Level::Try(_) => {
self.instrs.push(Instruction::End(None)); self.instrs.push(Instruction::End(None));
@ -305,7 +307,7 @@ impl<'a> ExpressionParser<'a> {
/// than an `if` as the syntactic form is: /// than an `if` as the syntactic form is:
/// ///
/// ```wat /// ```wat
/// (try (do $do) (catch $catch)) /// (try (do $do) (catch $event $catch))
/// ``` /// ```
/// ///
/// where the `do` and `catch` keywords are mandatory, even for an empty /// where the `do` and `catch` keywords are mandatory, even for an empty
@ -328,7 +330,7 @@ impl<'a> ExpressionParser<'a> {
if parser.parse::<Option<kw::r#do>>()?.is_some() { if parser.parse::<Option<kw::r#do>>()?.is_some() {
// The state is advanced here only if the parse succeeds in // The state is advanced here only if the parse succeeds in
// order to strictly require the keyword. // order to strictly require the keyword.
*i = Try::Catch; *i = Try::CatchOrUnwind;
self.stack.push(Level::TryArm); self.stack.push(Level::TryArm);
return Ok(true); return Ok(true);
} }
@ -338,10 +340,26 @@ impl<'a> ExpressionParser<'a> {
return Ok(false); return Ok(false);
} }
// `catch` handled similar to `do`, including requiring the keyword. // After a try's `do`, there are several possible kinds of handlers.
if let Try::Catch = i { if let Try::CatchOrUnwind = i {
self.instrs.push(Instruction::Catch); // `catch` may be followed by more `catch`s or `catch_all`.
if parser.parse::<Option<kw::catch>>()?.is_some() { if parser.parse::<Option<kw::catch>>()?.is_some() {
let evt = parser.parse::<ast::Index<'a>>()?;
self.instrs.push(Instruction::Catch(evt));
*i = Try::Catch;
self.stack.push(Level::TryArm);
return Ok(true);
}
// `catch_all` can only come at the end and has no argument.
if parser.parse::<Option<kw::catch_all>>()?.is_some() {
self.instrs.push(Instruction::CatchAll);
*i = Try::End;
self.stack.push(Level::TryArm);
return Ok(true);
}
// `unwind` is similar to `catch_all`.
if parser.parse::<Option<kw::unwind>>()?.is_some() {
self.instrs.push(Instruction::Unwind);
*i = Try::End; *i = Try::End;
self.stack.push(Level::TryArm); self.stack.push(Level::TryArm);
return Ok(true); return Ok(true);
@ -349,6 +367,23 @@ impl<'a> ExpressionParser<'a> {
return Ok(false); return Ok(false);
} }
if let Try::Catch = i {
if parser.parse::<Option<kw::catch>>()?.is_some() {
let evt = parser.parse::<ast::Index<'a>>()?;
self.instrs.push(Instruction::Catch(evt));
*i = Try::Catch;
self.stack.push(Level::TryArm);
return Ok(true);
}
if parser.parse::<Option<kw::catch_all>>()?.is_some() {
self.instrs.push(Instruction::CatchAll);
*i = Try::End;
self.stack.push(Level::TryArm);
return Ok(true);
}
return Err(parser.error("unexpected items after `catch`"));
}
Err(parser.error("too many payloads inside of `(try)`")) Err(parser.error("too many payloads inside of `(try)`"))
} }
} }
@ -997,11 +1032,12 @@ instructions! {
V128Load64Zero(MemArg<8>) : [0xfd, 0xfd] : "v128.load64_zero", V128Load64Zero(MemArg<8>) : [0xfd, 0xfd] : "v128.load64_zero",
// Exception handling proposal // Exception handling proposal
CatchAll : [0x05] : "catch_all", // Reuses the else opcode.
Try(BlockType<'a>) : [0x06] : "try", Try(BlockType<'a>) : [0x06] : "try",
Catch : [0x07] : "catch", Catch(ast::Index<'a>) : [0x07] : "catch",
Throw(ast::Index<'a>) : [0x08] : "throw", Throw(ast::Index<'a>) : [0x08] : "throw",
Rethrow : [0x09] : "rethrow", Rethrow(ast::Index<'a>) : [0x09] : "rethrow",
BrOnExn(BrOnExn<'a>) : [0x0a] : "br_on_exn", Unwind : [0x0a] : "unwind",
} }
} }

View File

@ -1,5 +1,5 @@
use crate::ast::{self, kw}; use crate::ast::{self, kw};
use crate::parser::{Parse, Parser, Result}; use crate::parser::{Lookahead1, Parse, Parser, Peek, Result};
/// A defined WebAssembly memory instance inside of a module. /// A defined WebAssembly memory instance inside of a module.
#[derive(Debug)] #[derive(Debug)]
@ -33,7 +33,7 @@ pub enum MemoryKind<'a> {
/// Whether or not this will be creating a 32-bit memory /// Whether or not this will be creating a 32-bit memory
is_32: bool, is_32: bool,
/// The inline data specified for this memory /// The inline data specified for this memory
data: Vec<&'a [u8]>, data: Vec<DataVal<'a>>,
}, },
} }
@ -99,7 +99,7 @@ pub struct Data<'a> {
/// Bytes for this `Data` segment, viewed as the concatenation of all the /// Bytes for this `Data` segment, viewed as the concatenation of all the
/// contained slices. /// contained slices.
pub data: Vec<&'a [u8]>, pub data: Vec<DataVal<'a>>,
} }
/// Different kinds of data segments, either passive or active. /// Different kinds of data segments, either passive or active.
@ -170,3 +170,83 @@ impl<'a> Parse<'a> for Data<'a> {
}) })
} }
} }
/// Differnet ways the value of a data segment can be defined.
#[derive(Debug)]
#[allow(missing_docs)]
pub enum DataVal<'a> {
String(&'a [u8]),
Integral(Vec<u8>),
}
impl DataVal<'_> {
/// Returns the length, in bytes, of the memory used to represent this data
/// value.
pub fn len(&self) -> usize {
match self {
DataVal::String(s) => s.len(),
DataVal::Integral(s) => s.len(),
}
}
/// Pushes the value of this data value onto the provided list of bytes.
pub fn push_onto(&self, dst: &mut Vec<u8>) {
match self {
DataVal::String(s) => dst.extend_from_slice(s),
DataVal::Integral(s) => dst.extend_from_slice(s),
}
}
}
impl<'a> Parse<'a> for DataVal<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
if !parser.peek::<ast::LParen>() {
return Ok(DataVal::String(parser.parse()?));
}
return parser.parens(|p| {
let mut result = Vec::new();
let mut lookahead = p.lookahead1();
let l = &mut lookahead;
let r = &mut result;
if consume::<kw::i8, i8, _>(p, l, r, |u, v| v.push(u as u8))?
|| consume::<kw::i16, i16, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
|| consume::<kw::i32, i32, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
|| consume::<kw::i64, i64, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
|| consume::<kw::f32, ast::Float32, _>(p, l, r, |u, v| {
v.extend(&u.bits.to_le_bytes())
})?
|| consume::<kw::f64, ast::Float64, _>(p, l, r, |u, v| {
v.extend(&u.bits.to_le_bytes())
})?
|| consume::<kw::v128, ast::V128Const, _>(p, l, r, |u, v| {
v.extend(&u.to_le_bytes())
})?
{
Ok(DataVal::Integral(result))
} else {
Err(lookahead.error())
}
});
fn consume<'a, T: Peek + Parse<'a>, U: Parse<'a>, F>(
parser: Parser<'a>,
lookahead: &mut Lookahead1<'a>,
dst: &mut Vec<u8>,
push: F,
) -> Result<bool>
where
F: Fn(U, &mut Vec<u8>),
{
if !lookahead.peek::<T>() {
return Ok(false);
}
parser.parse::<T>()?;
while !parser.is_empty() {
let val = parser.parse::<U>()?;
push(val, dst);
}
Ok(true)
}
}
}

View File

@ -35,6 +35,8 @@
/// } /// }
/// } /// }
/// ``` /// ```
///
/// Note that the keyword name can only start with a lower-case letter, i.e. 'a'..'z'.
#[macro_export] #[macro_export]
macro_rules! custom_keyword { macro_rules! custom_keyword {
($name:ident) => { ($name:ident) => {
@ -344,6 +346,7 @@ pub mod kw {
custom_keyword!(binary); custom_keyword!(binary);
custom_keyword!(block); custom_keyword!(block);
custom_keyword!(catch); custom_keyword!(catch);
custom_keyword!(catch_all);
custom_keyword!(code); custom_keyword!(code);
custom_keyword!(data); custom_keyword!(data);
custom_keyword!(declare); custom_keyword!(declare);
@ -414,6 +417,7 @@ pub mod kw {
custom_keyword!(table); custom_keyword!(table);
custom_keyword!(then); custom_keyword!(then);
custom_keyword!(r#try = "try"); custom_keyword!(r#try = "try");
custom_keyword!(unwind);
custom_keyword!(v128); custom_keyword!(v128);
} }

View File

@ -389,6 +389,8 @@ pub enum MemoryType {
B64 { B64 {
/// Limits on the page sizes of this memory /// Limits on the page sizes of this memory
limits: Limits64, limits: Limits64,
/// Whether or not this is a shared (atomic) memory type
shared: bool,
}, },
} }
@ -397,7 +399,8 @@ impl<'a> Parse<'a> for MemoryType {
if parser.peek::<kw::i64>() { if parser.peek::<kw::i64>() {
parser.parse::<kw::i64>()?; parser.parse::<kw::i64>()?;
let limits = parser.parse()?; let limits = parser.parse()?;
Ok(MemoryType::B64 { limits }) let shared = parser.parse::<Option<kw::shared>>()?.is_some();
Ok(MemoryType::B64 { limits, shared })
} else { } else {
parser.parse::<Option<kw::i32>>()?; parser.parse::<Option<kw::i32>>()?;
let limits = parser.parse()?; let limits = parser.parse()?;

View File

@ -61,15 +61,6 @@ fn encode_fields(
e.custom_sections(BeforeFirst); e.custom_sections(BeforeFirst);
// let moduletys = modules
// .iter()
// .map(|m| match &m.kind {
// NestedModuleKind::Inline { ty, .. } => ty.as_ref().expect("type should be filled in"),
// _ => panic!("only inline modules should be present now"),
// })
// .collect::<Vec<_>>();
// e.section_list(100, Module, &moduletys);
let mut items = fields let mut items = fields
.iter() .iter()
.filter(|i| match i { .filter(|i| match i {
@ -106,13 +97,13 @@ fn encode_fields(
} }
list!(1, Type); list!(1, Type);
list!(2, Import); list!(2, Import);
list!(100, NestedModule, Module, |m| match &m.kind { list!(14, NestedModule, Module, |m| match &m.kind {
NestedModuleKind::Inline { ty, .. } => NestedModuleKind::Inline { ty, .. } =>
ty.as_ref().expect("type should be filled in"), ty.as_ref().expect("type should be filled in"),
_ => panic!("only inline modules should be present now"), _ => panic!("only inline modules should be present now"),
}); });
list!(101, Instance); list!(15, Instance);
list!(102, Alias); list!(16, Alias);
} }
} }
@ -132,7 +123,7 @@ fn encode_fields(
if contains_bulk_memory(&funcs) { if contains_bulk_memory(&funcs) {
e.section(12, &data.len()); e.section(12, &data.len());
} }
e.section_list(103, ModuleCode, &modules); e.section_list(17, ModuleCode, &modules);
e.section_list(10, Code, &funcs); e.section_list(10, Code, &funcs);
e.section_list(11, Data, &data); e.section_list(11, Data, &data);
@ -450,8 +441,8 @@ impl Encode for Import<'_> {
match self.field { match self.field {
Some(s) => s.encode(e), Some(s) => s.encode(e),
None => { None => {
e.push(0x01); e.push(0x00);
e.push(0xc0); e.push(0xff);
} }
} }
self.item.encode(e); self.item.encode(e);
@ -547,8 +538,10 @@ impl Encode for MemoryType {
max.encode(e); max.encode(e);
} }
} }
MemoryType::B64 { limits } => { MemoryType::B64 { limits, shared } => {
let flags = (limits.max.is_some() as u8) | 0x04; let flag_max = limits.max.is_some() as u8;
let flag_shared = *shared as u8;
let flags = flag_max | (flag_shared << 1) | 0x04;
e.push(flags); e.push(flags);
limits.min.encode(e); limits.min.encode(e);
if let Some(max) = limits.max { if let Some(max) = limits.max {
@ -651,6 +644,9 @@ impl Encode for Elem<'_> {
fn encode(&self, e: &mut Vec<u8>) { fn encode(&self, e: &mut Vec<u8>) {
// Try to switch element expressions to indices if we can which uses a // Try to switch element expressions to indices if we can which uses a
// more MVP-compatible encoding. // more MVP-compatible encoding.
//
// FIXME(WebAssembly/wabt#1447) ideally we wouldn't do this so we could
// be faithful to the original format.
let mut to_encode = self.payload.clone(); let mut to_encode = self.payload.clone();
if let ElemPayload::Exprs { if let ElemPayload::Exprs {
ty: ty:
@ -769,8 +765,8 @@ impl Encode for Data<'_> {
} }
} }
self.data.iter().map(|l| l.len()).sum::<usize>().encode(e); self.data.iter().map(|l| l.len()).sum::<usize>().encode(e);
for list in self.data.iter() { for val in self.data.iter() {
e.extend_from_slice(list); val.push_onto(e);
} }
} }
} }

View File

@ -77,6 +77,7 @@ pub fn run(fields: &mut Vec<ModuleField>) {
min: u64::from(pages), min: u64::from(pages),
max: Some(u64::from(pages)), max: Some(u64::from(pages)),
}, },
shared: false,
} }
}); });
let data = match mem::replace(&mut m.kind, kind) { let data = match mem::replace(&mut m.kind, kind) {
@ -90,11 +91,7 @@ pub fn run(fields: &mut Vec<ModuleField>) {
kind: DataKind::Active { kind: DataKind::Active {
memory: Index::Id(id), memory: Index::Id(id),
offset: Expression { offset: Expression {
instrs: Box::new([if is_32 { instrs: Box::new([Instruction::I32Const(0)]),
Instruction::I32Const(0)
} else {
Instruction::I64Const(0)
}]),
}, },
}, },
data, data,

View File

@ -363,6 +363,9 @@ impl<'a> Expander<'a> {
ModuleField::Alias(a) => { ModuleField::Alias(a) => {
let (_idx, ns) = Ns::from_export(&a.kind); let (_idx, ns) = Ns::from_export(&a.kind);
self.record_defined(&a.id, ns); self.record_defined(&a.id, ns);
if let Some(instance) = &a.instance {
self.record_missing(instance, Ns::Instance);
}
} }
ModuleField::NestedModule(m) => { ModuleField::NestedModule(m) => {
@ -418,6 +421,121 @@ impl<'a> Expander<'a> {
self.record_missing(&t.src, Ns::Table); self.record_missing(&t.src, Ns::Table);
self.record_missing(&t.dst, Ns::Table); self.record_missing(&t.dst, Ns::Table);
} }
MemorySize(i) | MemoryGrow(i) | MemoryFill(i) => {
self.record_missing(&i.mem, Ns::Memory);
}
MemoryInit(i) => {
self.record_missing(&i.mem, Ns::Memory);
}
MemoryCopy(i) => {
self.record_missing(&i.src, Ns::Memory);
self.record_missing(&i.dst, Ns::Memory);
}
I32Load(m)
| I64Load(m)
| F32Load(m)
| F64Load(m)
| I32Load8s(m)
| I32Load8u(m)
| I32Load16s(m)
| I32Load16u(m)
| I64Load8s(m)
| I64Load8u(m)
| I64Load16s(m)
| I64Load16u(m)
| I64Load32s(m)
| I64Load32u(m)
| I32Store(m)
| I64Store(m)
| F32Store(m)
| F64Store(m)
| I32Store8(m)
| I32Store16(m)
| I64Store8(m)
| I64Store16(m)
| I64Store32(m)
| I32AtomicLoad(m)
| I64AtomicLoad(m)
| I32AtomicLoad8u(m)
| I32AtomicLoad16u(m)
| I64AtomicLoad8u(m)
| I64AtomicLoad16u(m)
| I64AtomicLoad32u(m)
| I32AtomicStore(m)
| I64AtomicStore(m)
| I32AtomicStore8(m)
| I32AtomicStore16(m)
| I64AtomicStore8(m)
| I64AtomicStore16(m)
| I64AtomicStore32(m)
| I32AtomicRmwAdd(m)
| I64AtomicRmwAdd(m)
| I32AtomicRmw8AddU(m)
| I32AtomicRmw16AddU(m)
| I64AtomicRmw8AddU(m)
| I64AtomicRmw16AddU(m)
| I64AtomicRmw32AddU(m)
| I32AtomicRmwSub(m)
| I64AtomicRmwSub(m)
| I32AtomicRmw8SubU(m)
| I32AtomicRmw16SubU(m)
| I64AtomicRmw8SubU(m)
| I64AtomicRmw16SubU(m)
| I64AtomicRmw32SubU(m)
| I32AtomicRmwAnd(m)
| I64AtomicRmwAnd(m)
| I32AtomicRmw8AndU(m)
| I32AtomicRmw16AndU(m)
| I64AtomicRmw8AndU(m)
| I64AtomicRmw16AndU(m)
| I64AtomicRmw32AndU(m)
| I32AtomicRmwOr(m)
| I64AtomicRmwOr(m)
| I32AtomicRmw8OrU(m)
| I32AtomicRmw16OrU(m)
| I64AtomicRmw8OrU(m)
| I64AtomicRmw16OrU(m)
| I64AtomicRmw32OrU(m)
| I32AtomicRmwXor(m)
| I64AtomicRmwXor(m)
| I32AtomicRmw8XorU(m)
| I32AtomicRmw16XorU(m)
| I64AtomicRmw8XorU(m)
| I64AtomicRmw16XorU(m)
| I64AtomicRmw32XorU(m)
| I32AtomicRmwXchg(m)
| I64AtomicRmwXchg(m)
| I32AtomicRmw8XchgU(m)
| I32AtomicRmw16XchgU(m)
| I64AtomicRmw8XchgU(m)
| I64AtomicRmw16XchgU(m)
| I64AtomicRmw32XchgU(m)
| I32AtomicRmwCmpxchg(m)
| I64AtomicRmwCmpxchg(m)
| I32AtomicRmw8CmpxchgU(m)
| I32AtomicRmw16CmpxchgU(m)
| I64AtomicRmw8CmpxchgU(m)
| I64AtomicRmw16CmpxchgU(m)
| I64AtomicRmw32CmpxchgU(m)
| V128Load(m)
| V128Load8x8S(m)
| V128Load8x8U(m)
| V128Load16x4S(m)
| V128Load16x4U(m)
| V128Load32x2S(m)
| V128Load32x2U(m)
| V128Load8Splat(m)
| V128Load16Splat(m)
| V128Load32Splat(m)
| V128Load64Splat(m)
| V128Load32Zero(m)
| V128Load64Zero(m)
| V128Store(m)
| MemoryAtomicNotify(m)
| MemoryAtomicWait32(m)
| MemoryAtomicWait64(m) => self.record_missing(&m.memory, Ns::Memory),
_ => {} _ => {}
} }
} }

View File

@ -64,6 +64,13 @@ pub struct Module<'a> {
/// currently-being-processed field. This should always be empty after /// currently-being-processed field. This should always be empty after
/// processing is complete. /// processing is complete.
to_prepend: Vec<ModuleField<'a>>, to_prepend: Vec<ModuleField<'a>>,
/// Cache for copying over types from other modules, used for module-linking
/// module types. The key of this map is the `(module_index, type_index)`
/// and the value is the copied over item into this module.
///
/// This is used by the `copy_type_from_module` method.
type_cache: HashMap<(usize, Index<'a>), Item<'a>>,
} }
enum InstanceDef<'a> { enum InstanceDef<'a> {
@ -146,22 +153,35 @@ impl<'a> Resolver<'a> {
// //
// Practically there is no purpose to interleaving the type and module // Practically there is no purpose to interleaving the type and module
// section today. As a result we can safely sort all types to the front. // section today. As a result we can safely sort all types to the front.
// This, however, can break the roundtrip binary-text-binary for
// strictly-speaking compliant modules with the module linking spec.
// Anyway, this is a bummer, should figure out a better thing in the
// future.
//
// I've tried to open discussion about this at // I've tried to open discussion about this at
// WebAssembly/module-linking#8 // WebAssembly/module-linking#8
fields.sort_by_key(|field| match field { //
ModuleField::Type(_) // Note that to avoid breaking round-tripping and as a convenience for
| ModuleField::Alias(Alias { // writing tests, we don't reorder any fields if we don't have to fill
kind: ExportKind::Type(_), // in the type for any modules.
.. let sort_types_first = fields.iter().any(|f| match f {
}) => 0, ModuleField::NestedModule(m) => match &m.kind {
_ => 1, NestedModuleKind::Inline { ty, .. } => ty.is_none(),
_ => false,
},
ModuleField::Import(i) => match &i.item.kind {
ItemKind::Module(ty) => ty.index.is_none(),
_ => false,
},
_ => false,
}); });
if sort_types_first {
fields.sort_by_key(|field| match field {
ModuleField::Type(_)
| ModuleField::Alias(Alias {
kind: ExportKind::Type(_),
..
}) => 0,
_ => 1,
});
}
// Number everything in the module, recording what names correspond to // Number everything in the module, recording what names correspond to
// what indices. // what indices.
let module = &mut self.modules[self.cur]; let module = &mut self.modules[self.cur];
@ -183,14 +203,16 @@ impl<'a> Resolver<'a> {
// This is the same as the comment above, only we're doing it now after // This is the same as the comment above, only we're doing it now after
// the full expansion process since all types should now be present in // the full expansion process since all types should now be present in
// the module. // the module.
fields.sort_by_key(|field| match field { if sort_types_first {
ModuleField::Type(_) fields.sort_by_key(|field| match field {
| ModuleField::Alias(Alias { ModuleField::Type(_)
kind: ExportKind::Type(_), | ModuleField::Alias(Alias {
.. kind: ExportKind::Type(_),
}) => 0, ..
_ => 1, }) => 0,
}); _ => 1,
});
}
// And finally the last step is to replace all our `Index::Id` instances // And finally the last step is to replace all our `Index::Id` instances
// with `Index::Num` in the AST. This does not recurse into nested // with `Index::Num` in the AST. This does not recurse into nested
@ -694,8 +716,22 @@ impl<'a> Resolver<'a> {
type_idx: &Index<'a>, type_idx: &Index<'a>,
switch_module_to_instance: bool, switch_module_to_instance: bool,
) -> Result<Item<'a>, Error> { ) -> Result<Item<'a>, Error> {
// First check the cache to avoid doing this work multiple times if
// necessary. Note that we also don't do this in the
// `switch_module_to_instance` case which happens only in rare cases
// above anyway.
//
// This prevents us from recursively realizing we don't need to copy
// over types each time we are asked to copy a type. This short-circuit
// prevents an exponential blowup of runtime for deeply nested modules.
if !switch_module_to_instance {
if let Some(ret) = self.modules[self.cur].type_cache.get(&(child, *type_idx)) {
return Ok(ret.clone());
}
}
let (ty, child) = self.type_for(child, type_idx)?; let (ty, child) = self.type_for(child, type_idx)?;
match ty { let item = match ty {
TypeInfo::Func(key) => { TypeInfo::Func(key) => {
let key = key.clone(); let key = key.clone();
let my_key = ( let my_key = (
@ -708,7 +744,7 @@ impl<'a> Resolver<'a> {
.map(|ty| self.copy_valtype_from_module(span, child, *ty)) .map(|ty| self.copy_valtype_from_module(span, child, *ty))
.collect::<Result<Box<[_]>, Error>>()?, .collect::<Result<Box<[_]>, Error>>()?,
); );
Ok(Item::Func(self.modules[self.cur].key_to_idx(span, my_key))) Item::Func(self.modules[self.cur].key_to_idx(span, my_key))
} }
TypeInfo::Instance { key, .. } => { TypeInfo::Instance { key, .. } => {
@ -720,9 +756,7 @@ impl<'a> Resolver<'a> {
.map(|x| (*name, x)) .map(|x| (*name, x))
}) })
.collect::<Result<Vec<_>, Error>>()?; .collect::<Result<Vec<_>, Error>>()?;
Ok(Item::Instance( Item::Instance(self.modules[self.cur].key_to_idx(span, my_key))
self.modules[self.cur].key_to_idx(span, my_key),
))
} }
TypeInfo::Module { key, .. } => { TypeInfo::Module { key, .. } => {
@ -748,16 +782,20 @@ impl<'a> Resolver<'a> {
.map(|x| (*module, *field, x)) .map(|x| (*module, *field, x))
}) })
.collect::<Result<Vec<_>, Error>>()?; .collect::<Result<Vec<_>, Error>>()?;
Ok(Item::Module( Item::Module(self.modules[self.cur].key_to_idx(span, (imports, exports)))
self.modules[self.cur].key_to_idx(span, (imports, exports)),
))
} }
TypeInfo::Other => Err(Error::new( TypeInfo::Other => {
span, return Err(Error::new(
format!("cannot copy reference types between modules right now"), span,
)), format!("cannot copy reference types between modules right now"),
} ))
}
};
self.modules[self.cur]
.type_cache
.insert((child, *type_idx), item.clone());
Ok(item)
} }
fn copy_reftype_from_module( fn copy_reftype_from_module(
@ -1927,10 +1965,13 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
Throw(i) => { Throw(i) => {
self.module.resolve(i, Ns::Event)?; self.module.resolve(i, Ns::Event)?;
} }
BrOnExn(b) => { Rethrow(i) => {
self.resolve_label(&mut b.label)?; self.resolve_label(i)?;
self.module.resolve(&mut b.exn, Ns::Event)?;
} }
Catch(i) => {
self.module.resolve(i, Ns::Event)?;
}
BrOnCast(b) => { BrOnCast(b) => {
self.resolve_label(&mut b.label)?; self.resolve_label(&mut b.label)?;
self.module.resolve_heaptype(&mut b.val)?; self.module.resolve_heaptype(&mut b.val)?;
@ -2077,6 +2118,8 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
| V128Load16Splat(m) | V128Load16Splat(m)
| V128Load32Splat(m) | V128Load32Splat(m)
| V128Load64Splat(m) | V128Load64Splat(m)
| V128Load32Zero(m)
| V128Load64Zero(m)
| V128Store(m) | V128Store(m)
| MemoryAtomicNotify(m) | MemoryAtomicNotify(m)
| MemoryAtomicWait32(m) | MemoryAtomicWait32(m)

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"4b973805b8ce9c658bd9ca10e76e512496d02c689054c5953eec3d091a7c9b13","README.md":"6653a386a2210f0f7e36964f15214bc441e2c723c42867dfe90dfcedcd301814","src/lib.rs":"03652351228b7f7a520f4e7f1e689fa34a37b8e5e0fc8367a167cc893cdbc449"},"package":"26b2dccbce4d0e14875091846e110a2369267b18ddd0d6423479b88dad914d71"} {"files":{"Cargo.toml":"61a583833e6205f9c9bcb28b3a7148d23861d2ce884775c14eb2ca9bd3c64c45","README.md":"6653a386a2210f0f7e36964f15214bc441e2c723c42867dfe90dfcedcd301814","src/lib.rs":"03652351228b7f7a520f4e7f1e689fa34a37b8e5e0fc8367a167cc893cdbc449"},"package":"0d11a88d953b298172d218d18f22853f4e6e12873b62755d05617b864d312c68"}

View File

@ -13,7 +13,7 @@
[package] [package]
edition = "2018" edition = "2018"
name = "wat" name = "wat"
version = "1.0.27" version = "1.0.30"
authors = ["Alex Crichton <alex@alexcrichton.com>"] 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\n"
homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat" homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat"
@ -22,4 +22,4 @@ readme = "README.md"
license = "Apache-2.0 WITH LLVM-exception" license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat" repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat"
[dependencies.wast] [dependencies.wast]
version = "26.0.0" version = "29.0.0"