wast-217.0.0/.cargo_vcs_info.json0000644000000001510000000000100121560ustar { "git": { "sha1": "46cb2e80abe9e57c422a3496032dd4c49bdefcd9" }, "path_in_vcs": "crates/wast" }wast-217.0.0/Cargo.toml0000644000000041240000000000100101600ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.76.0" name = "wast" version = "217.0.0" authors = ["Alex Crichton "] build = false autobins = false autoexamples = false autotests = false autobenches = false 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 OR Apache-2.0 OR MIT" repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast" [package.metadata.docs.rs] all-features = true [lib] name = "wast" path = "src/lib.rs" [[test]] name = "annotations" path = "tests/annotations.rs" [[test]] name = "comments" path = "tests/comments.rs" [[test]] name = "parse-fail" path = "tests/parse-fail.rs" harness = false [[test]] name = "recursive" path = "tests/recursive.rs" [dependencies.bumpalo] version = "3.14.0" [dependencies.gimli] version = "0.30.0" optional = true [dependencies.leb128] version = "0.2.4" [dependencies.memchr] version = "2.4.1" [dependencies.unicode-width] version = "0.1.9" [dependencies.wasm-encoder] version = "0.217.0" [dev-dependencies.anyhow] version = "1.0.58" [dev-dependencies.libtest-mimic] version = "0.7.0" [dev-dependencies.rand] version = "0.8.4" features = ["small_rng"] [features] default = ["wasm-module"] dwarf = ["dep:gimli"] wasm-module = [] [lints.clippy] clone_on_copy = "warn" manual_strip = "warn" map_clone = "warn" unnecessary_to_owned = "warn" [lints.clippy.all] level = "allow" priority = -1 [lints.rust] unsafe_code = "deny" wast-217.0.0/Cargo.toml.orig000064400000000000000000000030421046102023000136370ustar 00000000000000[package] name = "wast" version = "217.0.0" authors = ["Alex Crichton "] edition.workspace = true license.workspace = true readme = "README.md" repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast" homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast" documentation = "https://docs.rs/wast" description = """ Customizable Rust parsers for the WebAssembly Text formats WAT and WAST """ rust-version.workspace = true [package.metadata.docs.rs] all-features = true [lints] workspace = true [dependencies] leb128 = { workspace = true } unicode-width = "0.1.9" memchr = "2.4.1" wasm-encoder = { workspace = true } bumpalo = "3.14.0" gimli = { workspace = true, optional = true } [dev-dependencies] anyhow = { workspace = true } libtest-mimic = { workspace = true } wasmparser = { path = "../wasmparser" } wat = { path = "../wat" } rand = { workspace = true } [features] default = ['wasm-module'] # Includes default parsing support for `*.wat` and `*.wast` files (wasm # modules). This isn't always needed though if you're parsing just an # s-expression based format. If you'd like to slim down this dependency to just # the lexer, parser framework, and token support, this feature can be disabled. # # This feature is turned on by default. wasm-module = [] # Off-by-default feature to support emitting DWARF debugging information in # parsed binaries pointing back to source locations in the original `*.wat` # source. dwarf = ["dep:gimli"] [[test]] name = "parse-fail" harness = false wast-217.0.0/README.md000064400000000000000000000042621046102023000122340ustar 00000000000000

wast

A Bytecode Alliance project

A Rust parser for the WebAssembly Text Format (WAT).

Crates.io version Download docs.rs docs

## Usage Add `wast` to your `Cargo.toml` ```sh $ cargo add wast ``` The intent of this crate is to provide utilities, combinators, and built-in types to parse anything that looks like a WebAssembly s-expression. * Need to parse a `*.wat` file? * Need to parse a `*.wast` file? * Need to run test suite assertions from the official wasm test suite? * Want to write an extension to the WebAssembly text format? If you'd like to do any of the above this crate might be right for you! You may also want to check out the `wat` crate which provides a much more stable interface if all you'd like to do is convert `*.wat` to `*.wasm`. ## Cargo features By default this crate enables and exports support necessary to parse `*.wat` and `*.wast` files, or in other words entire wasm modules. If you're using this crate, however, to parse simply an s-expression wasm-related format (like `*.witx` or `*.wit` perhaps) then you can disable the default set of features to only include a lexer, the parsing framework, and a few basic token-related parsers. ```sh $ cargo add wast --no-default-features ``` # License This project is licensed under the Apache 2.0 license with the LLVM exception. See [LICENSE](LICENSE) for more details. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. wast-217.0.0/src/component/alias.rs000064400000000000000000000176331046102023000152130ustar 00000000000000use crate::core::ExportKind; use crate::kw; use crate::parser::{Parse, Parser, Result}; use crate::token::{Id, Index, NameAnnotation, Span}; /// A inline alias for component exported items. /// /// Handles both `core export` and `export` aliases #[derive(Debug)] pub struct InlineExportAlias<'a, const CORE: bool> { /// The instance to alias the export from. pub instance: Index<'a>, /// The name of the export to alias. pub name: &'a str, } impl<'a, const CORE: bool> Parse<'a> for InlineExportAlias<'a, CORE> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; if CORE { parser.parse::()?; } parser.parse::()?; let instance = parser.parse()?; let name = parser.parse()?; Ok(Self { instance, name }) } } /// An alias to a component item. #[derive(Debug)] 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>, /// An optional name for this alias stored in the custom `name` section. pub name: Option>, /// The target of this alias. pub target: AliasTarget<'a>, } impl<'a> Alias<'a> { /// Parses only an outer type alias. pub fn parse_outer_core_type_alias(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; parser.parse::()?; let outer = parser.parse()?; let index = parser.parse()?; let (kind, id, name) = parser.parens(|parser| { let mut kind: ComponentOuterAliasKind = parser.parse()?; match kind { ComponentOuterAliasKind::CoreType => { return Err(parser.error("expected type for outer alias")) } ComponentOuterAliasKind::Type => { kind = ComponentOuterAliasKind::CoreType; } _ => return Err(parser.error("expected core type or type for outer alias")), } Ok((kind, parser.parse()?, parser.parse()?)) })?; Ok(Self { span, target: AliasTarget::Outer { outer, index, kind }, id, name, }) } } impl<'a> Parse<'a> for Alias<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let mut l = parser.lookahead1(); let (target, id, name) = if l.peek::()? { parser.parse::()?; let outer = parser.parse()?; let index = parser.parse()?; let (kind, id, name) = parser.parens(|parser| Ok((parser.parse()?, parser.parse()?, parser.parse()?)))?; (AliasTarget::Outer { outer, index, kind }, id, name) } else if l.peek::()? { parser.parse::()?; let instance = parser.parse()?; let export_name = parser.parse()?; let (kind, id, name) = parser.parens(|parser| Ok((parser.parse()?, parser.parse()?, parser.parse()?)))?; ( AliasTarget::Export { instance, name: export_name, kind, }, id, name, ) } else if l.peek::()? { parser.parse::()?; parser.parse::()?; let instance = parser.parse()?; let export_name = parser.parse()?; let (kind, id, name) = parser.parens(|parser| { parser.parse::()?; Ok((parser.parse()?, parser.parse()?, parser.parse()?)) })?; ( AliasTarget::CoreExport { instance, name: export_name, kind, }, id, name, ) } else { return Err(l.error()); }; Ok(Self { span, target, id, name, }) } } /// Represents the kind of instance export alias. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ComponentExportAliasKind { /// The alias is to a core module export. CoreModule, /// The alias is to a function export. Func, /// The alias is to a value export. Value, /// The alias is to a type export. Type, /// The alias is to a component export. Component, /// The alias is to an instance export. Instance, } impl<'a> Parse<'a> for ComponentExportAliasKind { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(Self::CoreModule) } else { Err(l.error()) } } else if l.peek::()? { parser.parse::()?; Ok(Self::Func) } else if l.peek::()? { parser.parse::()?; Ok(Self::Value) } else if l.peek::()? { parser.parse::()?; Ok(Self::Type) } else if l.peek::()? { parser.parse::()?; Ok(Self::Component) } else if l.peek::()? { parser.parse::()?; Ok(Self::Instance) } else { Err(l.error()) } } } /// Represents the kind of outer alias. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ComponentOuterAliasKind { /// The alias is to an outer core module. CoreModule, /// The alias is to an outer core type. CoreType, /// The alias is to an outer type. Type, /// The alias is to an outer component. Component, } impl<'a> Parse<'a> for ComponentOuterAliasKind { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(Self::CoreModule) } else if l.peek::()? { parser.parse::()?; Ok(Self::CoreType) } else { Err(l.error()) } } else if l.peek::()? { parser.parse::()?; Ok(Self::Type) } else if l.peek::()? { parser.parse::()?; Ok(Self::Component) } else { Err(l.error()) } } } /// The target of a component alias. #[derive(Debug)] pub enum AliasTarget<'a> { /// The alias is to an export of a component instance. Export { /// The component instance exporting the item. instance: Index<'a>, /// The name of the exported item to alias. name: &'a str, /// The export kind of the alias. kind: ComponentExportAliasKind, }, /// The alias is to an export of a module instance. CoreExport { /// The module instance exporting the item. instance: Index<'a>, /// The name of the exported item to alias. name: &'a str, /// The export kind of the alias. kind: ExportKind, }, /// The alias is to an item from an outer component. Outer { /// The number of enclosing components to skip. outer: Index<'a>, /// The index of the item being aliased. index: Index<'a>, /// The outer alias kind. kind: ComponentOuterAliasKind, }, } wast-217.0.0/src/component/binary.rs000064400000000000000000001105441046102023000154010ustar 00000000000000use crate::component::*; use crate::core; use crate::core::EncodeOptions; use crate::token::{Id, Index, NameAnnotation, Span}; use wasm_encoder::{ CanonicalFunctionSection, ComponentAliasSection, ComponentDefinedTypeEncoder, ComponentExportSection, ComponentImportSection, ComponentInstanceSection, ComponentNameSection, ComponentSection, ComponentSectionId, ComponentStartSection, ComponentTypeEncoder, ComponentTypeSection, CoreTypeEncoder, CoreTypeSection, InstanceSection, NameMap, NestedComponentSection, RawSection, SectionId, }; pub fn encode(component: &Component<'_>, options: &EncodeOptions) -> Vec { match &component.kind { ComponentKind::Text(fields) => { encode_fields(&component.id, &component.name, fields, options).finish() } ComponentKind::Binary(bytes) => bytes.iter().flat_map(|b| b.iter().copied()).collect(), } } fn encode_fields( // TODO: use the id and name for a future names section component_id: &Option>, component_name: &Option>, fields: &[ComponentField<'_>], options: &EncodeOptions, ) -> wasm_encoder::Component { let mut e = Encoder::default(); for field in fields { match field { ComponentField::CoreModule(m) => e.encode_core_module(m, options), ComponentField::CoreInstance(i) => e.encode_core_instance(i), ComponentField::CoreType(t) => e.encode_core_type(t), ComponentField::Component(c) => e.encode_component(c, options), ComponentField::Instance(i) => e.encode_instance(i), ComponentField::Alias(a) => e.encode_alias(a), ComponentField::Type(t) => e.encode_type(t), ComponentField::CanonicalFunc(f) => e.encode_canonical_func(f), ComponentField::CoreFunc(_) | ComponentField::Func(_) => { unreachable!("should be expanded already") } ComponentField::Start(s) => e.encode_start(s), ComponentField::Import(i) => e.encode_import(i), ComponentField::Export(ex) => e.encode_export(ex), ComponentField::Custom(c) => e.encode_custom(c), ComponentField::Producers(c) => e.encode_producers(c), } } e.flush(None); e.encode_names(component_id, component_name); e.component } fn encode_core_type(encoder: CoreTypeEncoder, ty: &CoreTypeDef) { match ty { CoreTypeDef::Def(def) => { if def.shared { todo!("encoding of shared types not yet implemented") } match &def.kind { core::InnerTypeKind::Func(f) => { encoder.function( f.params.iter().map(|(_, _, ty)| (*ty).into()), f.results.iter().copied().map(Into::into), ); } core::InnerTypeKind::Struct(_) | core::InnerTypeKind::Array(_) => { todo!("encoding of GC proposal types not yet implemented") } } } CoreTypeDef::Module(t) => { encoder.module(&t.into()); } } } fn encode_type(encoder: ComponentTypeEncoder, ty: &TypeDef) { match ty { TypeDef::Defined(t) => { encode_defined_type(encoder.defined_type(), t); } TypeDef::Func(f) => { let mut encoder = encoder.function(); encoder.params(f.params.iter().map(|p| (p.name, &p.ty))); if f.results.len() == 1 && f.results[0].name.is_none() { encoder.result(&f.results[0].ty); } else { encoder.results(f.results.iter().map(|r| (r.name.unwrap_or(""), &r.ty))); } } TypeDef::Component(c) => { encoder.component(&c.into()); } TypeDef::Instance(i) => { encoder.instance(&i.into()); } TypeDef::Resource(i) => { let dtor = i.dtor.as_ref().map(|i| i.idx.into()); encoder.resource(i.rep.into(), dtor); } } } fn encode_defined_type(encoder: ComponentDefinedTypeEncoder, ty: &ComponentDefinedType) { match ty { ComponentDefinedType::Primitive(p) => encoder.primitive((*p).into()), ComponentDefinedType::Record(r) => { encoder.record(r.fields.iter().map(|f| (f.name, &f.ty))); } ComponentDefinedType::Variant(v) => { encoder.variant(v.cases.iter().map(|c| { ( c.name, c.ty.as_ref().map(Into::into), c.refines.as_ref().map(Into::into), ) })); } ComponentDefinedType::List(l) => { encoder.list(l.element.as_ref()); } ComponentDefinedType::Tuple(t) => { encoder.tuple(t.fields.iter()); } ComponentDefinedType::Flags(f) => { encoder.flags(f.names.iter().copied()); } ComponentDefinedType::Enum(e) => { encoder.enum_type(e.names.iter().copied()); } ComponentDefinedType::Option(o) => { encoder.option(o.element.as_ref()); } ComponentDefinedType::Result(e) => { encoder.result( e.ok.as_deref().map(Into::into), e.err.as_deref().map(Into::into), ); } ComponentDefinedType::Own(i) => encoder.own((*i).into()), ComponentDefinedType::Borrow(i) => encoder.borrow((*i).into()), } } #[derive(Default)] struct Encoder<'a> { component: wasm_encoder::Component, current_section_id: Option, // Core sections // Note: module sections are written immediately core_instances: InstanceSection, core_types: CoreTypeSection, // Component sections // Note: custom, component, start sections are written immediately instances: ComponentInstanceSection, aliases: ComponentAliasSection, types: ComponentTypeSection, funcs: CanonicalFunctionSection, imports: ComponentImportSection, exports: ComponentExportSection, core_func_names: Vec>, core_table_names: Vec>, core_memory_names: Vec>, core_global_names: Vec>, core_type_names: Vec>, core_module_names: Vec>, core_instance_names: Vec>, func_names: Vec>, value_names: Vec>, type_names: Vec>, component_names: Vec>, instance_names: Vec>, } impl<'a> Encoder<'a> { fn encode_custom(&mut self, custom: &Custom) { // Flush any in-progress section before encoding the customs section self.flush(None); self.component.section(custom); } fn encode_producers(&mut self, custom: &core::Producers) { use crate::encode::Encode; let mut data = Vec::new(); custom.encode(&mut data); self.encode_custom(&Custom { name: "producers", span: Span::from_offset(0), data: vec![&data], }) } fn encode_core_module(&mut self, module: &CoreModule<'a>, options: &EncodeOptions) { // Flush any in-progress section before encoding the module self.flush(None); self.core_module_names .push(get_name(&module.id, &module.name)); match &module.kind { CoreModuleKind::Import { .. } => unreachable!("should be expanded already"), CoreModuleKind::Inline { fields } => { // TODO: replace this with a wasm-encoder based encoding (should return `wasm_encoder::Module`) let data = crate::core::binary::encode(&module.id, &module.name, fields, options); self.component.section(&RawSection { id: ComponentSectionId::CoreModule.into(), data: &data, }); } } } fn encode_core_instance(&mut self, instance: &CoreInstance<'a>) { self.core_instance_names .push(get_name(&instance.id, &instance.name)); match &instance.kind { CoreInstanceKind::Instantiate { module, args } => { self.core_instances.instantiate( module.into(), args.iter().map(|arg| (arg.name, (&arg.kind).into())), ); } CoreInstanceKind::BundleOfExports(exports) => { self.core_instances.export_items(exports.iter().map(|e| { let (kind, index) = (&e.item).into(); (e.name, kind, index) })); } } self.flush(Some(self.core_instances.id())); } fn encode_core_type(&mut self, ty: &CoreType<'a>) { self.core_type_names.push(get_name(&ty.id, &ty.name)); encode_core_type(self.core_types.ty(), &ty.def); self.flush(Some(self.core_types.id())); } fn encode_component(&mut self, component: &NestedComponent<'a>, options: &EncodeOptions) { self.component_names .push(get_name(&component.id, &component.name)); // Flush any in-progress section before encoding the component self.flush(None); match &component.kind { NestedComponentKind::Import { .. } => unreachable!("should be expanded already"), NestedComponentKind::Inline(fields) => { self.component .section(&NestedComponentSection(&encode_fields( &component.id, &component.name, fields, options, ))); } } } fn encode_instance(&mut self, instance: &Instance<'a>) { self.instance_names .push(get_name(&instance.id, &instance.name)); match &instance.kind { InstanceKind::Import { .. } => unreachable!("should be expanded already"), InstanceKind::Instantiate { component, args } => { self.instances.instantiate( component.into(), args.iter().map(|arg| { let (kind, index) = (&arg.kind).into(); (arg.name, kind, index) }), ); } InstanceKind::BundleOfExports(exports) => { self.instances.export_items(exports.iter().map(|e| { let (kind, index) = (&e.kind).into(); (e.name.0, kind, index) })); } } self.flush(Some(self.instances.id())); } fn encode_alias(&mut self, alias: &Alias<'a>) { let name = get_name(&alias.id, &alias.name); self.aliases.alias((&alias.target).into()); match &alias.target { AliasTarget::Export { kind, .. } => { self.names_for_component_export_alias(*kind).push(name); } AliasTarget::CoreExport { kind, .. } => { self.names_for_core_export_alias(*kind).push(name); } AliasTarget::Outer { kind, .. } => { self.names_for_component_outer_alias(*kind).push(name); } } self.flush(Some(self.aliases.id())); } fn encode_start(&mut self, start: &Start) { // Flush any in-progress section before encoding the start section self.flush(None); self.component.section(&ComponentStartSection { function_index: start.func.into(), args: start.args.iter().map(|a| a.idx.into()).collect::>(), results: start.results.len() as u32, }); } fn encode_type(&mut self, ty: &Type<'a>) { self.type_names.push(get_name(&ty.id, &ty.name)); encode_type(self.types.ty(), &ty.def); self.flush(Some(self.types.id())); } fn encode_canonical_func(&mut self, func: &CanonicalFunc<'a>) { let name = get_name(&func.id, &func.name); match &func.kind { CanonicalFuncKind::Lift { ty, info } => { self.func_names.push(name); self.funcs.lift( info.func.idx.into(), ty.into(), info.opts.iter().map(Into::into), ); } CanonicalFuncKind::Lower(info) => { self.core_func_names.push(name); self.funcs .lower(info.func.idx.into(), info.opts.iter().map(Into::into)); } CanonicalFuncKind::ResourceNew(info) => { self.core_func_names.push(name); self.funcs.resource_new(info.ty.into()); } CanonicalFuncKind::ResourceDrop(info) => { self.core_func_names.push(name); self.funcs.resource_drop(info.ty.into()); } CanonicalFuncKind::ResourceRep(info) => { self.core_func_names.push(name); self.funcs.resource_rep(info.ty.into()); } } self.flush(Some(self.funcs.id())); } fn encode_import(&mut self, import: &ComponentImport<'a>) { let name = get_name(&import.item.id, &import.item.name); self.names_for_item_kind(&import.item.kind).push(name); self.imports .import(import.name.0, (&import.item.kind).into()); self.flush(Some(self.imports.id())); } fn encode_export(&mut self, export: &ComponentExport<'a>) { let name = get_name(&export.id, &export.debug_name); let (kind, index) = (&export.kind).into(); self.exports.export( export.name.0, kind, index, export.ty.as_ref().map(|ty| (&ty.0.kind).into()), ); match &export.kind { ComponentExportKind::CoreModule(_) => self.core_module_names.push(name), ComponentExportKind::Func(_) => self.func_names.push(name), ComponentExportKind::Instance(_) => self.instance_names.push(name), ComponentExportKind::Value(_) => self.value_names.push(name), ComponentExportKind::Component(_) => self.component_names.push(name), ComponentExportKind::Type(_) => self.type_names.push(name), } self.flush(Some(self.exports.id())); } fn flush(&mut self, section_id: Option) { if self.current_section_id == section_id { return; } if let Some(id) = self.current_section_id { match id { // 0 => custom sections are written immediately // 1 => core modules sections are written immediately 2 => { assert_eq!(id, self.core_instances.id()); self.component.section(&self.core_instances); self.core_instances = Default::default(); } 3 => { assert_eq!(id, self.core_types.id()); self.component.section(&self.core_types); self.core_types = Default::default(); } // 4 => components sections are written immediately 5 => { assert_eq!(id, self.instances.id()); self.component.section(&self.instances); self.instances = Default::default(); } 6 => { assert_eq!(id, self.aliases.id()); self.component.section(&self.aliases); self.aliases = Default::default(); } 7 => { assert_eq!(id, self.types.id()); self.component.section(&self.types); self.types = Default::default(); } 8 => { assert_eq!(id, self.funcs.id()); self.component.section(&self.funcs); self.funcs = Default::default(); } // 9 => start sections are written immediately 10 => { assert_eq!(id, self.imports.id()); self.component.section(&self.imports); self.imports = Default::default(); } 11 => { assert_eq!(id, self.exports.id()); self.component.section(&self.exports); self.exports = Default::default(); } _ => unreachable!("unknown incremental component section id: {}", id), } } self.current_section_id = section_id } fn encode_names( &mut self, component_id: &Option>, component_name: &Option>, ) { let mut names = ComponentNameSection::new(); if let Some(name) = get_name(component_id, component_name) { names.component(name); } let mut funcs = |list: &[Option<&str>], append: fn(&mut ComponentNameSection, &NameMap)| { let mut map = NameMap::new(); for (i, entry) in list.iter().enumerate() { if let Some(name) = entry { map.append(i as u32, name); } } if !map.is_empty() { append(&mut names, &map); } }; funcs(&self.core_func_names, ComponentNameSection::core_funcs); funcs(&self.core_table_names, ComponentNameSection::core_tables); funcs(&self.core_memory_names, ComponentNameSection::core_memories); funcs(&self.core_global_names, ComponentNameSection::core_globals); funcs(&self.core_type_names, ComponentNameSection::core_types); funcs(&self.core_module_names, ComponentNameSection::core_modules); funcs( &self.core_instance_names, ComponentNameSection::core_instances, ); funcs(&self.func_names, ComponentNameSection::funcs); funcs(&self.value_names, ComponentNameSection::values); funcs(&self.type_names, ComponentNameSection::types); funcs(&self.component_names, ComponentNameSection::components); funcs(&self.instance_names, ComponentNameSection::instances); if !names.is_empty() { self.component.section(&names); } } fn names_for_component_export_alias( &mut self, kind: ComponentExportAliasKind, ) -> &mut Vec> { match kind { ComponentExportAliasKind::Func => &mut self.func_names, ComponentExportAliasKind::CoreModule => &mut self.core_module_names, ComponentExportAliasKind::Value => &mut self.value_names, ComponentExportAliasKind::Type => &mut self.type_names, ComponentExportAliasKind::Component => &mut self.component_names, ComponentExportAliasKind::Instance => &mut self.instance_names, } } fn names_for_component_outer_alias( &mut self, kind: ComponentOuterAliasKind, ) -> &mut Vec> { match kind { ComponentOuterAliasKind::CoreModule => &mut self.core_module_names, ComponentOuterAliasKind::CoreType => &mut self.core_type_names, ComponentOuterAliasKind::Component => &mut self.component_names, ComponentOuterAliasKind::Type => &mut self.type_names, } } fn names_for_core_export_alias(&mut self, kind: core::ExportKind) -> &mut Vec> { match kind { core::ExportKind::Func => &mut self.core_func_names, core::ExportKind::Global => &mut self.core_global_names, core::ExportKind::Table => &mut self.core_table_names, core::ExportKind::Memory => &mut self.core_memory_names, core::ExportKind::Tag => unimplemented!(), } } fn names_for_item_kind(&mut self, kind: &ItemSigKind) -> &mut Vec> { match kind { ItemSigKind::CoreModule(_) => &mut self.core_module_names, ItemSigKind::Func(_) => &mut self.func_names, ItemSigKind::Component(_) => &mut self.component_names, ItemSigKind::Instance(_) => &mut self.instance_names, ItemSigKind::Value(_) => &mut self.value_names, ItemSigKind::Type(_) => &mut self.type_names, } } } fn get_name<'a>(id: &Option>, name: &Option>) -> Option<&'a str> { name.as_ref().map(|n| n.name).or_else(|| { id.and_then(|id| { if id.is_gensym() { None } else { Some(id.name()) } }) }) } // This implementation is much like `wasm_encoder::CustomSection`, except // that it extends via a list of slices instead of a single slice. impl wasm_encoder::Encode for Custom<'_> { fn encode(&self, sink: &mut Vec) { let mut buf = [0u8; 5]; let encoded_name_len = leb128::write::unsigned(&mut &mut buf[..], u64::try_from(self.name.len()).unwrap()) .unwrap(); let data_len = self.data.iter().fold(0, |acc, s| acc + s.len()); // name length (encoded_name_len + self.name.len() + data_len).encode(sink); // name self.name.encode(sink); // data for s in &self.data { sink.extend(*s); } } } impl wasm_encoder::ComponentSection for Custom<'_> { fn id(&self) -> u8 { SectionId::Custom.into() } } // TODO: move these core conversion functions to the core module // once we update core encoding to use wasm-encoder. impl From> for wasm_encoder::ValType { fn from(ty: core::ValType) -> Self { match ty { core::ValType::I32 => Self::I32, core::ValType::I64 => Self::I64, core::ValType::F32 => Self::F32, core::ValType::F64 => Self::F64, core::ValType::V128 => Self::V128, core::ValType::Ref(r) => Self::Ref(r.into()), } } } impl From> for wasm_encoder::RefType { fn from(r: core::RefType<'_>) -> Self { wasm_encoder::RefType { nullable: r.nullable, heap_type: r.heap.into(), } } } impl From> for wasm_encoder::HeapType { fn from(r: core::HeapType<'_>) -> Self { use wasm_encoder::AbstractHeapType::*; match r { core::HeapType::Abstract { shared, ty } => { let ty = match ty { core::AbstractHeapType::Func => Func, core::AbstractHeapType::Extern => Extern, core::AbstractHeapType::Exn => Exn, core::AbstractHeapType::NoExn => NoExn, core::AbstractHeapType::Any => Any, core::AbstractHeapType::Eq => Eq, core::AbstractHeapType::Struct => Struct, core::AbstractHeapType::Array => Array, core::AbstractHeapType::NoFunc => NoFunc, core::AbstractHeapType::NoExtern => NoExtern, core::AbstractHeapType::None => None, core::AbstractHeapType::I31 => I31, }; Self::Abstract { shared, ty } } core::HeapType::Concrete(Index::Num(i, _)) => Self::Concrete(i), core::HeapType::Concrete(_) => panic!("unresolved index"), } } } impl From<&core::ItemKind<'_>> for wasm_encoder::EntityType { fn from(kind: &core::ItemKind) -> Self { match kind { core::ItemKind::Func(t) => Self::Function(t.into()), core::ItemKind::Table(t) => Self::Table((*t).into()), core::ItemKind::Memory(t) => Self::Memory((*t).into()), core::ItemKind::Global(t) => Self::Global((*t).into()), core::ItemKind::Tag(t) => Self::Tag(t.into()), } } } impl From> for wasm_encoder::TableType { fn from(ty: core::TableType) -> Self { Self { element_type: ty.elem.into(), minimum: ty.limits.min, maximum: ty.limits.max, table64: ty.limits.is64, shared: ty.shared, } } } impl From for wasm_encoder::MemoryType { fn from(ty: core::MemoryType) -> Self { Self { minimum: ty.limits.min, maximum: ty.limits.max, memory64: ty.limits.is64, shared: ty.shared, page_size_log2: ty.page_size_log2, } } } impl From> for wasm_encoder::GlobalType { fn from(ty: core::GlobalType) -> Self { Self { val_type: ty.ty.into(), mutable: ty.mutable, shared: ty.shared, } } } impl From<&core::TagType<'_>> for wasm_encoder::TagType { fn from(ty: &core::TagType) -> Self { match ty { core::TagType::Exception(r) => Self { kind: wasm_encoder::TagKind::Exception, func_type_idx: r.into(), }, } } } impl From<&core::TypeUse<'_, T>> for u32 { fn from(u: &core::TypeUse<'_, T>) -> Self { match &u.index { Some(i) => (*i).into(), None => unreachable!("unresolved type use in encoding: {:?}", u), } } } impl From<&CoreInstantiationArgKind<'_>> for wasm_encoder::ModuleArg { fn from(kind: &CoreInstantiationArgKind) -> Self { match kind { CoreInstantiationArgKind::Instance(i) => { wasm_encoder::ModuleArg::Instance(i.idx.into()) } CoreInstantiationArgKind::BundleOfExports(..) => { unreachable!("should be expanded already") } } } } impl From<&CoreItemRef<'_, core::ExportKind>> for (wasm_encoder::ExportKind, u32) { fn from(item: &CoreItemRef<'_, core::ExportKind>) -> Self { match &item.kind { core::ExportKind::Func => (wasm_encoder::ExportKind::Func, item.idx.into()), core::ExportKind::Table => (wasm_encoder::ExportKind::Table, item.idx.into()), core::ExportKind::Memory => (wasm_encoder::ExportKind::Memory, item.idx.into()), core::ExportKind::Global => (wasm_encoder::ExportKind::Global, item.idx.into()), core::ExportKind::Tag => (wasm_encoder::ExportKind::Tag, item.idx.into()), } } } impl From for wasm_encoder::ExportKind { fn from(kind: core::ExportKind) -> Self { match kind { core::ExportKind::Func => Self::Func, core::ExportKind::Table => Self::Table, core::ExportKind::Memory => Self::Memory, core::ExportKind::Global => Self::Global, core::ExportKind::Tag => Self::Tag, } } } impl From> for u32 { fn from(i: Index<'_>) -> Self { match i { Index::Num(i, _) => i, Index::Id(_) => unreachable!("unresolved index in encoding: {:?}", i), } } } impl From<&ItemRef<'_, T>> for u32 { fn from(i: &ItemRef<'_, T>) -> Self { assert!(i.export_names.is_empty()); i.idx.into() } } impl From<&CoreTypeUse<'_, T>> for u32 { fn from(u: &CoreTypeUse<'_, T>) -> Self { match u { CoreTypeUse::Inline(_) => unreachable!("should be expanded already"), CoreTypeUse::Ref(r) => r.idx.into(), } } } impl From<&ComponentTypeUse<'_, T>> for u32 { fn from(u: &ComponentTypeUse<'_, T>) -> Self { match u { ComponentTypeUse::Inline(_) => unreachable!("should be expanded already"), ComponentTypeUse::Ref(r) => r.idx.into(), } } } impl From<&ComponentValType<'_>> for wasm_encoder::ComponentValType { fn from(r: &ComponentValType) -> Self { match r { ComponentValType::Inline(ComponentDefinedType::Primitive(p)) => { Self::Primitive((*p).into()) } ComponentValType::Ref(i) => Self::Type(u32::from(*i)), ComponentValType::Inline(_) => unreachable!("should be expanded by now"), } } } impl From for wasm_encoder::PrimitiveValType { fn from(p: PrimitiveValType) -> Self { match p { PrimitiveValType::Bool => Self::Bool, PrimitiveValType::S8 => Self::S8, PrimitiveValType::U8 => Self::U8, PrimitiveValType::S16 => Self::S16, PrimitiveValType::U16 => Self::U16, PrimitiveValType::S32 => Self::S32, PrimitiveValType::U32 => Self::U32, PrimitiveValType::S64 => Self::S64, PrimitiveValType::U64 => Self::U64, PrimitiveValType::F32 => Self::F32, PrimitiveValType::F64 => Self::F64, PrimitiveValType::Char => Self::Char, PrimitiveValType::String => Self::String, } } } impl From<&Refinement<'_>> for u32 { fn from(r: &Refinement) -> Self { match r { Refinement::Index(..) => unreachable!("should be resolved by now"), Refinement::Resolved(i) => *i, } } } impl From<&ItemSigKind<'_>> for wasm_encoder::ComponentTypeRef { fn from(k: &ItemSigKind) -> Self { match k { ItemSigKind::Component(c) => Self::Component(c.into()), ItemSigKind::CoreModule(m) => Self::Module(m.into()), ItemSigKind::Instance(i) => Self::Instance(i.into()), ItemSigKind::Value(v) => Self::Value((&v.0).into()), ItemSigKind::Func(f) => Self::Func(f.into()), ItemSigKind::Type(TypeBounds::Eq(t)) => { Self::Type(wasm_encoder::TypeBounds::Eq((*t).into())) } ItemSigKind::Type(TypeBounds::SubResource) => { Self::Type(wasm_encoder::TypeBounds::SubResource) } } } } impl From<&ComponentType<'_>> for wasm_encoder::ComponentType { fn from(ty: &ComponentType) -> Self { let mut encoded = wasm_encoder::ComponentType::new(); for decl in &ty.decls { match decl { ComponentTypeDecl::CoreType(t) => { encode_core_type(encoded.core_type(), &t.def); } ComponentTypeDecl::Type(t) => { encode_type(encoded.ty(), &t.def); } ComponentTypeDecl::Alias(a) => { encoded.alias((&a.target).into()); } ComponentTypeDecl::Import(i) => { encoded.import(i.name.0, (&i.item.kind).into()); } ComponentTypeDecl::Export(e) => { encoded.export(e.name.0, (&e.item.kind).into()); } } } encoded } } impl From<&InstanceType<'_>> for wasm_encoder::InstanceType { fn from(ty: &InstanceType) -> Self { let mut encoded = wasm_encoder::InstanceType::new(); for decl in &ty.decls { match decl { InstanceTypeDecl::CoreType(t) => { encode_core_type(encoded.core_type(), &t.def); } InstanceTypeDecl::Type(t) => { encode_type(encoded.ty(), &t.def); } InstanceTypeDecl::Alias(a) => { encoded.alias((&a.target).into()); } InstanceTypeDecl::Export(e) => { encoded.export(e.name.0, (&e.item.kind).into()); } } } encoded } } impl From<&ModuleType<'_>> for wasm_encoder::ModuleType { fn from(ty: &ModuleType) -> Self { let mut encoded = wasm_encoder::ModuleType::new(); for decl in &ty.decls { match decl { ModuleTypeDecl::Type(t) => match &t.def.kind { core::InnerTypeKind::Func(f) => encoded.ty().function( f.params.iter().map(|(_, _, ty)| (*ty).into()), f.results.iter().copied().map(Into::into), ), core::InnerTypeKind::Struct(_) | core::InnerTypeKind::Array(_) => { todo!("encoding of GC proposal types not yet implemented") } }, ModuleTypeDecl::Alias(a) => match &a.target { AliasTarget::Outer { outer, index, kind: ComponentOuterAliasKind::CoreType, } => { encoded.alias_outer_core_type(u32::from(*outer), u32::from(*index)); } _ => unreachable!("only outer type aliases are supported"), }, ModuleTypeDecl::Import(i) => { encoded.import(i.module, i.field, (&i.item.kind).into()); } ModuleTypeDecl::Export(name, item) => { encoded.export(name, (&item.kind).into()); } } } encoded } } impl From<&InstantiationArgKind<'_>> for (wasm_encoder::ComponentExportKind, u32) { fn from(kind: &InstantiationArgKind) -> Self { match kind { InstantiationArgKind::Item(i) => i.into(), InstantiationArgKind::BundleOfExports(..) => unreachable!("should be expanded already"), } } } impl From<&ComponentExportKind<'_>> for (wasm_encoder::ComponentExportKind, u32) { fn from(kind: &ComponentExportKind) -> Self { match kind { ComponentExportKind::CoreModule(m) => { (wasm_encoder::ComponentExportKind::Module, m.idx.into()) } ComponentExportKind::Func(f) => (wasm_encoder::ComponentExportKind::Func, f.idx.into()), ComponentExportKind::Value(v) => { (wasm_encoder::ComponentExportKind::Value, v.idx.into()) } ComponentExportKind::Type(t) => (wasm_encoder::ComponentExportKind::Type, t.idx.into()), ComponentExportKind::Component(c) => { (wasm_encoder::ComponentExportKind::Component, c.idx.into()) } ComponentExportKind::Instance(i) => { (wasm_encoder::ComponentExportKind::Instance, i.idx.into()) } } } } impl From for wasm_encoder::ComponentOuterAliasKind { fn from(kind: ComponentOuterAliasKind) -> Self { match kind { ComponentOuterAliasKind::CoreModule => Self::CoreModule, ComponentOuterAliasKind::CoreType => Self::CoreType, ComponentOuterAliasKind::Type => Self::Type, ComponentOuterAliasKind::Component => Self::Component, } } } impl From for wasm_encoder::ComponentExportKind { fn from(kind: ComponentExportAliasKind) -> Self { match kind { ComponentExportAliasKind::CoreModule => Self::Module, ComponentExportAliasKind::Func => Self::Func, ComponentExportAliasKind::Value => Self::Value, ComponentExportAliasKind::Type => Self::Type, ComponentExportAliasKind::Component => Self::Component, ComponentExportAliasKind::Instance => Self::Instance, } } } impl From<&CanonOpt<'_>> for wasm_encoder::CanonicalOption { fn from(opt: &CanonOpt) -> Self { match opt { CanonOpt::StringUtf8 => Self::UTF8, CanonOpt::StringUtf16 => Self::UTF16, CanonOpt::StringLatin1Utf16 => Self::CompactUTF16, CanonOpt::Memory(m) => Self::Memory(m.idx.into()), CanonOpt::Realloc(f) => Self::Realloc(f.idx.into()), CanonOpt::PostReturn(f) => Self::PostReturn(f.idx.into()), } } } impl<'a> From<&AliasTarget<'a>> for wasm_encoder::Alias<'a> { fn from(target: &AliasTarget<'a>) -> Self { match target { AliasTarget::Export { instance, name, kind, } => wasm_encoder::Alias::InstanceExport { instance: (*instance).into(), kind: (*kind).into(), name, }, AliasTarget::CoreExport { instance, name, kind, } => wasm_encoder::Alias::CoreInstanceExport { instance: (*instance).into(), kind: (*kind).into(), name, }, AliasTarget::Outer { outer, index, kind } => wasm_encoder::Alias::Outer { count: (*outer).into(), kind: (*kind).into(), index: (*index).into(), }, } } } wast-217.0.0/src/component/component.rs000064400000000000000000000255771046102023000161320ustar 00000000000000use crate::annotation; use crate::component::*; use crate::core::Producers; use crate::kw; use crate::parser::{Parse, Parser, Result}; use crate::token::Index; 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>, /// An optional `@name` annotation for this component pub name: Option>, /// 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>), /// 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`. 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, crate::Error> { crate::core::EncodeOptions::default().encode_component(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(()) } pub(crate) fn parse_without_component_keyword( component_keyword_span: Span, parser: Parser<'a>, ) -> Result { let id = parser.parse()?; let name = parser.parse()?; let kind = if parser.peek::()? { parser.parse::()?; 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: component_keyword_span, id, name, kind, }) } } impl<'a> Parse<'a> for Component<'a> { fn parse(parser: Parser<'a>) -> Result { parser.with_standard_annotations_registered(|parser| { let span = parser.parse::()?.0; Component::parse_without_component_keyword(span, parser) }) } } /// A listing of all possible fields that can make up a WebAssembly component. #[allow(missing_docs)] #[derive(Debug)] pub enum ComponentField<'a> { CoreModule(CoreModule<'a>), CoreInstance(CoreInstance<'a>), CoreType(CoreType<'a>), Component(NestedComponent<'a>), Instance(Instance<'a>), Alias(Alias<'a>), Type(Type<'a>), CanonicalFunc(CanonicalFunc<'a>), CoreFunc(CoreFunc<'a>), // Supports inverted forms of other items Func(Func<'a>), // Supports inverted forms of other items Start(Start<'a>), Import(ComponentImport<'a>), Export(ComponentExport<'a>), Custom(Custom<'a>), Producers(Producers<'a>), } impl<'a> ComponentField<'a> { fn parse_remaining(parser: Parser<'a>) -> Result>> { 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 { if parser.peek::()? { if parser.peek2::()? { return Ok(Self::CoreModule(parser.parse()?)); } if parser.peek2::()? { return Ok(Self::CoreInstance(parser.parse()?)); } if parser.peek2::()? { return Ok(Self::CoreType(parser.parse()?)); } if parser.peek2::()? { return Ok(Self::CoreFunc(parser.parse()?)); } } else { if parser.peek::()? { return Ok(Self::Component(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Instance(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Alias(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Type(Type::parse_maybe_with_inline_exports(parser)?)); } if parser.peek::()? { return Ok(Self::Import(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Func(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Export(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Start(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Custom(parser.parse()?)); } if parser.peek::()? { return Ok(Self::Producers(parser.parse()?)); } } Err(parser.error("expected valid component field")) } } /// A function to call at instantiation time. #[derive(Debug)] pub struct Start<'a> { /// The function to call. pub func: Index<'a>, /// The arguments to pass to the function. pub args: Vec>, /// Names of the result values. pub results: Vec>>, } impl<'a> Parse<'a> for Start<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let func = parser.parse()?; let mut args = Vec::new(); while !parser.is_empty() && !parser.peek2::()? { args.push(parser.parens(|parser| parser.parse())?); } let mut results = Vec::new(); while !parser.is_empty() && parser.peek2::()? { results.push(parser.parens(|parser| { parser.parse::()?; parser.parens(|parser| { parser.parse::()?; parser.parse() }) })?); } Ok(Start { func, args, results, }) } } /// A nested WebAssembly component. #[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>, /// An optional `@name` annotation for this component pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: 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>), } impl<'a> Parse<'a> for NestedComponent<'a> { fn parse(parser: Parser<'a>) -> Result { parser.depth_check()?; let span = parser.parse::()?.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, }) } } wast-217.0.0/src/component/custom.rs000064400000000000000000000013161046102023000154230ustar 00000000000000use crate::annotation; use crate::parser::{Parse, Parser, Result}; use crate::token::Span; /// A custom section within a component. #[derive(Debug)] pub struct Custom<'a> { /// Where this `@custom` was defined. pub span: Span, /// Name of the custom section. pub name: &'a str, /// Payload of this custom section. pub data: Vec<&'a [u8]>, } impl<'a> Parse<'a> for Custom<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let name = parser.parse()?; let mut data = Vec::new(); while !parser.is_empty() { data.push(parser.parse()?); } Ok(Self { span, name, data }) } } wast-217.0.0/src/component/expand.rs000064400000000000000000000752401046102023000153770ustar 00000000000000use crate::component::*; use crate::core; use crate::gensym; use crate::kw; use crate::token::Id; use crate::token::{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 "i" (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>) { Expander::default().expand_component_fields(fields) } enum AnyType<'a> { Core(CoreType<'a>), Component(Type<'a>), } impl<'a> From> for ComponentTypeDecl<'a> { fn from(t: AnyType<'a>) -> Self { match t { AnyType::Core(t) => Self::CoreType(t), AnyType::Component(t) => Self::Type(t), } } } impl<'a> From> for InstanceTypeDecl<'a> { fn from(t: AnyType<'a>) -> Self { match t { AnyType::Core(t) => Self::CoreType(t), AnyType::Component(t) => Self::Type(t), } } } impl<'a> From> for ComponentField<'a> { fn from(t: AnyType<'a>) -> Self { match t { AnyType::Core(t) => Self::CoreType(t), AnyType::Component(t) => Self::Type(t), } } } #[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. types_to_prepend: Vec>, component_fields_to_prepend: Vec>, /// Fields that are appended to the end of the module once everything has /// finished. component_fields_to_append: Vec>, } impl<'a> Expander<'a> { fn expand_component_fields(&mut self, fields: &mut Vec>) { let mut cur = 0; while cur < fields.len() { self.expand_field(&mut fields[cur]); let amt = self.types_to_prepend.len() + self.component_fields_to_prepend.len(); fields.splice(cur..cur, self.component_fields_to_prepend.drain(..)); fields.splice(cur..cur, self.types_to_prepend.drain(..).map(Into::into)); cur += 1 + amt; } fields.append(&mut self.component_fields_to_append); } fn expand_decls(&mut self, decls: &mut Vec, expand: fn(&mut Self, &mut T)) where T: From>, { let mut cur = 0; while cur < decls.len() { expand(self, &mut decls[cur]); assert!(self.component_fields_to_prepend.is_empty()); assert!(self.component_fields_to_append.is_empty()); let amt = self.types_to_prepend.len(); decls.splice(cur..cur, self.types_to_prepend.drain(..).map(From::from)); cur += 1 + amt; } } fn expand_field(&mut self, item: &mut ComponentField<'a>) { let expanded = match item { ComponentField::CoreModule(m) => self.expand_core_module(m), ComponentField::CoreInstance(i) => { self.expand_core_instance(i); None } ComponentField::CoreType(t) => { self.expand_core_type(t); None } ComponentField::Component(c) => self.expand_nested_component(c), ComponentField::Instance(i) => self.expand_instance(i), ComponentField::Type(t) => { self.expand_type(t); None } ComponentField::CanonicalFunc(f) => { self.expand_canonical_func(f); None } ComponentField::CoreFunc(f) => self.expand_core_func(f), ComponentField::Func(f) => self.expand_func(f), ComponentField::Import(i) => { self.expand_item_sig(&mut i.item); None } ComponentField::Export(e) => { if let Some(sig) = &mut e.ty { self.expand_item_sig(&mut sig.0); } None } ComponentField::Start(_) | ComponentField::Alias(_) | ComponentField::Custom(_) | ComponentField::Producers(_) => None, }; if let Some(expanded) = expanded { *item = expanded; } } fn expand_core_module(&mut self, module: &mut CoreModule<'a>) -> Option> { for name in module.exports.names.drain(..) { let id = gensym::fill(module.span, &mut module.id); self.component_fields_to_append .push(ComponentField::Export(ComponentExport { span: module.span, id: None, debug_name: None, name, kind: ComponentExportKind::module(module.span, id), ty: None, })); } match &mut module.kind { // inline modules are expanded later during resolution CoreModuleKind::Inline { .. } => None, CoreModuleKind::Import { import, ty } => { let idx = self.expand_core_type_use(ty); Some(ComponentField::Import(ComponentImport { span: module.span, name: import.name, item: ItemSig { span: module.span, id: module.id, name: None, kind: ItemSigKind::CoreModule(CoreTypeUse::Ref(idx)), }, })) } } } fn expand_core_instance(&mut self, instance: &mut CoreInstance<'a>) { match &mut instance.kind { CoreInstanceKind::Instantiate { args, .. } => { for arg in args { self.expand_core_instantiation_arg(&mut arg.kind); } } CoreInstanceKind::BundleOfExports { .. } => {} } } fn expand_nested_component( &mut self, component: &mut NestedComponent<'a>, ) -> Option> { for name in component.exports.names.drain(..) { let id = gensym::fill(component.span, &mut component.id); self.component_fields_to_append .push(ComponentField::Export(ComponentExport { span: component.span, id: None, debug_name: None, name, kind: ComponentExportKind::component(component.span, id), ty: None, })); } match &mut component.kind { NestedComponentKind::Inline(fields) => { expand(fields); None } NestedComponentKind::Import { import, ty } => { let idx = self.expand_component_type_use(ty); Some(ComponentField::Import(ComponentImport { span: component.span, name: import.name, item: ItemSig { span: component.span, id: component.id, name: None, kind: ItemSigKind::Component(ComponentTypeUse::Ref(idx)), }, })) } } } fn expand_instance(&mut self, instance: &mut Instance<'a>) -> Option> { for name in instance.exports.names.drain(..) { let id = gensym::fill(instance.span, &mut instance.id); self.component_fields_to_append .push(ComponentField::Export(ComponentExport { span: instance.span, id: None, debug_name: None, name, kind: ComponentExportKind::instance(instance.span, id), ty: None, })); } match &mut instance.kind { InstanceKind::Import { import, ty } => { let idx = self.expand_component_type_use(ty); Some(ComponentField::Import(ComponentImport { span: instance.span, name: import.name, item: ItemSig { span: instance.span, id: instance.id, name: None, kind: ItemSigKind::Instance(ComponentTypeUse::Ref(idx)), }, })) } InstanceKind::Instantiate { args, .. } => { for arg in args { self.expand_instantiation_arg(&mut arg.kind); } None } InstanceKind::BundleOfExports { .. } => None, } } fn expand_canonical_func(&mut self, func: &mut CanonicalFunc<'a>) { match &mut func.kind { CanonicalFuncKind::Lift { ty, .. } => { self.expand_component_type_use(ty); } CanonicalFuncKind::Lower(_) | CanonicalFuncKind::ResourceNew(_) | CanonicalFuncKind::ResourceRep(_) | CanonicalFuncKind::ResourceDrop(_) => {} } } fn expand_core_func(&mut self, func: &mut CoreFunc<'a>) -> Option> { match &mut func.kind { CoreFuncKind::Alias(a) => Some(ComponentField::Alias(Alias { span: func.span, id: func.id, name: func.name, target: AliasTarget::CoreExport { instance: a.instance, name: a.name, kind: core::ExportKind::Func, }, })), CoreFuncKind::Lower(info) => Some(ComponentField::CanonicalFunc(CanonicalFunc { span: func.span, id: func.id, name: func.name, kind: CanonicalFuncKind::Lower(mem::take(info)), })), CoreFuncKind::ResourceNew(info) => Some(ComponentField::CanonicalFunc(CanonicalFunc { span: func.span, id: func.id, name: func.name, kind: CanonicalFuncKind::ResourceNew(mem::take(info)), })), CoreFuncKind::ResourceDrop(info) => { Some(ComponentField::CanonicalFunc(CanonicalFunc { span: func.span, id: func.id, name: func.name, kind: CanonicalFuncKind::ResourceDrop(mem::take(info)), })) } CoreFuncKind::ResourceRep(info) => Some(ComponentField::CanonicalFunc(CanonicalFunc { span: func.span, id: func.id, name: func.name, kind: CanonicalFuncKind::ResourceRep(mem::take(info)), })), } } fn expand_func(&mut self, func: &mut Func<'a>) -> Option> { for name in func.exports.names.drain(..) { let id = gensym::fill(func.span, &mut func.id); self.component_fields_to_append .push(ComponentField::Export(ComponentExport { span: func.span, id: None, debug_name: None, name, kind: ComponentExportKind::func(func.span, id), ty: None, })); } match &mut func.kind { FuncKind::Import { import, ty } => { let idx = self.expand_component_type_use(ty); Some(ComponentField::Import(ComponentImport { span: func.span, name: import.name, item: ItemSig { span: func.span, id: func.id, name: None, kind: ItemSigKind::Func(ComponentTypeUse::Ref(idx)), }, })) } FuncKind::Lift { ty, info } => { let idx = self.expand_component_type_use(ty); Some(ComponentField::CanonicalFunc(CanonicalFunc { span: func.span, id: func.id, name: func.name, kind: CanonicalFuncKind::Lift { ty: ComponentTypeUse::Ref(idx), info: mem::take(info), }, })) } FuncKind::Alias(a) => Some(ComponentField::Alias(Alias { span: func.span, id: func.id, name: func.name, target: AliasTarget::Export { instance: a.instance, name: a.name, kind: ComponentExportAliasKind::Func, }, })), } } fn expand_core_type(&mut self, field: &mut CoreType<'a>) { match &mut field.def { CoreTypeDef::Def(_) => {} CoreTypeDef::Module(m) => self.expand_module_ty(m), } let id = gensym::fill(field.span, &mut field.id); let index = Index::Id(id); match &field.def { CoreTypeDef::Def(_) => {} CoreTypeDef::Module(t) => t.key().insert(self, index), } } fn expand_type(&mut self, field: &mut Type<'a>) { match &mut field.def { TypeDef::Defined(d) => self.expand_defined_ty(d), TypeDef::Func(f) => self.expand_func_ty(f), TypeDef::Component(c) => self.expand_component_ty(c), TypeDef::Instance(i) => self.expand_instance_ty(i), TypeDef::Resource(_) => {} } let id = gensym::fill(field.span, &mut field.id); let index = Index::Id(id); match &field.def { TypeDef::Defined(t) => t.key().insert(self, index), TypeDef::Func(t) => t.key().insert(self, index), TypeDef::Component(t) => t.key().insert(self, index), TypeDef::Instance(t) => t.key().insert(self, index), TypeDef::Resource(_) => {} } for name in field.exports.names.drain(..) { self.component_fields_to_append .push(ComponentField::Export(ComponentExport { span: field.span, id: None, debug_name: None, name, kind: ComponentExportKind::ty(field.span, id), ty: None, })); } } fn expand_func_ty(&mut self, ty: &mut ComponentFunctionType<'a>) { for param in ty.params.iter_mut() { self.expand_component_val_ty(&mut param.ty); } for result in ty.results.iter_mut() { self.expand_component_val_ty(&mut result.ty); } } 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.decls.len() { match &mut ty.decls[i] { ModuleTypeDecl::Type(ty) => match &ty.def.kind { core::InnerTypeKind::Func(f) => { let id = gensym::fill(ty.span, &mut ty.id); func_type_to_idx.insert(f.key(), Index::Id(id)); } core::InnerTypeKind::Struct(_) => {} core::InnerTypeKind::Array(_) => {} }, ModuleTypeDecl::Alias(_) => {} ModuleTypeDecl::Import(ty) => { expand_sig(&mut ty.item, &mut to_prepend, &mut func_type_to_idx); } ModuleTypeDecl::Export(_, item) => { expand_sig(item, &mut to_prepend, &mut func_type_to_idx); } } ty.decls.splice(i..i, to_prepend.drain(..)); i += 1; } fn expand_sig<'a>( item: &mut core::ItemSig<'a>, to_prepend: &mut Vec>, func_type_to_idx: &mut HashMap, 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); return; } let id = gensym::gen(item.span); to_prepend.push(ModuleTypeDecl::Type(core::Type { span: item.span, id: Some(id), name: None, // Currently, there is no way in the WebAssembly text // format to mark a function `shared` inline; a // `shared` function must use an explicit type index, // e.g., `(func (type $ft))`. def: key.to_def(item.span, /* shared = */ false), parent: None, final_type: None, })); 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_decls(&mut ty.decls, |e, decl| match decl { ComponentTypeDecl::CoreType(t) => e.expand_core_type(t), ComponentTypeDecl::Type(t) => e.expand_type(t), ComponentTypeDecl::Alias(_) => {} ComponentTypeDecl::Export(t) => e.expand_item_sig(&mut t.item), ComponentTypeDecl::Import(t) => e.expand_item_sig(&mut t.item), }) } fn expand_instance_ty(&mut self, ty: &mut InstanceType<'a>) { Expander::default().expand_decls(&mut ty.decls, |e, decl| match decl { InstanceTypeDecl::CoreType(t) => e.expand_core_type(t), InstanceTypeDecl::Type(t) => e.expand_type(t), InstanceTypeDecl::Alias(_) => {} InstanceTypeDecl::Export(t) => e.expand_item_sig(&mut t.item), }) } fn expand_item_sig(&mut self, ext: &mut ItemSig<'a>) { match &mut ext.kind { ItemSigKind::CoreModule(t) => { self.expand_core_type_use(t); } ItemSigKind::Func(t) => { self.expand_component_type_use(t); } ItemSigKind::Component(t) => { self.expand_component_type_use(t); } ItemSigKind::Instance(t) => { self.expand_component_type_use(t); } ItemSigKind::Value(t) => { self.expand_component_val_ty(&mut t.0); } ItemSigKind::Type(_) => {} } } fn expand_defined_ty(&mut self, ty: &mut ComponentDefinedType<'a>) { match ty { ComponentDefinedType::Primitive(_) | ComponentDefinedType::Flags(_) | ComponentDefinedType::Enum(_) => {} ComponentDefinedType::Record(r) => { for field in r.fields.iter_mut() { self.expand_component_val_ty(&mut field.ty); } } ComponentDefinedType::Variant(v) => { for case in v.cases.iter_mut() { if let Some(ty) = &mut case.ty { self.expand_component_val_ty(ty); } } } ComponentDefinedType::List(t) => { self.expand_component_val_ty(&mut t.element); } ComponentDefinedType::Tuple(t) => { for field in t.fields.iter_mut() { self.expand_component_val_ty(field); } } ComponentDefinedType::Option(t) => { self.expand_component_val_ty(&mut t.element); } ComponentDefinedType::Result(r) => { if let Some(ty) = &mut r.ok { self.expand_component_val_ty(ty); } if let Some(ty) = &mut r.err { self.expand_component_val_ty(ty); } } ComponentDefinedType::Own(_) | ComponentDefinedType::Borrow(_) => {} } } fn expand_component_val_ty(&mut self, ty: &mut ComponentValType<'a>) { let inline = match ty { ComponentValType::Inline(ComponentDefinedType::Primitive(_)) | ComponentValType::Ref(_) => return, ComponentValType::Inline(inline) => { self.expand_defined_ty(inline); mem::take(inline) } }; // 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 = ComponentValType::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.types_to_prepend.push(inline.into_any_type(span, id)); let idx = Index::Id(id); *ty = ComponentValType::Ref(idx); } fn expand_core_type_use( &mut self, item: &mut CoreTypeUse<'a, T>, ) -> CoreItemRef<'a, kw::r#type> where T: TypeReference<'a>, { let span = Span::from_offset(0); // FIXME(#613): don't manufacture let mut inline = match mem::take(item) { // 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. CoreTypeUse::Ref(idx) => { *item = CoreTypeUse::Ref(idx.clone()); return idx; } // ... otherwise with an inline type definition we go into // processing below. CoreTypeUse::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 = CoreItemRef { idx, kind: kw::r#type(span), export_name: None, }; *item = CoreTypeUse::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.types_to_prepend.push(inline.into_any_type(span, id)); let idx = Index::Id(id); let ret = CoreItemRef { idx, kind: kw::r#type(span), export_name: None, }; *item = CoreTypeUse::Ref(ret.clone()); ret } fn expand_component_type_use( &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 mut inline = match mem::take(item) { // 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.types_to_prepend.push(inline.into_any_type(span, id)); let idx = Index::Id(id); let ret = ItemRef { idx, kind: kw::r#type(span), export_names: Vec::new(), }; *item = ComponentTypeUse::Ref(ret.clone()); ret } fn expand_core_instantiation_arg(&mut self, arg: &mut CoreInstantiationArgKind<'a>) { let (span, exports) = match arg { CoreInstantiationArgKind::Instance(_) => return, CoreInstantiationArgKind::BundleOfExports(span, exports) => (*span, mem::take(exports)), }; let id = gensym::gen(span); self.component_fields_to_prepend .push(ComponentField::CoreInstance(CoreInstance { span, id: Some(id), name: None, kind: CoreInstanceKind::BundleOfExports(exports), })); *arg = CoreInstantiationArgKind::Instance(CoreItemRef { kind: kw::instance(span), idx: Index::Id(id), export_name: None, }); } fn expand_instantiation_arg(&mut self, arg: &mut InstantiationArgKind<'a>) { let (span, exports) = match arg { InstantiationArgKind::Item(_) => return, InstantiationArgKind::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(exports), })); *arg = InstantiationArgKind::Item(ComponentExportKind::instance(span, id)); } } trait TypeReference<'a> { type Key: TypeKey<'a>; fn key(&self) -> Self::Key; fn expand(&mut self, cx: &mut Expander<'a>); fn into_any_type(self, span: Span, id: Id<'a>) -> AnyType<'a>; } impl<'a> TypeReference<'a> for ComponentDefinedType<'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_defined_ty(self) } fn into_any_type(self, span: Span, id: Id<'a>) -> AnyType<'a> { AnyType::Component(Type { span, id: Some(id), name: None, exports: Default::default(), def: TypeDef::Defined(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_any_type(self, span: Span, id: Id<'a>) -> AnyType<'a> { AnyType::Component(Type { span, id: Some(id), name: None, exports: Default::default(), def: TypeDef::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_any_type(self, span: Span, id: Id<'a>) -> AnyType<'a> { AnyType::Core(CoreType { span, id: Some(id), name: None, def: CoreTypeDef::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_any_type(self, span: Span, id: Id<'a>) -> AnyType<'a> { AnyType::Component(Type { span, id: Some(id), name: None, exports: Default::default(), def: TypeDef::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_any_type(self, span: Span, id: Id<'a>) -> AnyType<'a> { AnyType::Component(Type { span, id: Some(id), name: None, exports: Default::default(), def: TypeDef::Func(self), }) } } trait TypeKey<'a> { fn lookup(&self, cx: &Expander<'a>) -> Option>; fn insert(&self, cx: &mut Expander<'a>, index: Index<'a>); } struct Todo; impl<'a> TypeKey<'a> for Todo { fn lookup(&self, _cx: &Expander<'a>) -> Option> { None } fn insert(&self, _cx: &mut Expander<'a>, _index: Index<'a>) {} } wast-217.0.0/src/component/export.rs000064400000000000000000000153761046102023000154450ustar 00000000000000use super::{ComponentExternName, ItemRef, ItemSigNoName}; use crate::kw; use crate::parser::{Cursor, Parse, Parser, Peek, Result}; use crate::token::{Id, Index, NameAnnotation, Span}; /// An entry in a WebAssembly component's export section. #[derive(Debug)] pub struct ComponentExport<'a> { /// Where this export was defined. pub span: Span, /// Optional identifier bound to this export. pub id: Option>, /// An optional name for this instance stored in the custom `name` section. pub debug_name: Option>, /// The name of this export from the component. pub name: ComponentExternName<'a>, /// The kind of export. pub kind: ComponentExportKind<'a>, /// The kind of export. pub ty: Option>, } impl<'a> Parse<'a> for ComponentExport<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let debug_name = parser.parse()?; let name = parser.parse()?; let kind = parser.parse()?; let ty = if !parser.is_empty() { Some(parser.parens(|p| p.parse())?) } else { None }; Ok(ComponentExport { span, id, debug_name, name, kind, ty, }) } } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut exports = Vec::new(); while !parser.is_empty() { exports.push(parser.parens(|parser| parser.parse())?); } Ok(exports) } } /// The kind of exported item. #[derive(Debug)] pub enum ComponentExportKind<'a> { /// The export is a core module. /// /// Note this isn't a core item ref as currently only /// components can export core modules. CoreModule(ItemRef<'a, kw::module>), /// The export is a function. Func(ItemRef<'a, kw::func>), /// The export is a value. Value(ItemRef<'a, kw::value>), /// The export is a type. Type(ItemRef<'a, kw::r#type>), /// The export is a component. Component(ItemRef<'a, kw::component>), /// The export is an instance. Instance(ItemRef<'a, kw::instance>), } impl<'a> ComponentExportKind<'a> { pub(crate) fn module(span: Span, id: Id<'a>) -> Self { Self::CoreModule(ItemRef { kind: kw::module(span), idx: Index::Id(id), export_names: Default::default(), }) } pub(crate) fn component(span: Span, id: Id<'a>) -> Self { Self::Component(ItemRef { kind: kw::component(span), idx: Index::Id(id), export_names: Default::default(), }) } pub(crate) fn instance(span: Span, id: Id<'a>) -> Self { Self::Instance(ItemRef { kind: kw::instance(span), idx: Index::Id(id), export_names: Default::default(), }) } pub(crate) fn func(span: Span, id: Id<'a>) -> Self { Self::Func(ItemRef { kind: kw::func(span), idx: Index::Id(id), export_names: Default::default(), }) } pub(crate) fn ty(span: Span, id: Id<'a>) -> Self { Self::Type(ItemRef { kind: kw::r#type(span), idx: Index::Id(id), export_names: Default::default(), }) } } impl<'a> Parse<'a> for ComponentExportKind<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parens(|parser| { let mut l = parser.lookahead1(); if l.peek::()? { // Remove core prefix parser.parse::()?; Ok(Self::CoreModule(parser.parse()?)) } else if l.peek::()? { Ok(Self::Func(parser.parse()?)) } else if l.peek::()? { Ok(Self::Value(parser.parse()?)) } else if l.peek::()? { Ok(Self::Type(parser.parse()?)) } else if l.peek::()? { Ok(Self::Component(parser.parse()?)) } else if l.peek::()? { Ok(Self::Instance(parser.parse()?)) } else { Err(l.error()) } }) } } impl Peek for ComponentExportKind<'_> { fn peek(cursor: Cursor) -> Result { let cursor = match cursor.lparen()? { Some(c) => c, None => return Ok(false), }; let cursor = match cursor.keyword()? { Some(("core", c)) => match c.keyword()? { Some(("module", c)) => c, _ => return Ok(false), }, Some(("func", c)) | Some(("value", c)) | Some(("type", c)) | Some(("component", c)) | Some(("instance", c)) => c, _ => return Ok(false), }; Index::peek(cursor) } fn display() -> &'static str { "component export" } } /// A listing of inline `(export "foo" )` statements on a WebAssembly /// component item in its textual format. #[derive(Debug, Default)] pub struct InlineExport<'a> { /// The extra names to export an item as, if any. pub names: Vec>, } impl<'a> Parse<'a> for InlineExport<'a> { fn parse(parser: Parser<'a>) -> Result { let mut names = Vec::new(); while parser.peek::()? { names.push(parser.parens(|p| { p.parse::()?; p.parse() })?); } Ok(InlineExport { names }) } } impl Peek for InlineExport<'_> { fn peek(cursor: Cursor<'_>) -> Result { let cursor = match cursor.lparen()? { Some(cursor) => cursor, None => return Ok(false), }; let cursor = match cursor.keyword()? { Some(("export", cursor)) => cursor, _ => return Ok(false), }; // (export "foo") if let Some((_, cursor)) = cursor.string()? { return Ok(cursor.rparen()?.is_some()); } // (export (interface "foo")) let cursor = match cursor.lparen()? { Some(cursor) => cursor, None => return Ok(false), }; let cursor = match cursor.keyword()? { Some(("interface", cursor)) => cursor, _ => return Ok(false), }; let cursor = match cursor.string()? { Some((_, cursor)) => cursor, _ => return Ok(false), }; let cursor = match cursor.rparen()? { Some(cursor) => cursor, _ => return Ok(false), }; Ok(cursor.rparen()?.is_some()) } fn display() -> &'static str { "inline export" } } wast-217.0.0/src/component/func.rs000064400000000000000000000346551046102023000150600ustar 00000000000000use crate::component::*; use crate::kw; use crate::parser::{Parse, Parser, Result}; use crate::token::{Id, Index, LParen, NameAnnotation, Span}; /// A declared core function. /// /// This is a member of both the core alias and canon sections. #[derive(Debug)] pub struct CoreFunc<'a> { /// Where this `core func` was defined. pub span: Span, /// An identifier that this function is resolved with (optionally) for name /// resolution. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// The kind of core function. pub kind: CoreFuncKind<'a>, } impl<'a> Parse<'a> for CoreFunc<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; parser.parse::()?; let id = parser.parse()?; let name = parser.parse()?; let kind = parser.parse()?; Ok(Self { span, id, name, kind, }) } } /// Represents the kind of core functions. #[derive(Debug)] #[allow(missing_docs)] pub enum CoreFuncKind<'a> { /// The core function is defined in terms of lowering a component function. /// /// The core function is actually a member of the canon section. Lower(CanonLower<'a>), /// The core function is defined in terms of aliasing a module instance export. /// /// The core function is actually a member of the core alias section. Alias(InlineExportAlias<'a, true>), ResourceNew(CanonResourceNew<'a>), ResourceDrop(CanonResourceDrop<'a>), ResourceRep(CanonResourceRep<'a>), } impl<'a> Parse<'a> for CoreFuncKind<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parens(|parser| { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; } else if l.peek::()? { return Ok(Self::Alias(parser.parse()?)); } else { return Err(l.error()); } let mut l = parser.lookahead1(); if l.peek::()? { Ok(CoreFuncKind::Lower(parser.parse()?)) } else if l.peek::()? { Ok(CoreFuncKind::ResourceNew(parser.parse()?)) } else if l.peek::()? { Ok(CoreFuncKind::ResourceDrop(parser.parse()?)) } else if l.peek::()? { Ok(CoreFuncKind::ResourceRep(parser.parse()?)) } else { Err(l.error()) } }) } } /// A declared component function. /// /// This may be a member of the import, alias, or canon sections. #[derive(Debug)] pub struct Func<'a> { /// Where this `func` was defined. pub span: Span, /// An identifier that this function is resolved with (optionally) for name /// resolution. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: InlineExport<'a>, /// The kind of function. pub kind: FuncKind<'a>, } impl<'a> Parse<'a> for Func<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; let kind = parser.parse()?; Ok(Self { span, id, name, exports, kind, }) } } /// Represents the kind of component functions. #[derive(Debug)] pub enum FuncKind<'a> { /// A function which is actually defined as an import, such as: /// /// ```text /// (func (import "foo") (param string)) /// ``` Import { /// The import name of this import. import: InlineImport<'a>, /// The type that this function will have. ty: ComponentTypeUse<'a, ComponentFunctionType<'a>>, }, /// The function is defined in terms of lifting a core function. /// /// The function is actually a member of the canon section. Lift { /// The lifted function's type. ty: ComponentTypeUse<'a, ComponentFunctionType<'a>>, /// Information relating to the lifting of the core function. info: CanonLift<'a>, }, /// The function is defined in terms of aliasing a component instance export. /// /// The function is actually a member of the alias section. Alias(InlineExportAlias<'a, false>), } impl<'a> Parse<'a> for FuncKind<'a> { fn parse(parser: Parser<'a>) -> Result { if let Some(import) = parser.parse()? { Ok(Self::Import { import, ty: parser.parse()?, }) } else if parser.peek::()? && parser.peek2::()? { parser.parens(|parser| Ok(Self::Alias(parser.parse()?))) } else { Ok(Self::Lift { ty: parser.parse()?, info: parser.parens(|parser| { parser.parse::()?; parser.parse() })?, }) } } } /// A WebAssembly canonical function to be inserted into a component. /// /// This is a member of the canonical section. #[derive(Debug)] pub struct CanonicalFunc<'a> { /// Where this `func` was defined. pub span: Span, /// An identifier that this function is resolved with (optionally) for name /// resolution. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// What kind of function this is, be it a lowered or lifted function. pub kind: CanonicalFuncKind<'a>, } impl<'a> Parse<'a> for CanonicalFunc<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; if parser.peek::()? { let info = parser.parse()?; let (id, name, ty) = parser.parens(|parser| { parser.parse::()?; let id = parser.parse()?; let name = parser.parse()?; let ty = parser.parse()?; Ok((id, name, ty)) })?; Ok(Self { span, id, name, kind: CanonicalFuncKind::Lift { info, ty }, }) } else if parser.peek::()? { Self::parse_core_func(span, parser, CanonicalFuncKind::Lower) } else if parser.peek::()? { Self::parse_core_func(span, parser, CanonicalFuncKind::ResourceNew) } else if parser.peek::()? { Self::parse_core_func(span, parser, CanonicalFuncKind::ResourceDrop) } else if parser.peek::()? { Self::parse_core_func(span, parser, CanonicalFuncKind::ResourceRep) } else { Err(parser.error("expected `canon lift` or `canon lower`")) } } } impl<'a> CanonicalFunc<'a> { fn parse_core_func( span: Span, parser: Parser<'a>, variant: fn(T) -> CanonicalFuncKind<'a>, ) -> Result where T: Parse<'a>, { let info = parser.parse()?; let (id, name) = parser.parens(|parser| { parser.parse::()?; parser.parse::()?; let id = parser.parse()?; let name = parser.parse()?; Ok((id, name)) })?; Ok(Self { span, id, name, kind: variant(info), }) } } /// Possible ways to define a canonical function in the text format. #[derive(Debug)] #[allow(missing_docs)] pub enum CanonicalFuncKind<'a> { /// A canonical function that is defined in terms of lifting a core function. Lift { /// The lifted function's type. ty: ComponentTypeUse<'a, ComponentFunctionType<'a>>, /// Information relating to the lifting of the core function. info: CanonLift<'a>, }, /// A canonical function that is defined in terms of lowering a component function. Lower(CanonLower<'a>), ResourceNew(CanonResourceNew<'a>), ResourceDrop(CanonResourceDrop<'a>), ResourceRep(CanonResourceRep<'a>), } /// Information relating to lifting a core function. #[derive(Debug)] pub struct CanonLift<'a> { /// The core function being lifted. pub func: CoreItemRef<'a, kw::func>, /// The canonical options for the lifting. pub opts: Vec>, } impl<'a> Parse<'a> for CanonLift<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { func: parser.parens(|parser| { parser.parse::()?; parser.parse() })?, opts: parser.parse()?, }) } } impl Default for CanonLift<'_> { fn default() -> Self { let span = Span::from_offset(0); Self { func: CoreItemRef { kind: kw::func(span), idx: Index::Num(0, span), export_name: None, }, opts: Vec::new(), } } } /// Information relating to lowering a component function. #[derive(Debug)] pub struct CanonLower<'a> { /// The function being lowered. pub func: ItemRef<'a, kw::func>, /// The canonical options for the lowering. pub opts: Vec>, } impl<'a> Parse<'a> for CanonLower<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { func: parser.parens(|parser| parser.parse())?, opts: parser.parse()?, }) } } impl Default for CanonLower<'_> { fn default() -> Self { let span = Span::from_offset(0); Self { func: ItemRef { kind: kw::func(span), idx: Index::Num(0, span), export_names: Vec::new(), }, opts: Vec::new(), } } } /// Information relating to the `resource.new` intrinsic. #[derive(Debug)] pub struct CanonResourceNew<'a> { /// The resource type that this intrinsic creates an owned reference to. pub ty: Index<'a>, } impl<'a> Parse<'a> for CanonResourceNew<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { ty: parser.parse()?, }) } } impl Default for CanonResourceNew<'_> { fn default() -> Self { CanonResourceNew { ty: Index::Num(0, Span::from_offset(0)), } } } /// Information relating to the `resource.drop` intrinsic. #[derive(Debug)] pub struct CanonResourceDrop<'a> { /// The resource type that this intrinsic is dropping. pub ty: Index<'a>, } impl<'a> Parse<'a> for CanonResourceDrop<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { ty: parser.parse()?, }) } } impl Default for CanonResourceDrop<'_> { fn default() -> Self { CanonResourceDrop { ty: Index::Num(0, Span::from_offset(0)), } } } /// Information relating to the `resource.rep` intrinsic. #[derive(Debug)] pub struct CanonResourceRep<'a> { /// The resource type that this intrinsic is accessing. pub ty: Index<'a>, } impl<'a> Parse<'a> for CanonResourceRep<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { ty: parser.parse()?, }) } } impl Default for CanonResourceRep<'_> { fn default() -> Self { CanonResourceRep { ty: Index::Num(0, Span::from_offset(0)), } } } #[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, /// Use the specified memory for canonical ABI memory access. Memory(CoreItemRef<'a, kw::memory>), /// Use the specified reallocation function for memory allocations. Realloc(CoreItemRef<'a, kw::func>), /// Call the specified function after the lifted function has returned. PostReturn(CoreItemRef<'a, kw::func>), } impl<'a> Parse<'a> for CanonOpt<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(Self::StringUtf8) } else if l.peek::()? { parser.parse::()?; Ok(Self::StringUtf16) } else if l.peek::()? { parser.parse::()?; Ok(Self::StringLatin1Utf16) } else if l.peek::()? { parser.parens(|parser| { let mut l = parser.lookahead1(); if l.peek::()? { let span = parser.parse::()?.0; Ok(CanonOpt::Memory(parse_trailing_item_ref( kw::memory(span), parser, )?)) } else if l.peek::()? { parser.parse::()?; Ok(CanonOpt::Realloc( parser.parse::>()?.0, )) } else if l.peek::()? { parser.parse::()?; Ok(CanonOpt::PostReturn( parser.parse::>()?.0, )) } else { Err(l.error()) } }) } else { Err(l.error()) } } } fn parse_trailing_item_ref(kind: T, parser: Parser) -> Result> { Ok(CoreItemRef { kind, idx: parser.parse()?, export_name: parser.parse()?, }) } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut funcs = Vec::new(); while !parser.is_empty() { funcs.push(parser.parse()?); } Ok(funcs) } } wast-217.0.0/src/component/import.rs000064400000000000000000000160431046102023000154260ustar 00000000000000use crate::component::*; use crate::kw; use crate::parser::{Cursor, Parse, Parser, Peek, Result}; use crate::token::{Id, Index, LParen, 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 being imported. pub name: ComponentExternName<'a>, /// The item that's being imported. pub item: ItemSig<'a>, } impl<'a> Parse<'a> for ComponentImport<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let name = parser.parse()?; let item = parser.parens(|p| p.parse())?; Ok(ComponentImport { span, name, item }) } } /// The different ways an import can be named. #[derive(Debug, Copy, Clone)] pub struct ComponentExternName<'a>(pub &'a str); impl<'a> Parse<'a> for ComponentExternName<'a> { fn parse(parser: Parser<'a>) -> Result { // Prior to WebAssembly/component-model#263 the syntactic form // `(interface "...")` was supported for interface names. This is no // longer part of the syntax of the binary format nor the text format, // but continue to parse this as "sugar" for the current format. This // is intended to avoid breaking folks and provide a smoother transition // forward. let name = if parser.peek::()? { parser.parens(|p| { p.parse::()?; p.parse() })? } else { parser.parse()? }; Ok(ComponentExternName(name)) } } /// An item signature for imported items. #[derive(Debug)] 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>, /// An optional name which, for functions, will be stored in the /// custom `name` section. pub name: Option>, /// What kind of item this is. pub kind: ItemSigKind<'a>, } impl<'a> Parse<'a> for ItemSig<'a> { fn parse(parser: Parser<'a>) -> Result { parse_item_sig(parser, true) } } /// An item signature for imported items. #[derive(Debug)] pub struct ItemSigNoName<'a>(pub ItemSig<'a>); impl<'a> Parse<'a> for ItemSigNoName<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(ItemSigNoName(parse_item_sig(parser, false)?)) } } fn parse_item_sig<'a>(parser: Parser<'a>, name: bool) -> Result> { let mut l = parser.lookahead1(); let (span, parse_kind): (_, fn(Parser<'a>) -> Result>) = if l.peek::()? { let span = parser.parse::()?.0; parser.parse::()?; (span, |parser| Ok(ItemSigKind::CoreModule(parser.parse()?))) } else if l.peek::()? { let span = parser.parse::()?.0; (span, |parser| Ok(ItemSigKind::Func(parser.parse()?))) } else if l.peek::()? { let span = parser.parse::()?.0; (span, |parser| Ok(ItemSigKind::Component(parser.parse()?))) } else if l.peek::()? { let span = parser.parse::()?.0; (span, |parser| Ok(ItemSigKind::Instance(parser.parse()?))) } else if l.peek::()? { let span = parser.parse::()?.0; (span, |parser| Ok(ItemSigKind::Value(parser.parse()?))) } else if l.peek::()? { let span = parser.parse::()?.0; (span, |parser| { Ok(ItemSigKind::Type(parser.parens(|parser| parser.parse())?)) }) } else { return Err(l.error()); }; Ok(ItemSig { span, id: if name { parser.parse()? } else { None }, name: if name { parser.parse()? } else { None }, kind: parse_kind(parser)?, }) } /// The kind of signatures for imported items. #[derive(Debug)] pub enum ItemSigKind<'a> { /// The item signature is for a core module. CoreModule(CoreTypeUse<'a, ModuleType<'a>>), /// The item signature is for a function. Func(ComponentTypeUse<'a, ComponentFunctionType<'a>>), /// The item signature is for a component. Component(ComponentTypeUse<'a, ComponentType<'a>>), /// The item signature is for an instance. Instance(ComponentTypeUse<'a, InstanceType<'a>>), /// The item signature is for a value. Value(ComponentValTypeUse<'a>), /// The item signature is for a type. Type(TypeBounds<'a>), } /// Represents the bounds applied to types being imported. #[derive(Debug)] pub enum TypeBounds<'a> { /// The equality type bounds. Eq(Index<'a>), /// A resource type is imported/exported, SubResource, } impl<'a> Parse<'a> for TypeBounds<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(Self::Eq(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; parser.parse::()?; Ok(Self::SubResource) } else { Err(l.error()) } } } /// A listing of a inline `(import "foo")` statement. /// /// This is the same as `core::InlineImport` except only one string import is /// required. #[derive(Debug, Clone)] pub struct InlineImport<'a> { /// The name of the item being imported. pub name: ComponentExternName<'a>, } impl<'a> Parse<'a> for InlineImport<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parens(|p| { p.parse::()?; Ok(InlineImport { name: p.parse()? }) }) } } impl Peek for InlineImport<'_> { fn peek(cursor: Cursor<'_>) -> Result { let cursor = match cursor.lparen()? { Some(cursor) => cursor, None => return Ok(false), }; let cursor = match cursor.keyword()? { Some(("import", cursor)) => cursor, _ => return Ok(false), }; // (import "foo") if let Some((_, cursor)) = cursor.string()? { return Ok(cursor.rparen()?.is_some()); } // (import (interface "foo")) let cursor = match cursor.lparen()? { Some(cursor) => cursor, None => return Ok(false), }; let cursor = match cursor.keyword()? { Some(("interface", cursor)) => cursor, _ => return Ok(false), }; let cursor = match cursor.string()? { Some((_, cursor)) => cursor, _ => return Ok(false), }; let cursor = match cursor.rparen()? { Some(cursor) => cursor, _ => return Ok(false), }; Ok(cursor.rparen()?.is_some()) } fn display() -> &'static str { "inline import" } } wast-217.0.0/src/component/instance.rs000064400000000000000000000213631046102023000157210ustar 00000000000000use crate::component::*; use crate::core; use crate::kw; use crate::parser::{Parse, Parser, Result}; use crate::token::{Id, LParen, NameAnnotation, Span}; /// A core instance defined by instantiation or exporting core items. #[derive(Debug)] pub struct CoreInstance<'a> { /// Where this `core instance` was defined. pub span: Span, /// An identifier that this instance is resolved with (optionally) for name /// resolution. pub id: Option>, /// An optional name for this instance stored in the custom `name` section. pub name: Option>, /// What kind of instance this is. pub kind: CoreInstanceKind<'a>, } impl<'a> Parse<'a> for CoreInstance<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; parser.parse::()?; let id = parser.parse()?; let name = parser.parse()?; let kind = parser.parse()?; Ok(Self { span, id, name, kind, }) } } /// The kinds of core instances in the text format. #[derive(Debug)] pub enum CoreInstanceKind<'a> { /// Instantiate a core module. Instantiate { /// The module being instantiated. module: ItemRef<'a, kw::module>, /// Arguments used to instantiate the instance. args: Vec>, }, /// The instance is defined by exporting local items as an instance. BundleOfExports(Vec>), } impl<'a> Parse<'a> for CoreInstanceKind<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? && parser.peek2::()? { parser.parens(|parser| { parser.parse::()?; Ok(Self::Instantiate { module: parser.parse::>()?.0, args: parser.parse()?, }) }) } else { Ok(Self::BundleOfExports(parser.parse()?)) } } } impl Default for kw::module { fn default() -> kw::module { kw::module(Span::from_offset(0)) } } /// An argument to instantiate a core module. #[derive(Debug)] pub struct CoreInstantiationArg<'a> { /// The name of the instantiation argument. pub name: &'a str, /// The kind of core instantiation argument. pub kind: CoreInstantiationArgKind<'a>, } impl<'a> Parse<'a> for CoreInstantiationArg<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { name: parser.parse()?, kind: parser.parse()?, }) } } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut args = Vec::new(); while !parser.is_empty() { args.push(parser.parens(|parser| parser.parse())?); } Ok(args) } } /// The kind of core instantiation argument. #[derive(Debug)] pub enum CoreInstantiationArgKind<'a> { /// The argument is a reference to an instance. Instance(CoreItemRef<'a, kw::instance>), /// The argument is an instance created from local exported core items. /// /// This is syntactic sugar for defining a core instance and also using it /// as an instantiation argument. BundleOfExports(Span, Vec>), } impl<'a> Parse<'a> for CoreInstantiationArgKind<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parens(|parser| { if let Some(r) = parser.parse()? { Ok(Self::Instance(r)) } else { let span = parser.parse::()?.0; Ok(Self::BundleOfExports(span, parser.parse()?)) } }) } } /// An exported item as part of a core instance. #[derive(Debug)] pub struct CoreInstanceExport<'a> { /// Where this export was defined. pub span: Span, /// The name of this export from the instance. pub name: &'a str, /// What's being exported from the instance. pub item: CoreItemRef<'a, core::ExportKind>, } impl<'a> Parse<'a> for CoreInstanceExport<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(Self { span: parser.parse::()?.0, name: parser.parse()?, item: parser.parens(|parser| parser.parse())?, }) } } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut exports = Vec::new(); while !parser.is_empty() { exports.push(parser.parens(|parser| parser.parse())?); } Ok(exports) } } /// A component instance defined by instantiation or exporting items. #[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>, /// An optional name for this instance stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: InlineExport<'a>, /// What kind of instance this is. pub kind: InstanceKind<'a>, } impl<'a> Parse<'a> for Instance<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; let kind = parser.parse()?; Ok(Self { span, id, name, exports, kind, }) } } /// The kinds of instances 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 component. Instantiate { /// The component being instantiated. component: ItemRef<'a, kw::component>, /// Arguments used to instantiate the instance. args: Vec>, }, /// The instance is defined by exporting local items as an instance. BundleOfExports(Vec>), } impl<'a> Parse<'a> for InstanceKind<'a> { fn parse(parser: Parser<'a>) -> Result { if let Some(import) = parser.parse()? { return Ok(Self::Import { import, ty: parser.parse()?, }); } if parser.peek::()? && parser.peek2::()? { parser.parens(|parser| { parser.parse::()?; Ok(Self::Instantiate { component: parser.parse::>()?.0, args: parser.parse()?, }) }) } else { Ok(Self::BundleOfExports(parser.parse()?)) } } } impl Default for kw::component { fn default() -> kw::component { kw::component(Span::from_offset(0)) } } /// An argument to instantiate a component. #[derive(Debug)] pub struct InstantiationArg<'a> { /// The name of the instantiation argument. pub name: &'a str, /// The kind of instantiation argument. pub kind: InstantiationArgKind<'a>, } impl<'a> Parse<'a> for InstantiationArg<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { name: parser.parse()?, kind: parser.parse()?, }) } } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut args = Vec::new(); while !parser.is_empty() { args.push(parser.parens(|parser| parser.parse())?); } Ok(args) } } /// The kind of instantiation argument. #[derive(Debug)] pub enum InstantiationArgKind<'a> { /// The argument is a reference to a component item. Item(ComponentExportKind<'a>), /// The argument is an instance created from local exported items. /// /// This is syntactic sugar for defining an instance and also using it /// as an instantiation argument. BundleOfExports(Span, Vec>), } impl<'a> Parse<'a> for InstantiationArgKind<'a> { fn parse(parser: Parser<'a>) -> Result { if let Some(item) = parser.parse()? { Ok(Self::Item(item)) } else { parser.parens(|parser| { let span = parser.parse::()?.0; Ok(Self::BundleOfExports(span, parser.parse()?)) }) } } } wast-217.0.0/src/component/item_ref.rs000064400000000000000000000103471046102023000157070ustar 00000000000000use crate::parser::{Cursor, Parse, Parser, Peek, Result}; use crate::token::Index; fn peek(cursor: Cursor) -> Result { // 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. // Peek for the given keyword type if !K::peek(cursor)? { return Ok(false); } // Move past the given keyword let cursor = match cursor.keyword()? { Some((_, c)) => c, _ => return Ok(false), }; // Peek an id or integer index, followed by `)` or string to disambiguate let cursor = match cursor.id()? { Some((_, cursor)) => Some(cursor), None => cursor.integer()?.map(|p| p.1), }; Ok(match cursor { Some(cursor) => cursor.rparen()?.is_some() || cursor.string()?.is_some(), None => false, }) } /// Parses core item references. #[derive(Clone, Debug)] pub struct CoreItemRef<'a, K> { /// The item kind being parsed. pub kind: K, /// The item or instance reference. pub idx: Index<'a>, /// Export name to resolve the item from. pub export_name: Option<&'a str>, } impl<'a, K: Parse<'a>> Parse<'a> for CoreItemRef<'a, K> { fn parse(parser: Parser<'a>) -> Result { // This does not parse the surrounding `(` and `)` because // core prefix is context dependent and only the caller knows if it should be // present for core references; therefore, the caller parses the parens and any core prefix let kind = parser.parse::()?; let idx = parser.parse()?; let export_name = parser.parse()?; Ok(Self { kind, idx, export_name, }) } } impl<'a, K: Peek> Peek for CoreItemRef<'a, K> { fn peek(cursor: Cursor<'_>) -> Result { peek::(cursor) } fn display() -> &'static str { "a core item reference" } } /// Parses component item references. #[derive(Clone, Debug)] pub struct ItemRef<'a, K> { /// The item kind being parsed. pub kind: K, /// The item or instance reference. pub idx: Index<'a>, /// Export names to resolve the item from. pub export_names: Vec<&'a str>, } impl<'a, K: Parse<'a>> Parse<'a> for ItemRef<'a, K> { fn parse(parser: Parser<'a>) -> Result { let kind = parser.parse::()?; let idx = parser.parse()?; let mut export_names = Vec::new(); while !parser.is_empty() { export_names.push(parser.parse()?); } Ok(Self { kind, idx, export_names, }) } } impl<'a, K: Peek> Peek for ItemRef<'a, K> { fn peek(cursor: Cursor<'_>) -> Result { peek::(cursor) } fn display() -> &'static str { "a component 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 { if parser.peek::>()? { Ok(IndexOrRef(ItemRef { kind: K::default(), idx: parser.parse()?, export_names: Vec::new(), })) } else { Ok(IndexOrRef(parser.parens(|p| p.parse())?)) } } } /// Convenience structure to parse `$f` or `(item $f)`. #[derive(Clone, Debug)] pub struct IndexOrCoreRef<'a, K>(pub CoreItemRef<'a, K>); impl<'a, K> Parse<'a> for IndexOrCoreRef<'a, K> where K: Parse<'a> + Default, { fn parse(parser: Parser<'a>) -> Result { if parser.peek::>()? { Ok(IndexOrCoreRef(CoreItemRef { kind: K::default(), idx: parser.parse()?, export_name: None, })) } else { Ok(IndexOrCoreRef(parser.parens(|p| p.parse())?)) } } } wast-217.0.0/src/component/module.rs000064400000000000000000000043321046102023000153770ustar 00000000000000use crate::component::*; use crate::core; use crate::kw; use crate::parser::{Parse, Parser, Result}; use crate::token::{Id, NameAnnotation, Span}; /// A core WebAssembly module to be created as part of a component. /// /// This is a member of the core module section. #[derive(Debug)] pub struct CoreModule<'a> { /// Where this `core module` was defined. pub span: Span, /// An identifier that this module is resolved with (optionally) for name /// resolution. pub id: Option>, /// An optional name for this module stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: InlineExport<'a>, /// What kind of module this is, be it an inline-defined or imported one. pub kind: CoreModuleKind<'a>, } /// Possible ways to define a core module in the text format. #[derive(Debug)] pub enum CoreModuleKind<'a> { /// A core module which is actually defined as an import Import { /// Where this core module is imported from import: InlineImport<'a>, /// The type that this core module will have. ty: CoreTypeUse<'a, ModuleType<'a>>, }, /// Modules that are defined inline. Inline { /// Fields in the core module. fields: Vec>, }, } impl<'a> Parse<'a> for CoreModule<'a> { fn parse(parser: Parser<'a>) -> Result { parser.depth_check()?; let span = parser.parse::()?.0; parser.parse::()?; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; let kind = if let Some(import) = parser.parse()? { CoreModuleKind::Import { import, ty: parser.parse()?, } } else { let mut fields = Vec::new(); while !parser.is_empty() { fields.push(parser.parens(|p| p.parse())?); } CoreModuleKind::Inline { fields } }; Ok(Self { span, id, name, exports, kind, }) } } wast-217.0.0/src/component/resolve.rs000064400000000000000000001076111046102023000155750ustar 00000000000000use crate::component::*; use crate::core::{self, resolve::ResolveCoreType, ValType}; use crate::kw; use crate::names::Namespace; use crate::token::Span; 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) } impl<'a> From> for ComponentField<'a> { fn from(a: Alias<'a>) -> Self { Self::Alias(a) } } impl<'a> From> for ModuleTypeDecl<'a> { fn from(a: Alias<'a>) -> Self { Self::Alias(a) } } impl<'a> From> for ComponentTypeDecl<'a> { fn from(a: Alias<'a>) -> Self { Self::Alias(a) } } impl<'a> From> for InstanceTypeDecl<'a> { fn from(a: Alias<'a>) -> Self { Self::Alias(a) } } #[derive(Default)] struct Resolver<'a> { stack: Vec>, // 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>, } /// Context structure used to perform name resolution. #[derive(Default)] struct ComponentState<'a> { id: Option>, // 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. core_funcs: Namespace<'a>, core_globals: Namespace<'a>, core_tables: Namespace<'a>, core_memories: Namespace<'a>, core_types: Namespace<'a>, core_tags: Namespace<'a>, core_instances: Namespace<'a>, core_modules: Namespace<'a>, funcs: Namespace<'a>, types: Namespace<'a>, instances: Namespace<'a>, components: Namespace<'a>, values: Namespace<'a>, } impl<'a> ComponentState<'a> { fn new(id: Option>) -> ComponentState<'a> { ComponentState { id, ..ComponentState::default() } } fn register_item_sig(&mut self, sig: &ItemSig<'a>) -> Result { match &sig.kind { ItemSigKind::CoreModule(_) => self.core_modules.register(sig.id, "core module"), ItemSigKind::Func(_) => self.funcs.register(sig.id, "func"), ItemSigKind::Component(_) => self.components.register(sig.id, "component"), ItemSigKind::Instance(_) => self.instances.register(sig.id, "instance"), ItemSigKind::Value(_) => self.values.register(sig.id, "value"), ItemSigKind::Type(_) => self.types.register(sig.id, "type"), } } } 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>, fields: &mut Vec>, ) -> 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( &mut self, fields: &mut Vec, resolve: fn(&mut Self, &mut T) -> Result<(), Error>, register: fn(&mut ComponentState<'a>, &T) -> Result<(), Error>, ) -> Result<(), Error> where T: From>, { 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 definitions to refer to. register(self.current(), &fields[i])?; i += 1; } Ok(()) } fn field(&mut self, field: &mut ComponentField<'a>) -> Result<(), Error> { match field { ComponentField::CoreModule(m) => self.core_module(m), ComponentField::CoreInstance(i) => self.core_instance(i), ComponentField::CoreType(t) => self.core_ty(t), ComponentField::Component(c) => self.component(c), ComponentField::Instance(i) => self.instance(i), ComponentField::Alias(a) => self.alias(a), ComponentField::Type(t) => self.ty(t), ComponentField::CanonicalFunc(f) => self.canonical_func(f), ComponentField::CoreFunc(_) => unreachable!("should be expanded already"), ComponentField::Func(_) => unreachable!("should be expanded already"), ComponentField::Start(s) => self.start(s), ComponentField::Import(i) => self.item_sig(&mut i.item), ComponentField::Export(e) => { if let Some(ty) = &mut e.ty { self.item_sig(&mut ty.0)?; } self.export(&mut e.kind) } ComponentField::Custom(_) | ComponentField::Producers(_) => Ok(()), } } fn core_module(&mut self, module: &mut CoreModule) -> Result<(), Error> { match &mut module.kind { CoreModuleKind::Inline { fields } => { crate::core::resolve::resolve(fields)?; } CoreModuleKind::Import { .. } => { unreachable!("should be expanded already") } } Ok(()) } fn component(&mut self, component: &mut NestedComponent<'a>) -> Result<(), Error> { match &mut component.kind { NestedComponentKind::Import { .. } => unreachable!("should be expanded already"), NestedComponentKind::Inline(fields) => self.fields(component.id, fields), } } fn core_instance(&mut self, instance: &mut CoreInstance<'a>) -> Result<(), Error> { match &mut instance.kind { CoreInstanceKind::Instantiate { module, args } => { self.component_item_ref(module)?; for arg in args { match &mut arg.kind { CoreInstantiationArgKind::Instance(i) => { self.core_item_ref(i)?; } CoreInstantiationArgKind::BundleOfExports(..) => { unreachable!("should be expanded already"); } } } } CoreInstanceKind::BundleOfExports(exports) => { for export in exports { self.core_item_ref(&mut export.item)?; } } } Ok(()) } fn instance(&mut self, instance: &mut Instance<'a>) -> Result<(), Error> { match &mut instance.kind { InstanceKind::Instantiate { component, args } => { self.component_item_ref(component)?; for arg in args { match &mut arg.kind { InstantiationArgKind::Item(e) => { self.export(e)?; } InstantiationArgKind::BundleOfExports(..) => { unreachable!("should be expanded already") } } } } InstanceKind::BundleOfExports(exports) => { for export in exports { self.export(&mut export.kind)?; } } InstanceKind::Import { .. } => { unreachable!("should be expanded already") } } Ok(()) } fn item_sig(&mut self, item: &mut ItemSig<'a>) -> Result<(), Error> { match &mut item.kind { // Here we must be explicit otherwise the module type reference will // be assumed to be in the component type namespace ItemSigKind::CoreModule(t) => self.core_type_use(t), ItemSigKind::Func(t) => self.component_type_use(t), ItemSigKind::Component(t) => self.component_type_use(t), ItemSigKind::Instance(t) => self.component_type_use(t), ItemSigKind::Value(t) => self.component_val_type(&mut t.0), ItemSigKind::Type(b) => match b { TypeBounds::Eq(i) => self.resolve_ns(i, Ns::Type), TypeBounds::SubResource => Ok(()), }, } } fn export(&mut self, kind: &mut ComponentExportKind<'a>) -> Result<(), Error> { match kind { // Here we do *not* have to be explicit as the item ref is to a core module ComponentExportKind::CoreModule(r) => self.component_item_ref(r), ComponentExportKind::Func(r) => self.component_item_ref(r), ComponentExportKind::Value(r) => self.component_item_ref(r), ComponentExportKind::Type(r) => self.component_item_ref(r), ComponentExportKind::Component(r) => self.component_item_ref(r), ComponentExportKind::Instance(r) => self.component_item_ref(r), } } fn start(&mut self, start: &mut Start<'a>) -> Result<(), Error> { self.resolve_ns(&mut start.func, Ns::Func)?; for arg in start.args.iter_mut() { self.component_item_ref(arg)?; } Ok(()) } fn outer_alias>( &mut self, outer: &mut Index<'a>, index: &mut Index<'a>, kind: T, span: Span, ) -> Result<(), Error> { // 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().rev() { if resolver.id == Some(*id) { break; } depth += 1; } if depth as usize == self.stack.len() { return Err(Error::new( span, format!("outer component `{}` not found", id.name()), )); } depth } Index::Num(n, _span) => *n, }; if depth as usize >= self.stack.len() { return Err(Error::new( span, format!("outer count of `{}` is too large", depth), )); } *outer = Index::Num(depth, span); // Resolve `index` within the computed scope depth. let computed = self.stack.len() - 1 - depth as usize; self.stack[computed].resolve(kind.into(), index)?; Ok(()) } fn alias(&mut self, alias: &mut Alias<'a>) -> Result<(), Error> { match &mut alias.target { AliasTarget::Export { instance, name: _, kind: _, } => self.resolve_ns(instance, Ns::Instance), AliasTarget::CoreExport { instance, name: _, kind: _, } => self.resolve_ns(instance, Ns::CoreInstance), AliasTarget::Outer { outer, index, kind } => { self.outer_alias(outer, index, *kind, alias.span) } } } fn canonical_func(&mut self, func: &mut CanonicalFunc<'a>) -> Result<(), Error> { let opts = match &mut func.kind { CanonicalFuncKind::Lift { ty, info } => { self.component_type_use(ty)?; self.core_item_ref(&mut info.func)?; &mut info.opts } CanonicalFuncKind::Lower(info) => { self.component_item_ref(&mut info.func)?; &mut info.opts } CanonicalFuncKind::ResourceNew(info) => return self.resolve_ns(&mut info.ty, Ns::Type), CanonicalFuncKind::ResourceRep(info) => return self.resolve_ns(&mut info.ty, Ns::Type), CanonicalFuncKind::ResourceDrop(info) => { return self.resolve_ns(&mut info.ty, Ns::Type) } }; for opt in opts { match opt { CanonOpt::StringUtf8 | CanonOpt::StringUtf16 | CanonOpt::StringLatin1Utf16 => {} CanonOpt::Memory(r) => self.core_item_ref(r)?, CanonOpt::Realloc(r) | CanonOpt::PostReturn(r) => self.core_item_ref(r)?, } } Ok(()) } fn core_type_use(&mut self, ty: &mut CoreTypeUse<'a, T>) -> Result<(), Error> { let item = match ty { CoreTypeUse::Ref(r) => r, CoreTypeUse::Inline(_) => { unreachable!("inline type-use should be expanded by now") } }; self.core_item_ref(item) } fn component_type_use(&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.component_item_ref(item) } fn defined_type(&mut self, ty: &mut ComponentDefinedType<'a>) -> Result<(), Error> { match ty { ComponentDefinedType::Primitive(_) => {} ComponentDefinedType::Flags(_) => {} ComponentDefinedType::Enum(_) => {} ComponentDefinedType::Record(r) => { for field in r.fields.iter_mut() { self.component_val_type(&mut field.ty)?; } } ComponentDefinedType::Variant(v) => { // Namespace for case identifier resolution let mut ns = Namespace::default(); for case in v.cases.iter_mut() { let index = ns.register(case.id, "variant case")?; if let Some(ty) = &mut case.ty { self.component_val_type(ty)?; } if let Some(refines) = &mut case.refines { if let Refinement::Index(span, idx) = refines { let resolved = ns.resolve(idx, "variant case")?; if resolved == index { return Err(Error::new( *span, "variant case cannot refine itself".to_string(), )); } *refines = Refinement::Resolved(resolved); } } } } ComponentDefinedType::List(l) => { self.component_val_type(&mut l.element)?; } ComponentDefinedType::Tuple(t) => { for field in t.fields.iter_mut() { self.component_val_type(field)?; } } ComponentDefinedType::Option(o) => { self.component_val_type(&mut o.element)?; } ComponentDefinedType::Result(r) => { if let Some(ty) = &mut r.ok { self.component_val_type(ty)?; } if let Some(ty) = &mut r.err { self.component_val_type(ty)?; } } ComponentDefinedType::Own(t) | ComponentDefinedType::Borrow(t) => { self.resolve_ns(t, Ns::Type)?; } } Ok(()) } fn component_val_type(&mut self, ty: &mut ComponentValType<'a>) -> Result<(), Error> { match ty { ComponentValType::Ref(idx) => self.resolve_ns(idx, Ns::Type), ComponentValType::Inline(ComponentDefinedType::Primitive(_)) => Ok(()), ComponentValType::Inline(_) => unreachable!("should be expanded by now"), } } fn core_ty(&mut self, field: &mut CoreType<'a>) -> Result<(), Error> { match &mut field.def { CoreTypeDef::Def(ty) => self.stack.last_mut().unwrap().resolve_type_def(ty)?, CoreTypeDef::Module(t) => { self.stack.push(ComponentState::new(field.id)); self.module_type(t)?; self.stack.pop(); } } Ok(()) } fn ty(&mut self, field: &mut Type<'a>) -> Result<(), Error> { match &mut field.def { TypeDef::Defined(t) => { self.defined_type(t)?; } TypeDef::Func(f) => { for param in f.params.iter_mut() { self.component_val_type(&mut param.ty)?; } for result in f.results.iter_mut() { self.component_val_type(&mut result.ty)?; } } TypeDef::Component(c) => { self.stack.push(ComponentState::new(field.id)); self.component_type(c)?; self.stack.pop(); } TypeDef::Instance(i) => { self.stack.push(ComponentState::new(field.id)); self.instance_type(i)?; self.stack.pop(); } TypeDef::Resource(r) => { match &mut r.rep { ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} ValType::Ref(r) => match &mut r.heap { core::HeapType::Abstract { .. } => {} core::HeapType::Concrete(id) => { self.resolve_ns(id, Ns::Type)?; } }, } if let Some(dtor) = &mut r.dtor { self.core_item_ref(dtor)?; } } } Ok(()) } fn component_type(&mut self, c: &mut ComponentType<'a>) -> Result<(), Error> { self.resolve_prepending_aliases( &mut c.decls, |resolver, decl| match decl { ComponentTypeDecl::Alias(alias) => resolver.alias(alias), ComponentTypeDecl::CoreType(ty) => resolver.core_ty(ty), ComponentTypeDecl::Type(ty) => resolver.ty(ty), ComponentTypeDecl::Import(import) => resolver.item_sig(&mut import.item), ComponentTypeDecl::Export(export) => resolver.item_sig(&mut export.item), }, |state, decl| { match decl { ComponentTypeDecl::Alias(alias) => { state.register_alias(alias)?; } ComponentTypeDecl::CoreType(ty) => { state.core_types.register(ty.id, "core type")?; } ComponentTypeDecl::Type(ty) => { state.types.register(ty.id, "type")?; } ComponentTypeDecl::Export(e) => { state.register_item_sig(&e.item)?; } ComponentTypeDecl::Import(i) => { state.register_item_sig(&i.item)?; } } Ok(()) }, ) } fn instance_type(&mut self, c: &mut InstanceType<'a>) -> Result<(), Error> { self.resolve_prepending_aliases( &mut c.decls, |resolver, decl| match decl { InstanceTypeDecl::Alias(alias) => resolver.alias(alias), InstanceTypeDecl::CoreType(ty) => resolver.core_ty(ty), InstanceTypeDecl::Type(ty) => resolver.ty(ty), InstanceTypeDecl::Export(export) => resolver.item_sig(&mut export.item), }, |state, decl| { match decl { InstanceTypeDecl::Alias(alias) => { state.register_alias(alias)?; } InstanceTypeDecl::CoreType(ty) => { state.core_types.register(ty.id, "core type")?; } InstanceTypeDecl::Type(ty) => { state.types.register(ty.id, "type")?; } InstanceTypeDecl::Export(export) => { state.register_item_sig(&export.item)?; } } Ok(()) }, ) } fn core_item_ref(&mut self, item: &mut CoreItemRef<'a, K>) -> Result<(), Error> where K: CoreItem + Copy, { // Check for not being an instance export reference if item.export_name.is_none() { self.resolve_ns(&mut item.idx, item.kind.ns())?; return Ok(()); } // This is a reference to a core instance export let mut index = item.idx; self.resolve_ns(&mut index, Ns::CoreInstance)?; // Record an alias to reference the export let span = item.idx.span(); let alias = Alias { span, id: None, name: None, target: AliasTarget::CoreExport { instance: index, name: item.export_name.unwrap(), kind: item.kind.ns().into(), }, }; index = Index::Num(self.current().register_alias(&alias)?, span); self.aliases_to_insert.push(alias); item.idx = index; item.export_name = None; Ok(()) } fn component_item_ref(&mut self, item: &mut ItemRef<'a, K>) -> Result<(), Error> where K: ComponentItem + Copy, { // Check for not being an instance export reference if item.export_names.is_empty() { self.resolve_ns(&mut item.idx, item.kind.ns())?; return Ok(()); } // This is a reference to an instance export let mut index = item.idx; self.resolve_ns(&mut index, Ns::Instance)?; let span = item.idx.span(); for (pos, export_name) in item.export_names.iter().enumerate() { // Record an alias to reference the export let alias = Alias { span, id: None, name: None, target: AliasTarget::Export { instance: index, name: export_name, kind: if pos == item.export_names.len() - 1 { item.kind.ns().into() } else { ComponentExportAliasKind::Instance }, }, }; index = Index::Num(self.current().register_alias(&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; 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, 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 alias = Alias { span, id: Some(id), name: None, target: AliasTarget::Outer { outer: Index::Num(depth, span), index: Index::Num(found, span), kind: ns.into(), }, }; let local_index = self.current().register_alias(&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 module_type(&mut self, ty: &mut ModuleType<'a>) -> Result<(), Error> { return self.resolve_prepending_aliases( &mut ty.decls, |resolver, decl| match decl { ModuleTypeDecl::Alias(alias) => resolver.alias(alias), ModuleTypeDecl::Type(_) => Ok(()), ModuleTypeDecl::Import(import) => resolve_item_sig(resolver, &mut import.item), ModuleTypeDecl::Export(_, item) => resolve_item_sig(resolver, item), }, |state, decl| { match decl { ModuleTypeDecl::Alias(alias) => { state.register_alias(alias)?; } ModuleTypeDecl::Type(ty) => { state.core_types.register(ty.id, "type")?; } // Only the type namespace is populated within the module type // namespace so these are ignored here. ModuleTypeDecl::Import(_) | ModuleTypeDecl::Export(..) => {} } Ok(()) }, ); fn resolve_item_sig<'a>( resolver: &Resolver<'a>, sig: &mut core::ItemSig<'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"); resolver .stack .last() .unwrap() .core_types .resolve(idx, "type")?; } core::ItemKind::Memory(_) | core::ItemKind::Global(_) | core::ItemKind::Table(_) => {} } Ok(()) } } } impl<'a> ComponentState<'a> { fn resolve(&self, ns: Ns, idx: &mut Index<'a>) -> Result { match ns { Ns::CoreFunc => self.core_funcs.resolve(idx, "core func"), Ns::CoreGlobal => self.core_globals.resolve(idx, "core global"), Ns::CoreTable => self.core_tables.resolve(idx, "core table"), Ns::CoreMemory => self.core_memories.resolve(idx, "core memory"), Ns::CoreType => self.core_types.resolve(idx, "core type"), Ns::CoreTag => self.core_tags.resolve(idx, "core tag"), Ns::CoreInstance => self.core_instances.resolve(idx, "core instance"), Ns::CoreModule => self.core_modules.resolve(idx, "core module"), Ns::Func => self.funcs.resolve(idx, "func"), Ns::Type => self.types.resolve(idx, "type"), Ns::Instance => self.instances.resolve(idx, "instance"), Ns::Component => self.components.resolve(idx, "component"), Ns::Value => self.values.resolve(idx, "value"), } } /// Assign an index to the given field. fn register(&mut self, item: &ComponentField<'a>) -> Result<(), Error> { match item { ComponentField::CoreModule(m) => self.core_modules.register(m.id, "core module")?, ComponentField::CoreInstance(i) => { self.core_instances.register(i.id, "core instance")? } ComponentField::CoreType(t) => self.core_types.register(t.id, "core type")?, ComponentField::Component(c) => self.components.register(c.id, "component")?, ComponentField::Instance(i) => self.instances.register(i.id, "instance")?, ComponentField::Alias(a) => self.register_alias(a)?, ComponentField::Type(t) => self.types.register(t.id, "type")?, ComponentField::CanonicalFunc(f) => match &f.kind { CanonicalFuncKind::Lift { .. } => self.funcs.register(f.id, "func")?, CanonicalFuncKind::Lower(_) | CanonicalFuncKind::ResourceNew(_) | CanonicalFuncKind::ResourceRep(_) | CanonicalFuncKind::ResourceDrop(_) => { self.core_funcs.register(f.id, "core func")? } }, ComponentField::CoreFunc(_) | ComponentField::Func(_) => { unreachable!("should be expanded already") } ComponentField::Start(s) => { for r in &s.results { self.values.register(*r, "value")?; } return Ok(()); } ComponentField::Import(i) => self.register_item_sig(&i.item)?, ComponentField::Export(e) => match &e.kind { ComponentExportKind::CoreModule(_) => { self.core_modules.register(e.id, "core module")? } ComponentExportKind::Func(_) => self.funcs.register(e.id, "func")?, ComponentExportKind::Instance(_) => self.instances.register(e.id, "instance")?, ComponentExportKind::Value(_) => self.values.register(e.id, "value")?, ComponentExportKind::Component(_) => self.components.register(e.id, "component")?, ComponentExportKind::Type(_) => self.types.register(e.id, "type")?, }, ComponentField::Custom(_) | ComponentField::Producers(_) => return Ok(()), }; Ok(()) } fn register_alias(&mut self, alias: &Alias<'a>) -> Result { match alias.target { AliasTarget::Export { kind, .. } => match kind { ComponentExportAliasKind::CoreModule => { self.core_modules.register(alias.id, "core module") } ComponentExportAliasKind::Func => self.funcs.register(alias.id, "func"), ComponentExportAliasKind::Value => self.values.register(alias.id, "value"), ComponentExportAliasKind::Type => self.types.register(alias.id, "type"), ComponentExportAliasKind::Component => { self.components.register(alias.id, "component") } ComponentExportAliasKind::Instance => self.instances.register(alias.id, "instance"), }, AliasTarget::CoreExport { kind, .. } => match kind { core::ExportKind::Func => self.core_funcs.register(alias.id, "core func"), core::ExportKind::Table => self.core_tables.register(alias.id, "core table"), core::ExportKind::Memory => self.core_memories.register(alias.id, "core memory"), core::ExportKind::Global => self.core_globals.register(alias.id, "core global"), core::ExportKind::Tag => self.core_tags.register(alias.id, "core tag"), }, AliasTarget::Outer { kind, .. } => match kind { ComponentOuterAliasKind::CoreModule => { self.core_modules.register(alias.id, "core module") } ComponentOuterAliasKind::CoreType => { self.core_types.register(alias.id, "core type") } ComponentOuterAliasKind::Type => self.types.register(alias.id, "type"), ComponentOuterAliasKind::Component => { self.components.register(alias.id, "component") } }, } } } impl<'a> ResolveCoreType<'a> for ComponentState<'a> { fn resolve_type_name(&self, name: &mut Index<'a>) -> Result { self.resolve(Ns::CoreType, name) } } #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] enum Ns { CoreFunc, CoreGlobal, CoreTable, CoreMemory, CoreType, CoreTag, CoreInstance, CoreModule, Func, Type, Instance, Component, Value, } trait ComponentItem { fn ns(&self) -> Ns; } trait CoreItem { fn ns(&self) -> Ns; } macro_rules! component_item { ($kw:path, $kind:ident) => { impl ComponentItem for $kw { fn ns(&self) -> Ns { Ns::$kind } } }; } macro_rules! core_item { ($kw:path, $kind:ident) => { impl CoreItem for $kw { fn ns(&self) -> Ns { Ns::$kind } } }; } component_item!(kw::func, Func); component_item!(kw::r#type, Type); component_item!(kw::r#instance, Instance); component_item!(kw::component, Component); component_item!(kw::value, Value); component_item!(kw::module, CoreModule); core_item!(kw::func, CoreFunc); core_item!(kw::memory, CoreMemory); core_item!(kw::r#type, CoreType); core_item!(kw::r#instance, CoreInstance); impl From for ComponentExportAliasKind { fn from(ns: Ns) -> Self { match ns { Ns::CoreModule => Self::CoreModule, Ns::Func => Self::Func, Ns::Type => Self::Type, Ns::Instance => Self::Instance, Ns::Component => Self::Component, Ns::Value => Self::Value, _ => unreachable!("not a component exportable namespace"), } } } impl From for ComponentOuterAliasKind { fn from(ns: Ns) -> Self { match ns { Ns::CoreModule => Self::CoreModule, Ns::CoreType => Self::CoreType, Ns::Type => Self::Type, Ns::Component => Self::Component, _ => unreachable!("not an outer alias namespace"), } } } impl From for core::ExportKind { fn from(ns: Ns) -> Self { match ns { Ns::CoreFunc => Self::Func, Ns::CoreTable => Self::Table, Ns::CoreGlobal => Self::Global, Ns::CoreMemory => Self::Memory, Ns::CoreTag => Self::Tag, _ => unreachable!("not a core exportable namespace"), } } } impl From for Ns { fn from(kind: ComponentOuterAliasKind) -> Self { match kind { ComponentOuterAliasKind::CoreModule => Self::CoreModule, ComponentOuterAliasKind::CoreType => Self::CoreType, ComponentOuterAliasKind::Type => Self::Type, ComponentOuterAliasKind::Component => Self::Component, } } } impl CoreItem for core::ExportKind { fn ns(&self) -> Ns { match self { Self::Func => Ns::CoreFunc, Self::Table => Ns::CoreTable, Self::Global => Ns::CoreGlobal, Self::Memory => Ns::CoreMemory, Self::Tag => Ns::CoreTag, } } } wast-217.0.0/src/component/types.rs000064400000000000000000000674521046102023000152720ustar 00000000000000use crate::component::*; use crate::core; use crate::kw; use crate::parser::Lookahead1; use crate::parser::Peek; use crate::parser::{Parse, Parser, Result}; use crate::token::Index; use crate::token::LParen; use crate::token::{Id, NameAnnotation, Span}; /// A core type declaration. #[derive(Debug)] pub struct CoreType<'a> { /// Where this type was defined. pub span: Span, /// An optional identifier to refer to this `core type` by as part of name /// resolution. pub id: Option>, /// An optional name for this type stored in the custom `name` section. pub name: Option>, /// The core type's definition. pub def: CoreTypeDef<'a>, } impl<'a> Parse<'a> for CoreType<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; parser.parse::()?; let id = parser.parse()?; let name = parser.parse()?; let def = parser.parens(|p| p.parse())?; Ok(Self { span, id, name, def, }) } } /// Represents a core type definition. /// /// In the future this may be removed when module types are a part of /// a core module. #[derive(Debug)] pub enum CoreTypeDef<'a> { /// The type definition is one of the core types. Def(core::TypeDef<'a>), /// The type definition is a module type. Module(ModuleType<'a>), } impl<'a> Parse<'a> for CoreTypeDef<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { parser.parse::()?; Ok(Self::Module(parser.parse()?)) } else { Ok(Self::Def(parser.parse()?)) } } } /// A type definition for a core module. #[derive(Debug)] pub struct ModuleType<'a> { /// The declarations of the module type. pub decls: Vec>, } impl<'a> Parse<'a> for ModuleType<'a> { fn parse(parser: Parser<'a>) -> Result { parser.depth_check()?; Ok(Self { decls: parser.parse()?, }) } } /// The declarations of a [`ModuleType`]. #[derive(Debug)] pub enum ModuleTypeDecl<'a> { /// A core type. Type(core::Type<'a>), /// An alias local to the component type. Alias(Alias<'a>), /// An import. Import(core::Import<'a>), /// An export. Export(&'a str, core::ItemSig<'a>), } impl<'a> Parse<'a> for ModuleTypeDecl<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { Ok(Self::Type(parser.parse()?)) } else if l.peek::()? { Ok(Self::Alias(Alias::parse_outer_core_type_alias(parser)?)) } else if l.peek::()? { Ok(Self::Import(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; let name = parser.parse()?; let et = parser.parens(|parser| parser.parse())?; Ok(Self::Export(name, et)) } else { Err(l.error()) } } } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut decls = Vec::new(); while !parser.is_empty() { decls.push(parser.parens(|parser| parser.parse())?); } Ok(decls) } } /// A type declaration in a component. #[derive(Debug)] pub struct Type<'a> { /// Where this type was defined. pub span: Span, /// An optional identifier to refer to this `type` by as part of name /// resolution. pub id: Option>, /// An optional name for this type stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: InlineExport<'a>, /// The type definition. pub def: TypeDef<'a>, } impl<'a> Type<'a> { /// Parses a `Type` while allowing inline `(export "...")` names to be /// defined. pub fn parse_maybe_with_inline_exports(parser: Parser<'a>) -> Result { Type::parse(parser, true) } fn parse_no_inline_exports(parser: Parser<'a>) -> Result { Type::parse(parser, false) } fn parse(parser: Parser<'a>, allow_inline_exports: bool) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = if allow_inline_exports { parser.parse()? } else { Default::default() }; let def = parser.parse()?; Ok(Self { span, id, name, exports, def, }) } } /// A definition of a component type. #[derive(Debug)] pub enum TypeDef<'a> { /// A defined value type. Defined(ComponentDefinedType<'a>), /// A component function type. Func(ComponentFunctionType<'a>), /// A component type. Component(ComponentType<'a>), /// An instance type. Instance(InstanceType<'a>), /// A resource type. Resource(ResourceType<'a>), } impl<'a> Parse<'a> for TypeDef<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { parser.parens(|parser| { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(Self::Func(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; Ok(Self::Component(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; Ok(Self::Instance(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; Ok(Self::Resource(parser.parse()?)) } else { Ok(Self::Defined(ComponentDefinedType::parse_non_primitive( parser, l, )?)) } }) } else { // Only primitive types have no parens Ok(Self::Defined(ComponentDefinedType::Primitive( parser.parse()?, ))) } } } /// A primitive value type. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PrimitiveValType { Bool, S8, U8, S16, U16, S32, U32, S64, U64, F32, F64, Char, String, } impl<'a> Parse<'a> for PrimitiveValType { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(Self::Bool) } else if l.peek::()? { parser.parse::()?; Ok(Self::S8) } else if l.peek::()? { parser.parse::()?; Ok(Self::U8) } else if l.peek::()? { parser.parse::()?; Ok(Self::S16) } else if l.peek::()? { parser.parse::()?; Ok(Self::U16) } else if l.peek::()? { parser.parse::()?; Ok(Self::S32) } else if l.peek::()? { parser.parse::()?; Ok(Self::U32) } else if l.peek::()? { parser.parse::()?; Ok(Self::S64) } else if l.peek::()? { parser.parse::()?; Ok(Self::U64) } else if l.peek::()? { parser.parse::()?; Ok(Self::F32) } else if l.peek::()? { parser.parse::()?; Ok(Self::F64) } else if l.peek::()? { parser.parse::()?; Ok(Self::F32) } else if l.peek::()? { parser.parse::()?; Ok(Self::F64) } else if l.peek::()? { parser.parse::()?; Ok(Self::Char) } else if l.peek::()? { parser.parse::()?; Ok(Self::String) } else { Err(l.error()) } } } impl Peek for PrimitiveValType { fn peek(cursor: crate::parser::Cursor<'_>) -> Result { Ok(matches!( cursor.keyword()?, Some(("bool", _)) | Some(("s8", _)) | Some(("u8", _)) | Some(("s16", _)) | Some(("u16", _)) | Some(("s32", _)) | Some(("u32", _)) | Some(("s64", _)) | Some(("u64", _)) | Some(("f32", _)) | Some(("f64", _)) | Some(("float32", _)) | Some(("float64", _)) | Some(("char", _)) | Some(("string", _)) )) } fn display() -> &'static str { "primitive value type" } } /// A component value type. #[allow(missing_docs)] #[derive(Debug)] pub enum ComponentValType<'a> { /// The value type is an inline defined type. Inline(ComponentDefinedType<'a>), /// The value type is an index reference to a defined type. Ref(Index<'a>), } impl<'a> Parse<'a> for ComponentValType<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::>()? { Ok(Self::Ref(parser.parse()?)) } else { Ok(Self::Inline(InlineComponentValType::parse(parser)?.0)) } } } impl Peek for ComponentValType<'_> { fn peek(cursor: crate::parser::Cursor<'_>) -> Result { Ok(Index::peek(cursor)? || ComponentDefinedType::peek(cursor)?) } fn display() -> &'static str { "component value type" } } /// An inline-only component value type. /// /// This variation does not parse type indexes. #[allow(missing_docs)] #[derive(Debug)] pub struct InlineComponentValType<'a>(ComponentDefinedType<'a>); impl<'a> Parse<'a> for InlineComponentValType<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { parser.parens(|parser| { Ok(Self(ComponentDefinedType::parse_non_primitive( parser, parser.lookahead1(), )?)) }) } else { Ok(Self(ComponentDefinedType::Primitive(parser.parse()?))) } } } // A component defined type. #[allow(missing_docs)] #[derive(Debug)] pub enum ComponentDefinedType<'a> { Primitive(PrimitiveValType), Record(Record<'a>), Variant(Variant<'a>), List(List<'a>), Tuple(Tuple<'a>), Flags(Flags<'a>), Enum(Enum<'a>), Option(OptionType<'a>), Result(ResultType<'a>), Own(Index<'a>), Borrow(Index<'a>), } impl<'a> ComponentDefinedType<'a> { fn parse_non_primitive(parser: Parser<'a>, mut l: Lookahead1<'a>) -> Result { parser.depth_check()?; if l.peek::()? { Ok(Self::Record(parser.parse()?)) } else if l.peek::()? { Ok(Self::Variant(parser.parse()?)) } else if l.peek::()? { Ok(Self::List(parser.parse()?)) } else if l.peek::()? { Ok(Self::Tuple(parser.parse()?)) } else if l.peek::()? { Ok(Self::Flags(parser.parse()?)) } else if l.peek::()? { Ok(Self::Enum(parser.parse()?)) } else if l.peek::()? { Ok(Self::Option(parser.parse()?)) } else if l.peek::()? { Ok(Self::Result(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; Ok(Self::Own(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; Ok(Self::Borrow(parser.parse()?)) } else { Err(l.error()) } } } impl Default for ComponentDefinedType<'_> { fn default() -> Self { Self::Primitive(PrimitiveValType::Bool) } } impl Peek for ComponentDefinedType<'_> { fn peek(cursor: crate::parser::Cursor<'_>) -> Result { if PrimitiveValType::peek(cursor)? { return Ok(true); } Ok(match cursor.lparen()? { Some(cursor) => matches!( cursor.keyword()?, Some(("record", _)) | Some(("variant", _)) | Some(("list", _)) | Some(("tuple", _)) | Some(("flags", _)) | Some(("enum", _)) | Some(("option", _)) | Some(("result", _)) | Some(("own", _)) | Some(("borrow", _)) ), None => false, }) } fn display() -> &'static str { "component defined type" } } /// A record defined type. #[derive(Debug)] pub struct Record<'a> { /// The fields of the record. pub fields: Vec>, } impl<'a> Parse<'a> for Record<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let mut fields = Vec::new(); while !parser.is_empty() { fields.push(parser.parens(|p| p.parse())?); } Ok(Self { fields }) } } /// A record type field. #[derive(Debug)] pub struct RecordField<'a> { /// The name of the field. pub name: &'a str, /// The type of the field. pub ty: ComponentValType<'a>, } impl<'a> Parse<'a> for RecordField<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { name: parser.parse()?, ty: parser.parse()?, }) } } /// A variant defined type. #[derive(Debug)] pub struct Variant<'a> { /// The cases of the variant type. pub cases: Vec>, } impl<'a> Parse<'a> for Variant<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let mut cases = Vec::new(); while !parser.is_empty() { cases.push(parser.parens(|p| p.parse())?); } Ok(Self { cases }) } } /// A case of a variant type. #[derive(Debug)] pub struct VariantCase<'a> { /// Where this `case` was defined pub span: Span, /// An optional identifier to refer to this case by as part of name /// resolution. pub id: Option>, /// The name of the case. pub name: &'a str, /// The optional type of the case. pub ty: Option>, /// The optional refinement. pub refines: Option>, } impl<'a> Parse<'a> for VariantCase<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let ty = parser.parse()?; let refines = if !parser.is_empty() { Some(parser.parse()?) } else { None }; Ok(Self { span, id, name, ty, refines, }) } } /// A refinement for a variant case. #[derive(Debug)] pub enum Refinement<'a> { /// The refinement is referenced by index. Index(Span, Index<'a>), /// The refinement has been resolved to an index into /// the cases of the variant. Resolved(u32), } impl<'a> Parse<'a> for Refinement<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parens(|parser| { let span = parser.parse::()?.0; let id = parser.parse()?; Ok(Self::Index(span, id)) }) } } /// A list type. #[derive(Debug)] pub struct List<'a> { /// The element type of the array. pub element: Box>, } impl<'a> Parse<'a> for List<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { element: Box::new(parser.parse()?), }) } } /// A tuple type. #[derive(Debug)] pub struct Tuple<'a> { /// The types of the fields of the tuple. pub fields: Vec>, } impl<'a> Parse<'a> for Tuple<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let mut fields = Vec::new(); while !parser.is_empty() { fields.push(parser.parse()?); } Ok(Self { fields }) } } /// A flags type. #[derive(Debug)] pub struct Flags<'a> { /// The names of the individual flags. pub names: Vec<&'a str>, } impl<'a> Parse<'a> for Flags<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let mut names = Vec::new(); while !parser.is_empty() { names.push(parser.parse()?); } Ok(Self { names }) } } /// An enum type. #[derive(Debug)] pub struct Enum<'a> { /// The tag names of the enum. pub names: Vec<&'a str>, } impl<'a> Parse<'a> for Enum<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let mut names = Vec::new(); while !parser.is_empty() { names.push(parser.parse()?); } Ok(Self { names }) } } /// An optional type. #[derive(Debug)] pub struct OptionType<'a> { /// The type of the value, when a value is present. pub element: Box>, } impl<'a> Parse<'a> for OptionType<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { element: Box::new(parser.parse()?), }) } } /// A result type. #[derive(Debug)] pub struct ResultType<'a> { /// The type on success. pub ok: Option>>, /// The type on failure. pub err: Option>>, } impl<'a> Parse<'a> for ResultType<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let ok: Option = parser.parse()?; let err: Option = if parser.peek::()? { Some(parser.parens(|parser| { parser.parse::()?; parser.parse() })?) } else { None }; Ok(Self { ok: ok.map(Box::new), err: err.map(Box::new), }) } } /// A component function type with parameters and result. #[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 of a function, optionally each having an identifier for /// name resolution and a name for the custom `name` section. pub results: Box<[ComponentFunctionResult<'a>]>, } impl<'a> Parse<'a> for ComponentFunctionType<'a> { fn parse(parser: Parser<'a>) -> Result { let mut params: Vec = Vec::new(); while parser.peek2::()? { params.push(parser.parens(|p| p.parse())?); } let mut results: Vec = Vec::new(); while parser.peek2::()? { results.push(parser.parens(|p| p.parse())?); } Ok(Self { params: params.into(), results: results.into(), }) } } /// A parameter of a [`ComponentFunctionType`]. #[derive(Debug)] pub struct ComponentFunctionParam<'a> { /// The name of the parameter pub name: &'a str, /// The type of the parameter. pub ty: ComponentValType<'a>, } impl<'a> Parse<'a> for ComponentFunctionParam<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { name: parser.parse()?, ty: parser.parse()?, }) } } /// A result of a [`ComponentFunctionType`]. #[derive(Debug)] pub struct ComponentFunctionResult<'a> { /// An optionally-specified name of this result pub name: Option<&'a str>, /// The type of the result. pub ty: ComponentValType<'a>, } impl<'a> Parse<'a> for ComponentFunctionResult<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; Ok(Self { name: parser.parse()?, ty: parser.parse()?, }) } } /// The type of an exported item from an component or instance type. #[derive(Debug)] pub struct ComponentExportType<'a> { /// Where this export was defined. pub span: Span, /// The name of this export. pub name: ComponentExternName<'a>, /// The signature of the item. pub item: ItemSig<'a>, } impl<'a> Parse<'a> for ComponentExportType<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let debug_name = parser.parse()?; let name = parser.parse()?; let item = parser.parens(|p| { let mut item = p.parse::>()?.0; item.id = id; item.name = debug_name; Ok(item) })?; Ok(Self { span, name, item }) } } /// A type definition for a component type. #[derive(Debug, Default)] pub struct ComponentType<'a> { /// The declarations of the component type. pub decls: Vec>, } impl<'a> Parse<'a> for ComponentType<'a> { fn parse(parser: Parser<'a>) -> Result { parser.depth_check()?; Ok(Self { decls: parser.parse()?, }) } } /// A declaration of a component type. #[derive(Debug)] pub enum ComponentTypeDecl<'a> { /// A core type definition local to the component type. CoreType(CoreType<'a>), /// A type definition local to the component type. Type(Type<'a>), /// An alias local to the component type. Alias(Alias<'a>), /// An import of the component type. Import(ComponentImport<'a>), /// An export of the component type. Export(ComponentExportType<'a>), } impl<'a> Parse<'a> for ComponentTypeDecl<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { Ok(Self::CoreType(parser.parse()?)) } else if l.peek::()? { Ok(Self::Type(Type::parse_no_inline_exports(parser)?)) } else if l.peek::()? { Ok(Self::Alias(parser.parse()?)) } else if l.peek::()? { Ok(Self::Import(parser.parse()?)) } else if l.peek::()? { Ok(Self::Export(parser.parse()?)) } else { Err(l.error()) } } } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut decls = Vec::new(); while !parser.is_empty() { decls.push(parser.parens(|parser| parser.parse())?); } Ok(decls) } } /// A type definition for an instance type. #[derive(Debug)] pub struct InstanceType<'a> { /// The declarations of the instance type. pub decls: Vec>, } impl<'a> Parse<'a> for InstanceType<'a> { fn parse(parser: Parser<'a>) -> Result { parser.depth_check()?; Ok(Self { decls: parser.parse()?, }) } } /// A declaration of an instance type. #[derive(Debug)] pub enum InstanceTypeDecl<'a> { /// A core type definition local to the component type. CoreType(CoreType<'a>), /// A type definition local to the instance type. Type(Type<'a>), /// An alias local to the instance type. Alias(Alias<'a>), /// An export of the instance type. Export(ComponentExportType<'a>), } impl<'a> Parse<'a> for InstanceTypeDecl<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { Ok(Self::CoreType(parser.parse()?)) } else if l.peek::()? { Ok(Self::Type(Type::parse_no_inline_exports(parser)?)) } else if l.peek::()? { Ok(Self::Alias(parser.parse()?)) } else if l.peek::()? { Ok(Self::Export(parser.parse()?)) } else { Err(l.error()) } } } impl<'a> Parse<'a> for Vec> { fn parse(parser: Parser<'a>) -> Result { let mut decls = Vec::new(); while !parser.is_empty() { decls.push(parser.parens(|parser| parser.parse())?); } Ok(decls) } } /// A type definition for an instance type. #[derive(Debug)] pub struct ResourceType<'a> { /// Representation, in core WebAssembly, of this resource. pub rep: core::ValType<'a>, /// The declarations of the instance type. pub dtor: Option>, } impl<'a> Parse<'a> for ResourceType<'a> { fn parse(parser: Parser<'a>) -> Result { let rep = parser.parens(|p| { p.parse::()?; p.parse() })?; let dtor = if parser.is_empty() { None } else { Some(parser.parens(|p| { p.parse::()?; p.parens(|p| p.parse()) })?) }; Ok(Self { rep, dtor }) } } /// A value type declaration used for values in import signatures. #[derive(Debug)] pub struct ComponentValTypeUse<'a>(pub ComponentValType<'a>); impl<'a> Parse<'a> for ComponentValTypeUse<'a> { fn parse(parser: Parser<'a>) -> Result { match ComponentTypeUse::<'a, InlineComponentValType<'a>>::parse(parser)? { ComponentTypeUse::Ref(i) => Ok(Self(ComponentValType::Ref(i.idx))), ComponentTypeUse::Inline(t) => Ok(Self(ComponentValType::Inline(t.0))), } } } /// A reference to a core type defined in this component. /// /// This is the same as `TypeUse`, but accepts `$T` as shorthand for /// `(type $T)`. #[derive(Debug, Clone)] pub enum CoreTypeUse<'a, T> { /// The type that we're referencing. Ref(CoreItemRef<'a, kw::r#type>), /// The inline type. Inline(T), } impl<'a, T: Parse<'a>> Parse<'a> for CoreTypeUse<'a, T> { fn parse(parser: Parser<'a>) -> Result { // Here the core context is assumed, so no core prefix is expected if parser.peek::()? && parser.peek2::>()? { Ok(Self::Ref(parser.parens(|parser| parser.parse())?)) } else { Ok(Self::Inline(parser.parse()?)) } } } impl Default for CoreTypeUse<'_, T> { fn default() -> Self { let span = Span::from_offset(0); Self::Ref(CoreItemRef { idx: Index::Num(0, span), kind: kw::r#type(span), export_name: None, }) } } /// 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 { if parser.peek::()? && parser.peek2::>()? { Ok(Self::Ref(parser.parens(|parser| parser.parse())?)) } else { Ok(Self::Inline(parser.parse()?)) } } } impl Default for ComponentTypeUse<'_, T> { fn default() -> Self { let span = Span::from_offset(0); Self::Ref(ItemRef { idx: Index::Num(0, span), kind: kw::r#type(span), export_names: Vec::new(), }) } } wast-217.0.0/src/component/wast.rs000064400000000000000000000112571046102023000150740ustar 00000000000000use crate::kw; use crate::parser::{Cursor, Parse, Parser, Peek, Result}; use crate::token::{F32, F64}; /// Expression that can be used inside of `invoke` expressions for core wasm /// functions. #[derive(Debug)] #[allow(missing_docs)] pub enum WastVal<'a> { Bool(bool), U8(u8), S8(i8), U16(u16), S16(i16), U32(u32), S32(i32), U64(u64), S64(i64), F32(F32), F64(F64), Char(char), String(&'a str), List(Vec>), Record(Vec<(&'a str, WastVal<'a>)>), Tuple(Vec>), Variant(&'a str, Option>>), Enum(&'a str), Option(Option>>), Result(Result>>, Option>>>), Flags(Vec<&'a str>), } static CASES: &[(&str, fn(Parser<'_>) -> Result>)] = { use WastVal::*; &[ ("bool.const", |p| { let mut l = p.lookahead1(); if l.peek::()? { p.parse::()?; Ok(Bool(true)) } else if l.peek::()? { p.parse::()?; Ok(Bool(false)) } else { Err(l.error()) } }), ("u8.const", |p| Ok(U8(p.parse()?))), ("s8.const", |p| Ok(S8(p.parse()?))), ("u16.const", |p| Ok(U16(p.parse()?))), ("s16.const", |p| Ok(S16(p.parse()?))), ("u32.const", |p| Ok(U32(p.parse()?))), ("s32.const", |p| Ok(S32(p.parse()?))), ("u64.const", |p| Ok(U64(p.parse()?))), ("s64.const", |p| Ok(S64(p.parse()?))), ("f32.const", |p| Ok(F32(p.parse()?))), ("f64.const", |p| Ok(F64(p.parse()?))), ("char.const", |p| { let s = p.parse::<&str>()?; let mut ch = s.chars(); let ret = match ch.next() { Some(c) => c, None => return Err(p.error("empty string")), }; if ch.next().is_some() { return Err(p.error("more than one character")); } Ok(Char(ret)) }), ("str.const", |p| Ok(String(p.parse()?))), ("list.const", |p| { let mut ret = Vec::new(); while !p.is_empty() { ret.push(p.parens(|p| p.parse())?); } Ok(List(ret)) }), ("record.const", |p| { let mut ret = Vec::new(); while !p.is_empty() { ret.push(p.parens(|p| { p.parse::()?; Ok((p.parse()?, p.parse()?)) })?); } Ok(Record(ret)) }), ("tuple.const", |p| { let mut ret = Vec::new(); while !p.is_empty() { ret.push(p.parens(|p| p.parse())?); } Ok(Tuple(ret)) }), ("variant.const", |p| { let name = p.parse()?; let payload = if p.is_empty() { None } else { Some(Box::new(p.parens(|p| p.parse())?)) }; Ok(Variant(name, payload)) }), ("enum.const", |p| Ok(Enum(p.parse()?))), ("option.none", |_| Ok(Option(None))), ("option.some", |p| { Ok(Option(Some(Box::new(p.parens(|p| p.parse())?)))) }), ("result.ok", |p| { Ok(Result(Ok(if p.is_empty() { None } else { Some(Box::new(p.parens(|p| p.parse())?)) }))) }), ("result.err", |p| { Ok(Result(Err(if p.is_empty() { None } else { Some(Box::new(p.parens(|p| p.parse())?)) }))) }), ("flags.const", |p| { let mut ret = Vec::new(); while !p.is_empty() { ret.push(p.parse()?); } Ok(Flags(ret)) }), ] }; impl<'a> Parse<'a> for WastVal<'a> { fn parse(parser: Parser<'a>) -> Result { parser.depth_check()?; let parse = parser.step(|c| { if let Some((kw, rest)) = c.keyword()? { if let Some(i) = CASES.iter().position(|(name, _)| *name == kw) { return Ok((CASES[i].1, rest)); } } Err(c.error("expected a [type].const expression")) })?; parse(parser) } } impl Peek for WastVal<'_> { fn peek(cursor: Cursor<'_>) -> Result { let kw = match cursor.keyword()? { Some((kw, _)) => kw, None => return Ok(false), }; Ok(CASES.iter().any(|(name, _)| *name == kw)) } fn display() -> &'static str { "core wasm argument" } } wast-217.0.0/src/component.rs000064400000000000000000000010161046102023000141060ustar 00000000000000//! Types and support for parsing the component model text format. mod alias; pub(crate) mod binary; mod component; mod custom; mod expand; mod export; mod func; mod import; mod instance; mod item_ref; mod module; mod resolve; mod types; mod wast; pub use self::alias::*; pub use self::component::*; pub use self::custom::*; pub use self::export::*; pub use self::func::*; pub use self::import::*; pub use self::instance::*; pub use self::item_ref::*; pub use self::module::*; pub use self::types::*; pub use self::wast::*; wast-217.0.0/src/core/binary/dwarf.rs000064400000000000000000000522201046102023000154260ustar 00000000000000//! Implementation of emitting DWARF debugging information for `*.wat` files. //! //! This is intended to be relatively simple but the goal is to enable emission //! of DWARF sections which point back to the original `*.wat` file itself. This //! enables debuggers like LLDB to debug `*.wat` files without necessarily //! built-in knowledge of WebAssembly itself. //! //! Overall I was curious on weekend and decided to implement this. It's an //! off-by-default crate feature and an off-by-default runtime feature of this //! crate. Hopefully doesn't carry too much complexity with it while still being //! easy/fun to play around with. use crate::core::binary::{EncodeOptions, Encoder, GenerateDwarf, Names, RecOrType}; use crate::core::{InnerTypeKind, Local, ValType}; use crate::token::Span; use gimli::write::{ self, Address, AttributeValue, DwarfUnit, Expression, FileId, LineProgram, LineString, Sections, UnitEntryId, Writer, }; use gimli::{Encoding, Format, LineEncoding, LittleEndian}; use std::cmp::Ordering; use std::collections::HashMap; use std::path::Path; pub struct Dwarf<'a> { // Metadata configured at `Dwarf` creation time func_names: HashMap, func_imports: u32, contents: &'a str, file_id: FileId, style: GenerateDwarf, types: &'a [RecOrType<'a>], dwarf: DwarfUnit, // Next function index when `start_func` is called next_func: u32, // Current `DW_TAG_subprogram` that's being built as part of `start_func`. cur_subprogram: Option, cur_subprogram_instrs: usize, // Code-section-relative offset of the start of every function. Filled in // as part of `end_func`. sym_offsets: Vec, // Metadata tracking what the line/column information was at the specified // last offset. line: u64, column: u64, last_offset: usize, i32_ty: Option, i64_ty: Option, f32_ty: Option, f64_ty: Option, } impl<'a> Dwarf<'a> { /// Creates a new `Dwarf` from the specified configuration. /// /// * `func_imports` - the number of imported functions in this module, or /// the index of the first defined function. /// * `opts` - encoding options, namely where whether DWARF is to be emitted /// is configured. /// * `names` - the `name` custom section for this module, used to name /// functions in DWARF. pub fn new( func_imports: u32, opts: &EncodeOptions<'a>, names: &Names<'a>, types: &'a [RecOrType<'a>], ) -> Option> { // This is a load-bearing `?` which notably short-circuits all DWARF // machinery entirely if this was not enabled at runtime. let (file, contents, style) = opts.dwarf_info?; let file = file.to_str()?; // Configure some initial `gimli::write` context. let encoding = Encoding { address_size: 4, format: Format::Dwarf32, version: 5, }; let mut dwarf = DwarfUnit::new(encoding); let (comp_dir, comp_file) = match ( Path::new(file).parent().and_then(|s| s.to_str()), Path::new(file).file_name().and_then(|s| s.to_str()), ) { (Some(parent), Some(file_name)) if !parent.is_empty() => (parent, file_name), _ => (".", file), }; let comp_dir_ref = dwarf.strings.add(comp_dir); let comp_file_ref = dwarf.strings.add(comp_file); dwarf.unit.line_program = LineProgram::new( encoding, LineEncoding::default(), LineString::StringRef(comp_dir_ref), LineString::StringRef(comp_file_ref), None, ); let dir_id = dwarf.unit.line_program.default_directory(); let file_id = dwarf .unit .line_program .add_file(LineString::StringRef(comp_file_ref), dir_id, None); // Configure a single compilation unit which encompasses the entire code // section. The code section isn't fully known at this point so only a // "low pc" is emitted here. let root = dwarf.unit.root(); let cu = dwarf.unit.get_mut(root); cu.set( gimli::DW_AT_producer, AttributeValue::String(format!("wast {}", env!("CARGO_PKG_VERSION")).into_bytes()), ); cu.set( gimli::DW_AT_language, // Technically this should be something like wasm or wat but that // doesn't exist so fill in something here. AttributeValue::Language(gimli::DW_LANG_C), ); cu.set(gimli::DW_AT_name, AttributeValue::StringRef(comp_file_ref)); cu.set( gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir_ref), ); cu.set(gimli::DW_AT_low_pc, AttributeValue::Data4(0)); // Build a lookup table for defined function index to its name. let mut func_names = HashMap::new(); for (idx, name) in names.funcs.iter() { func_names.insert(*idx, *name); } // Offsets pointing to newlines are considered internally as the 0th // column of the next line, so handle the case that the contents start // with a newline. let (line, column) = if contents.starts_with("\n") { (2, 0) } else { (1, 1) }; Some(Dwarf { dwarf, style, next_func: func_imports, func_imports, sym_offsets: Vec::new(), contents, line, column, last_offset: 0, file_id, cur_subprogram: None, cur_subprogram_instrs: 0, func_names, i32_ty: None, i64_ty: None, f32_ty: None, f64_ty: None, types, }) } /// Start emitting a new function defined at `span`. /// /// This will start a new line program for this function and additionally /// configure a new `DW_TAG_subprogram` for this new function. pub fn start_func(&mut self, span: Span, ty: u32, locals: &[Local<'_>]) { self.change_linecol(span); let addr = Address::Symbol { symbol: (self.next_func - self.func_imports) as usize, addend: 0, }; self.dwarf.unit.line_program.begin_sequence(Some(addr)); let root = self.dwarf.unit.root(); let subprogram = self.dwarf.unit.add(root, gimli::DW_TAG_subprogram); let entry = self.dwarf.unit.get_mut(subprogram); let fallback = format!("wasm-function[{}]", self.next_func); let func_name = self .func_names .get(&self.next_func) .copied() .unwrap_or(&fallback); entry.set(gimli::DW_AT_name, AttributeValue::String(func_name.into())); entry.set( gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(self.file_id)), ); entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(self.line)); entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(self.column)); entry.set(gimli::DW_AT_external, AttributeValue::FlagPresent); entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(addr)); if let GenerateDwarf::Full = self.style { self.add_func_params_and_locals(subprogram, ty, locals); } self.cur_subprogram = Some(subprogram); self.cur_subprogram_instrs = 0; self.next_func += 1; } /// Adds `DW_TAG_formal_parameter` and `DW_TAG_variable` for all locals /// (which are both params and function-defined locals). /// /// This is pretty simple in that the expression for the location of /// these variables is constant, it's just "it's the local", and it spans /// the entire function. fn add_func_params_and_locals( &mut self, subprogram: UnitEntryId, ty: u32, locals: &[Local<'_>], ) { // Iterate through `self.types` which is what was encoded into the // module and find the function type which gives access to the // parameters which gives access to their types. let ty = self .types .iter() .flat_map(|t| match t { RecOrType::Type(t) => std::slice::from_ref(*t), RecOrType::Rec(r) => &r.types, }) .nth(ty as usize); let ty = match ty.map(|t| &t.def.kind) { Some(InnerTypeKind::Func(ty)) => ty, _ => return, }; let mut local_idx = 0; for (_, _, ty) in ty.params.iter() { self.local(local_idx, subprogram, gimli::DW_TAG_formal_parameter, ty); local_idx += 1; } for local in locals { self.local(local_idx, subprogram, gimli::DW_TAG_variable, &local.ty); local_idx += 1; } } /// Attempts to define a local variable within `subprogram` with the `ty` /// given. /// /// This does nothing if `ty` can't be represented in DWARF. fn local(&mut self, local: u32, subprogram: UnitEntryId, tag: gimli::DwTag, ty: &ValType<'_>) { let ty = match self.val_type_to_dwarf_type(ty) { Some(ty) => ty, None => return, }; let param = self.dwarf.unit.add(subprogram, tag); let entry = self.dwarf.unit.get_mut(param); entry.set( gimli::DW_AT_name, AttributeValue::String(format!("local{local}").into()), ); let mut loc = Expression::new(); loc.op_wasm_local(local); loc.op(gimli::DW_OP_stack_value); entry.set(gimli::DW_AT_location, AttributeValue::Exprloc(loc)); entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(ty)); } fn val_type_to_dwarf_type(&mut self, ty: &ValType<'_>) -> Option { match ty { ValType::I32 => Some(self.i32_ty()), ValType::I64 => Some(self.i64_ty()), ValType::F32 => Some(self.f32_ty()), ValType::F64 => Some(self.f64_ty()), // TODO: make a C union of sorts or something like that to // represent v128 as an array-of-lanes or u128 or something like // that. ValType::V128 => None, // Not much that can be done about reference types without actually // knowing what the engine does, this probably needs an addition to // DWARF itself to represent this. ValType::Ref(_) => None, } } /// Emit an instruction which starts at `offset` and is defined at `span`. /// /// Note that `offset` is code-section-relative. pub fn instr(&mut self, offset: usize, span: Span) { self.change_linecol(span); let offset = u64::try_from(offset).unwrap(); let mut changed = false; let row = self.dwarf.unit.line_program.row(); set(&mut row.address_offset, offset, &mut changed); set(&mut row.line, self.line, &mut changed); set(&mut row.column, self.column, &mut changed); set(&mut row.file, self.file_id, &mut changed); set(&mut row.is_statement, true, &mut changed); set( &mut row.prologue_end, self.cur_subprogram_instrs == 0, &mut changed, ); if changed { self.dwarf.unit.line_program.generate_row(); } self.cur_subprogram_instrs += 1; fn set(slot: &mut T, val: T, changed: &mut bool) { if *slot != val { *slot = val; *changed = true; } } } /// Change `self.line` and `self.column` to be appropriate for the offset /// in `span`. /// /// This will incrementally move from `self.last_offset` to `span.offset()` /// and update line/column information as we go. It's assumed that this is /// more efficient than a precalculate-all-the-positions-for-each-byte /// approach since that would require a great deal of memory to store a /// line/col for all bytes in the input string. It's also assumed that most /// `span` adjustments are minor as it's between instructions in a function /// which are frequently close together. Whether or not this assumption /// pans out is yet to be seen. fn change_linecol(&mut self, span: Span) { let offset = span.offset(); loop { match self.last_offset.cmp(&offset) { Ordering::Less => { let haystack = self.contents[self.last_offset + 1..].as_bytes(); let next_newline = match memchr::memchr_iter(b'\n', haystack).next() { Some(pos) => pos + self.last_offset + 1, None => break, }; if next_newline > offset { break; } else { self.line += 1; self.column = 0; self.last_offset = next_newline; } } Ordering::Greater => { let haystack = self.contents[..self.last_offset].as_bytes(); match memchr::memchr_iter(b'\n', haystack).next_back() { Some(prev_newline) => { if self.column == 0 { self.line -= 1; } self.column = 0; self.last_offset = prev_newline; } None => { self.line = 1; self.column = 1; self.last_offset = 0; } } } Ordering::Equal => break, } } match self.last_offset.cmp(&offset) { Ordering::Less => { self.column += (offset - self.last_offset) as u64; } Ordering::Greater => { self.column -= (self.last_offset - offset) as u64; } Ordering::Equal => {} } self.last_offset = offset; } /// Completes emission of the latest function. /// /// The latest function took `func_size` bytes to encode and the current end /// of the code section, after the function was appended, is /// `code_section_end`. pub fn end_func(&mut self, func_size: usize, code_section_end: usize) { // Add a final row corresponding to the final `end` instruction in the // function to ensure there's something for all bytecodes. let row = self.dwarf.unit.line_program.row(); row.address_offset = (func_size - 1) as u64; self.dwarf.unit.line_program.generate_row(); // This function's symbol is relative to the start of the function // itself. Functions are encoded as a leb-size-of-function then the // function itself, so to account for the size of the // leb-size-of-function we calculate the function start as the current // end of the code section minus the size of the function's bytes. self.sym_offsets.push(code_section_end - func_size); // The line program is relative to the start address, so only the // function's size is used here. self.dwarf .unit .line_program .end_sequence(u64::try_from(func_size).unwrap()); // The high PC value here is relative to `DW_AT_low_pc`, so it's the // size of the function. let entry = self.dwarf.unit.get_mut(self.cur_subprogram.take().unwrap()); entry.set( gimli::DW_AT_high_pc, AttributeValue::Data4(func_size as u32), ); } pub fn set_code_section_size(&mut self, size: usize) { let root = self.dwarf.unit.root(); let entry = self.dwarf.unit.get_mut(root); entry.set(gimli::DW_AT_high_pc, AttributeValue::Data4(size as u32)); } pub fn emit(&mut self, dst: &mut Encoder<'_>) { let mut sections = Sections::new(DwarfWriter { sym_offsets: &self.sym_offsets, bytes: Vec::new(), }); self.dwarf.write(&mut sections).unwrap(); sections .for_each(|id, writer| { if !writer.bytes.is_empty() { dst.wasm.section(&wasm_encoder::CustomSection { name: id.name().into(), data: (&writer.bytes).into(), }); } Ok::<_, std::convert::Infallible>(()) }) .unwrap(); } fn i32_ty(&mut self) -> UnitEntryId { if self.i32_ty.is_none() { self.i32_ty = Some(self.mk_primitive("i32", 4, gimli::DW_ATE_signed)); } self.i32_ty.unwrap() } fn i64_ty(&mut self) -> UnitEntryId { if self.i64_ty.is_none() { self.i64_ty = Some(self.mk_primitive("i64", 8, gimli::DW_ATE_signed)); } self.i64_ty.unwrap() } fn f32_ty(&mut self) -> UnitEntryId { if self.f32_ty.is_none() { self.f32_ty = Some(self.mk_primitive("f32", 4, gimli::DW_ATE_float)); } self.f32_ty.unwrap() } fn f64_ty(&mut self) -> UnitEntryId { if self.f64_ty.is_none() { self.f64_ty = Some(self.mk_primitive("f64", 8, gimli::DW_ATE_float)); } self.f64_ty.unwrap() } fn mk_primitive(&mut self, name: &str, byte_size: u8, encoding: gimli::DwAte) -> UnitEntryId { let name = self.dwarf.strings.add(name); let root = self.dwarf.unit.root(); let ty = self.dwarf.unit.add(root, gimli::DW_TAG_base_type); let entry = self.dwarf.unit.get_mut(ty); entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name)); entry.set(gimli::DW_AT_byte_size, AttributeValue::Data1(byte_size)); entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(encoding)); ty } } #[derive(Clone)] struct DwarfWriter<'a> { sym_offsets: &'a [usize], bytes: Vec, } impl Writer for DwarfWriter<'_> { type Endian = LittleEndian; fn endian(&self) -> Self::Endian { LittleEndian } fn len(&self) -> usize { self.bytes.len() } fn write(&mut self, bytes: &[u8]) -> write::Result<()> { self.bytes.extend_from_slice(bytes); Ok(()) } fn write_at(&mut self, offset: usize, bytes: &[u8]) -> write::Result<()> { self.bytes[offset..][..bytes.len()].copy_from_slice(bytes); Ok(()) } fn write_address(&mut self, address: Address, size: u8) -> write::Result<()> { match address { Address::Constant(val) => self.write_udata(val, size), Address::Symbol { symbol, addend } => { assert_eq!(addend, 0); let offset = self.sym_offsets[symbol]; self.write_udata(offset as u64, size) } } } } #[cfg(test)] mod tests { use super::{Dwarf, EncodeOptions, GenerateDwarf}; use crate::token::Span; use rand::rngs::SmallRng; use rand::{Rng, SeedableRng}; fn linecol_test(contents: &str) { let mut dwarf = Dwarf::new( 0, EncodeOptions::default().dwarf("foo.wat".as_ref(), contents, GenerateDwarf::Lines), &Default::default(), &[], ) .unwrap(); // Print some debugging information in case someone's debugging this // test let mut offset = 0; for (i, line) in contents.lines().enumerate() { println!( "line {:2} is at {:2} .. {:2}", i + 1, offset, offset + line.len() ); offset += line.len() + 1; } println!(""); // Precalculate (line, col) for all characters, assumed to all be one // byte here. let mut precalculated_linecols = Vec::new(); let mut line = 1; let mut col = 1; for c in contents.chars() { if c == '\n' { line += 1; col = 0; } precalculated_linecols.push((line, col)); col += 1; } // Traverse randomly throughout this string and assert that the // incremental update matches the precalculated position. let mut rand = SmallRng::seed_from_u64(102); for _ in 0..1000 { let pos = rand.gen_range(0..contents.len()); dwarf.change_linecol(Span::from_offset(pos)); let (line, col) = precalculated_linecols[pos]; assert_eq!(dwarf.line, line, "line mismatch"); assert_eq!(dwarf.column, col, "column mismatch"); } } #[test] fn linecol_simple() { linecol_test( "a b c (; ... ;) d e f fg", ); } #[test] fn linecol_empty() { linecol_test("x"); } #[test] fn linecol_start_newline() { linecol_test("\nx ab\nyyy \ncc"); } #[test] fn linecol_lots_of_newlines() { linecol_test("\n\n\n\n"); } #[test] fn linecol_interspersed() { linecol_test("\na\nb\nc\n"); } } wast-217.0.0/src/core/binary/dwarf_disabled.rs000064400000000000000000000020001046102023000172440ustar 00000000000000//! Dummy version of dwarf emission that does nothing when the compile-time //! feature is disabled. use crate::core::binary::{EncodeOptions, Encoder, Names, RecOrType}; use crate::core::Local; use crate::token::Span; pub struct Dwarf<'a> { uninhabited: &'a std::convert::Infallible, } impl<'a> Dwarf<'a> { pub fn new( _func_imports: u32, _opts: &EncodeOptions<'a>, _names: &Names<'a>, _types: &'a [RecOrType<'a>], ) -> Option> { None } pub fn start_func(&mut self, _span: Span, _ty: u32, _locals: &[Local<'_>]) { match *self.uninhabited {} } pub fn instr(&mut self, _offset: usize, _span: Span) { match *self.uninhabited {} } pub fn end_func(&mut self, _: usize, _: usize) { match *self.uninhabited {} } pub fn set_code_section_size(&mut self, _size: usize) { match *self.uninhabited {} } pub fn emit(&mut self, _dst: &mut Encoder<'_>) { match *self.uninhabited {} } } wast-217.0.0/src/core/binary.rs000064400000000000000000001276531046102023000143400ustar 00000000000000use crate::component::Component; use crate::core::*; use crate::encode::Encode; use crate::token::*; use crate::Wat; use std::marker; #[cfg(feature = "dwarf")] use std::path::Path; use wasm_encoder::SectionId; /// Options that can be specified when encoding a component or a module to /// customize what the final binary looks like. /// /// Methods such as [`Module::encode`], [`Wat::encode`], and /// [`Component::encode`] will use the default options. #[derive(Default)] pub struct EncodeOptions<'a> { #[cfg(feature = "dwarf")] dwarf_info: Option<(&'a Path, &'a str, GenerateDwarf)>, _marker: marker::PhantomData<&'a str>, } #[cfg(feature = "dwarf")] mod dwarf; #[cfg(not(feature = "dwarf"))] mod dwarf_disabled; #[cfg(not(feature = "dwarf"))] use self::dwarf_disabled as dwarf; /// Configuration of how DWARF debugging information may be generated. #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub enum GenerateDwarf { /// Only generate line tables to map binary offsets back to source /// locations. Lines, /// Generate full debugging information for both line numbers and /// variables/locals/operands. Full, } impl<'a> EncodeOptions<'a> { /// Creates a new set of default encoding options. pub fn new() -> EncodeOptions<'a> { EncodeOptions::default() } /// Enables emission of DWARF debugging information in the final binary. /// /// This method will use the `file` specified as the source file for the /// `*.wat` file whose `contents` must also be supplied here. These are /// used to calculate filenames/line numbers and are referenced from the /// generated DWARF. #[cfg(feature = "dwarf")] pub fn dwarf(&mut self, file: &'a Path, contents: &'a str, style: GenerateDwarf) -> &mut Self { self.dwarf_info = Some((file, contents, style)); self } /// Encodes the given [`Module`] with these options. /// /// For more information see [`Module::encode`]. pub fn encode_module( &self, module: &mut Module<'_>, ) -> std::result::Result, crate::Error> { module.resolve()?; Ok(match &module.kind { ModuleKind::Text(fields) => encode(&module.id, &module.name, fields, self), ModuleKind::Binary(blobs) => blobs.iter().flat_map(|b| b.iter().cloned()).collect(), }) } /// Encodes the given [`Component`] with these options. /// /// For more information see [`Component::encode`]. pub fn encode_component( &self, component: &mut Component<'_>, ) -> std::result::Result, crate::Error> { component.resolve()?; Ok(crate::component::binary::encode(component, self)) } /// Encodes the given [`Wat`] with these options. /// /// For more information see [`Wat::encode`]. pub fn encode_wat(&self, wat: &mut Wat<'_>) -> std::result::Result, crate::Error> { match wat { Wat::Module(m) => self.encode_module(m), Wat::Component(c) => self.encode_component(c), } } } pub(crate) fn encode( module_id: &Option>, module_name: &Option>, fields: &[ModuleField<'_>], opts: &EncodeOptions, ) -> Vec { use CustomPlace::*; use CustomPlaceAnchor::*; let mut types = Vec::new(); let mut imports = Vec::new(); let mut funcs = Vec::new(); let mut tables = Vec::new(); let mut memories = Vec::new(); let mut globals = Vec::new(); let mut exports = Vec::new(); let mut start = Vec::new(); let mut elem = Vec::new(); let mut data = Vec::new(); let mut tags = Vec::new(); let mut customs = Vec::new(); for field in fields { match field { ModuleField::Type(i) => types.push(RecOrType::Type(i)), ModuleField::Rec(i) => types.push(RecOrType::Rec(i)), ModuleField::Import(i) => imports.push(i), ModuleField::Func(i) => funcs.push(i), ModuleField::Table(i) => tables.push(i), ModuleField::Memory(i) => memories.push(i), ModuleField::Global(i) => globals.push(i), ModuleField::Export(i) => exports.push(i), ModuleField::Start(i) => start.push(i), ModuleField::Elem(i) => elem.push(i), ModuleField::Data(i) => data.push(i), ModuleField::Tag(i) => tags.push(i), ModuleField::Custom(i) => customs.push(i), } } let mut e = Encoder { wasm: wasm_encoder::Module::new(), tmp: Vec::new(), customs: &customs, }; e.custom_sections(BeforeFirst); e.section_list(SectionId::Type, Type, &types); e.section_list(SectionId::Import, Import, &imports); let functys = funcs.iter().map(|f| &f.ty).collect::>(); e.section_list(SectionId::Function, Func, &functys); e.section_list(SectionId::Table, Table, &tables); e.section_list(SectionId::Memory, Memory, &memories); e.section_list(SectionId::Tag, Tag, &tags); e.section_list(SectionId::Global, Global, &globals); e.section_list(SectionId::Export, Export, &exports); e.custom_sections(Before(Start)); if let Some(start) = start.get(0) { e.wasm.section(&wasm_encoder::StartSection { function_index: start.unwrap_u32(), }); } e.custom_sections(After(Start)); e.section_list(SectionId::Element, Elem, &elem); if needs_data_count(&funcs) { e.wasm.section(&wasm_encoder::DataCountSection { count: data.len().try_into().unwrap(), }); } // Prepare to and emit the code section. This is where DWARF may optionally // be emitted depending on configuration settings. Note that `code_section` // will internally emit the branch hints section if necessary. let names = find_names(module_id, module_name, fields); let num_import_funcs = imports .iter() .filter(|i| matches!(i.item.kind, ItemKind::Func(..))) .count() as u32; let mut dwarf = dwarf::Dwarf::new(num_import_funcs, opts, &names, &types); e.code_section(&funcs, num_import_funcs, dwarf.as_mut()); e.section_list(SectionId::Data, Data, &data); if !names.is_empty() { e.custom_section("name", &names); } e.custom_sections(AfterLast); if let Some(dwarf) = &mut dwarf { dwarf.emit(&mut e); } return e.wasm.finish(); fn needs_data_count(funcs: &[&crate::core::Func<'_>]) -> bool { funcs .iter() .filter_map(|f| match &f.kind { FuncKind::Inline { expression, .. } => Some(expression), _ => None, }) .flat_map(|e| e.instrs.iter()) .any(|i| i.needs_data_count()) } } struct Encoder<'a> { wasm: wasm_encoder::Module, tmp: Vec, customs: &'a [&'a Custom<'a>], } impl Encoder<'_> { fn section(&mut self, id: SectionId, section: &dyn Encode) { self.tmp.truncate(0); section.encode(&mut self.tmp); self.wasm.section(&wasm_encoder::RawSection { id: id as u8, data: &self.tmp, }); } fn custom_sections(&mut self, place: CustomPlace) { for entry in self.customs.iter() { if entry.place() == place { self.custom_section(entry.name(), entry); } } } fn custom_section(&mut self, name: &str, data: &dyn Encode) { self.tmp.truncate(0); data.encode(&mut self.tmp); self.wasm.section(&wasm_encoder::CustomSection { name: name.into(), data: (&self.tmp).into(), }); } fn section_list( &mut self, id: wasm_encoder::SectionId, anchor: CustomPlaceAnchor, list: &[impl Encode], ) { self.custom_sections(CustomPlace::Before(anchor)); if !list.is_empty() { self.section(id, &list) } self.custom_sections(CustomPlace::After(anchor)); } /// Encodes the code section of a wasm module module while additionally /// handling the branch hinting proposal. /// /// The branch hinting proposal requires to encode the offsets of the /// instructions relative from the beginning of the function. Here we encode /// each instruction and we save its offset. If needed, we use this /// information to build the branch hint section and insert it before the /// code section. /// /// The `list` provided is the list of functions that are emitted into the /// code section. The `func_index` provided is the initial index of defined /// functions, so it's the count of imported functions. The `dwarf` field is /// optionally used to track debugging information. fn code_section<'a>( &'a mut self, list: &[&'a Func<'_>], mut func_index: u32, mut dwarf: Option<&mut dwarf::Dwarf>, ) { self.custom_sections(CustomPlace::Before(CustomPlaceAnchor::Code)); if !list.is_empty() { let mut branch_hints = Vec::new(); let mut code_section = Vec::new(); list.len().encode(&mut code_section); for func in list.iter() { let hints = func.encode(&mut code_section, dwarf.as_deref_mut()); if !hints.is_empty() { branch_hints.push(FunctionBranchHints { func_index, hints }); } func_index += 1; } // Branch hints section has to be inserted before the Code section // Insert the section only if we have some hints if !branch_hints.is_empty() { self.custom_section("metadata.code.branch_hint", &branch_hints); } // Finally, insert the Code section from the tmp buffer self.wasm.section(&wasm_encoder::RawSection { id: wasm_encoder::SectionId::Code as u8, data: &code_section, }); if let Some(dwarf) = &mut dwarf { dwarf.set_code_section_size(code_section.len()); } } self.custom_sections(CustomPlace::After(CustomPlaceAnchor::Code)); } } impl Encode for FunctionType<'_> { fn encode(&self, e: &mut Vec) { self.params.len().encode(e); for (_, _, ty) in self.params.iter() { ty.encode(e); } self.results.encode(e); } } impl Encode for StructType<'_> { fn encode(&self, e: &mut Vec) { self.fields.len().encode(e); for field in self.fields.iter() { field.ty.encode(e); (field.mutable as i32).encode(e); } } } impl Encode for ArrayType<'_> { fn encode(&self, e: &mut Vec) { self.ty.encode(e); (self.mutable as i32).encode(e); } } impl Encode for ExportType<'_> { fn encode(&self, e: &mut Vec) { self.name.encode(e); self.item.encode(e); } } enum RecOrType<'a> { Type(&'a Type<'a>), Rec(&'a Rec<'a>), } impl Encode for RecOrType<'_> { fn encode(&self, e: &mut Vec) { match self { RecOrType::Type(ty) => ty.encode(e), RecOrType::Rec(rec) => rec.encode(e), } } } impl Encode for Type<'_> { fn encode(&self, e: &mut Vec) { match (&self.parent, self.final_type) { (Some(parent), Some(true)) => { // Type is final with a supertype e.push(0x4f); e.push(0x01); parent.encode(e); } (Some(parent), Some(false) | None) => { // Type is not final and has a declared supertype e.push(0x50); e.push(0x01); parent.encode(e); } (None, Some(false)) => { // Sub was used without any declared supertype e.push(0x50); e.push(0x00); } (None, _) => {} // No supertype, sub wasn't used } if self.def.shared { e.push(0x65); } match &self.def.kind { InnerTypeKind::Func(func) => { e.push(0x60); func.encode(e) } InnerTypeKind::Struct(r#struct) => { e.push(0x5f); r#struct.encode(e) } InnerTypeKind::Array(array) => { e.push(0x5e); array.encode(e) } } } } impl Encode for Rec<'_> { fn encode(&self, e: &mut Vec) { e.push(0x4e); self.types.len().encode(e); for ty in &self.types { ty.encode(e); } } } impl Encode for Option> { fn encode(&self, _e: &mut Vec) { // used for parameters in the tuple impl as well as instruction labels } } impl<'a> Encode for ValType<'a> { fn encode(&self, e: &mut Vec) { match self { ValType::I32 => e.push(0x7f), ValType::I64 => e.push(0x7e), ValType::F32 => e.push(0x7d), ValType::F64 => e.push(0x7c), ValType::V128 => e.push(0x7b), ValType::Ref(ty) => { ty.encode(e); } } } } impl<'a> Encode for HeapType<'a> { fn encode(&self, e: &mut Vec) { match self { HeapType::Abstract { shared, ty } => { if *shared { e.push(0x65); } ty.encode(e) } // Note that this is encoded as a signed leb128 so be sure to cast // to an i64 first HeapType::Concrete(Index::Num(n, _)) => i64::from(*n).encode(e), HeapType::Concrete(Index::Id(n)) => { panic!("unresolved index in emission: {:?}", n) } } } } impl<'a> Encode for AbstractHeapType { fn encode(&self, e: &mut Vec) { use AbstractHeapType::*; match self { Func => e.push(0x70), Extern => e.push(0x6f), Exn => e.push(0x69), Any => e.push(0x6e), Eq => e.push(0x6d), Struct => e.push(0x6b), Array => e.push(0x6a), I31 => e.push(0x6c), NoFunc => e.push(0x73), NoExtern => e.push(0x72), NoExn => e.push(0x74), None => e.push(0x71), } } } impl<'a> Encode for RefType<'a> { fn encode(&self, e: &mut Vec) { match self { // Binary abbreviations (i.e., short form), for when the ref is // nullable. RefType { nullable: true, heap: heap @ HeapType::Abstract { .. }, } => { heap.encode(e); } // Generic 'ref null ' encoding (i.e., long form). RefType { nullable: true, heap, } => { e.push(0x63); heap.encode(e); } // Generic 'ref ' encoding. RefType { nullable: false, heap, } => { e.push(0x64); heap.encode(e); } } } } impl<'a> Encode for StorageType<'a> { fn encode(&self, e: &mut Vec) { match self { StorageType::I8 => e.push(0x78), StorageType::I16 => e.push(0x77), StorageType::Val(ty) => { ty.encode(e); } } } } impl Encode for Import<'_> { fn encode(&self, e: &mut Vec) { self.module.encode(e); self.field.encode(e); self.item.encode(e); } } impl Encode for ItemSig<'_> { fn encode(&self, e: &mut Vec) { match &self.kind { ItemKind::Func(f) => { e.push(0x00); f.encode(e); } ItemKind::Table(f) => { e.push(0x01); f.encode(e); } ItemKind::Memory(f) => { e.push(0x02); f.encode(e); } ItemKind::Global(f) => { e.push(0x03); f.encode(e); } ItemKind::Tag(f) => { e.push(0x04); f.encode(e); } } } } impl Encode for TypeUse<'_, T> { fn encode(&self, e: &mut Vec) { self.index .as_ref() .expect("TypeUse should be filled in by this point") .encode(e) } } impl Encode for Index<'_> { fn encode(&self, e: &mut Vec) { self.unwrap_u32().encode(e) } } impl Index<'_> { fn unwrap_u32(&self) -> u32 { match self { Index::Num(n, _) => *n, Index::Id(n) => panic!("unresolved index in emission: {:?}", n), } } } impl<'a> Encode for TableType<'a> { fn encode(&self, e: &mut Vec) { self.elem.encode(e); let mut flags = 0; if self.limits.max.is_some() { flags |= 1 << 0; } if self.shared { flags |= 1 << 1; } if self.limits.is64 { flags |= 1 << 2; } e.push(flags); self.limits.min.encode(e); if let Some(max) = self.limits.max { max.encode(e); } } } impl Encode for MemoryType { fn encode(&self, e: &mut Vec) { let mut flags = 0; if self.limits.max.is_some() { flags |= 1 << 0; } if self.shared { flags |= 1 << 1; } if self.limits.is64 { flags |= 1 << 2; } if self.page_size_log2.is_some() { flags |= 1 << 3; } e.push(flags); self.limits.min.encode(e); if let Some(max) = self.limits.max { max.encode(e); } if let Some(p) = self.page_size_log2 { p.encode(e); } } } impl<'a> Encode for GlobalType<'a> { fn encode(&self, e: &mut Vec) { self.ty.encode(e); let mut flags = 0; if self.mutable { flags |= 0b01; } if self.shared { flags |= 0b10; } e.push(flags); } } impl Encode for Table<'_> { fn encode(&self, e: &mut Vec) { assert!(self.exports.names.is_empty()); match &self.kind { TableKind::Normal { ty, init_expr: None, } => ty.encode(e), TableKind::Normal { ty, init_expr: Some(init_expr), } => { e.push(0x40); e.push(0x00); ty.encode(e); init_expr.encode(e, None); } _ => panic!("TableKind should be normal during encoding"), } } } impl Encode for Memory<'_> { fn encode(&self, e: &mut Vec) { assert!(self.exports.names.is_empty()); match &self.kind { MemoryKind::Normal(t) => t.encode(e), _ => panic!("MemoryKind should be normal during encoding"), } } } impl Encode for Global<'_> { fn encode(&self, e: &mut Vec) { assert!(self.exports.names.is_empty()); self.ty.encode(e); match &self.kind { GlobalKind::Inline(expr) => { let _hints = expr.encode(e, None); } _ => panic!("GlobalKind should be inline during encoding"), } } } impl Encode for Export<'_> { fn encode(&self, e: &mut Vec) { self.name.encode(e); self.kind.encode(e); self.item.encode(e); } } impl Encode for ExportKind { fn encode(&self, e: &mut Vec) { match self { ExportKind::Func => e.push(0x00), ExportKind::Table => e.push(0x01), ExportKind::Memory => e.push(0x02), ExportKind::Global => e.push(0x03), ExportKind::Tag => e.push(0x04), } } } impl Encode for Elem<'_> { fn encode(&self, e: &mut Vec) { match (&self.kind, &self.payload) { ( ElemKind::Active { table: Index::Num(0, _), offset, }, ElemPayload::Indices(_), ) => { e.push(0x00); offset.encode(e, None); } (ElemKind::Passive, ElemPayload::Indices(_)) => { e.push(0x01); // flags e.push(0x00); // extern_kind } (ElemKind::Active { table, offset }, ElemPayload::Indices(_)) => { e.push(0x02); // flags table.encode(e); offset.encode(e, None); e.push(0x00); // extern_kind } (ElemKind::Declared, ElemPayload::Indices(_)) => { e.push(0x03); // flags e.push(0x00); // extern_kind } ( ElemKind::Active { table: Index::Num(0, _), offset, }, ElemPayload::Exprs { ty: RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Func, }, }, .. }, ) => { e.push(0x04); offset.encode(e, None); } (ElemKind::Passive, ElemPayload::Exprs { ty, .. }) => { e.push(0x05); ty.encode(e); } (ElemKind::Active { table, offset }, ElemPayload::Exprs { ty, .. }) => { e.push(0x06); table.encode(e); offset.encode(e, None); ty.encode(e); } (ElemKind::Declared, ElemPayload::Exprs { ty, .. }) => { e.push(0x07); // flags ty.encode(e); } } self.payload.encode(e); } } impl Encode for ElemPayload<'_> { fn encode(&self, e: &mut Vec) { match self { ElemPayload::Indices(v) => v.encode(e), ElemPayload::Exprs { exprs, ty: _ } => { exprs.len().encode(e); for expr in exprs { expr.encode(e, None); } } } } } impl Encode for Data<'_> { fn encode(&self, e: &mut Vec) { match &self.kind { DataKind::Passive => e.push(0x01), DataKind::Active { memory: Index::Num(0, _), offset, } => { e.push(0x00); offset.encode(e, None); } DataKind::Active { memory, offset } => { e.push(0x02); memory.encode(e); offset.encode(e, None); } } self.data.iter().map(|l| l.len()).sum::().encode(e); for val in self.data.iter() { val.push_onto(e); } } } impl Func<'_> { /// Encodes the function into `e` while returning all branch hints with /// known relative offsets after encoding. /// /// The `dwarf` field is optional and used to track debugging information /// for each instruction. fn encode(&self, e: &mut Vec, mut dwarf: Option<&mut dwarf::Dwarf>) -> Vec { assert!(self.exports.names.is_empty()); let (expr, locals) = match &self.kind { FuncKind::Inline { expression, locals } => (expression, locals), _ => panic!("should only have inline functions in emission"), }; if let Some(dwarf) = &mut dwarf { let index = match self.ty.index.as_ref().unwrap() { Index::Num(n, _) => *n, _ => unreachable!(), }; dwarf.start_func(self.span, index, locals); } // Encode the function into a temporary vector because functions are // prefixed with their length. The temporary vector, when encoded, // encodes its length first then the body. let mut tmp = Vec::new(); locals.encode(&mut tmp); let branch_hints = expr.encode(&mut tmp, dwarf.as_deref_mut()); tmp.encode(e); if let Some(dwarf) = &mut dwarf { dwarf.end_func(tmp.len(), e.len()); } branch_hints } } impl Encode for Box<[Local<'_>]> { fn encode(&self, e: &mut Vec) { let mut locals_compressed = Vec::<(u32, ValType)>::new(); for local in self.iter() { if let Some((cnt, prev)) = locals_compressed.last_mut() { if *prev == local.ty { *cnt += 1; continue; } } locals_compressed.push((1, local.ty)); } locals_compressed.encode(e); } } impl Expression<'_> { /// Encodes this expression into `e` and optionally tracks debugging /// information for each instruction in `dwarf`. /// /// Returns all branch hints, if any, found while parsing this function. fn encode(&self, e: &mut Vec, mut dwarf: Option<&mut dwarf::Dwarf>) -> Vec { let mut hints = Vec::with_capacity(self.branch_hints.len()); let mut next_hint = self.branch_hints.iter().peekable(); for (i, instr) in self.instrs.iter().enumerate() { // Branch hints are stored in order of increasing `instr_index` so // check to see if the next branch hint matches this instruction's // index. if let Some(hint) = next_hint.next_if(|h| h.instr_index == i) { hints.push(BranchHint { branch_func_offset: u32::try_from(e.len()).unwrap(), branch_hint_value: hint.value, }); } // If DWARF is enabled then track this instruction's binary offset // and source location. if let Some(dwarf) = &mut dwarf { if let Some(span) = self.instr_spans.as_ref().map(|s| s[i]) { dwarf.instr(e.len(), span); } } // Finally emit the instruction and move to the next. instr.encode(e); } e.push(0x0b); hints } } impl Encode for BlockType<'_> { fn encode(&self, e: &mut Vec) { // block types using an index are encoded as an sleb, not a uleb if let Some(Index::Num(n, _)) = &self.ty.index { return i64::from(*n).encode(e); } let ty = self .ty .inline .as_ref() .expect("function type not filled in"); if ty.params.is_empty() && ty.results.is_empty() { return e.push(0x40); } if ty.params.is_empty() && ty.results.len() == 1 { return ty.results[0].encode(e); } panic!("multi-value block types should have an index"); } } impl Encode for LaneArg { fn encode(&self, e: &mut Vec) { self.lane.encode(e); } } impl Encode for MemArg<'_> { fn encode(&self, e: &mut Vec) { match &self.memory { Index::Num(0, _) => { self.align.trailing_zeros().encode(e); self.offset.encode(e); } _ => { (self.align.trailing_zeros() | (1 << 6)).encode(e); self.memory.encode(e); self.offset.encode(e); } } } } impl Encode for Ordering { fn encode(&self, buf: &mut Vec) { let flag: u8 = match self { Ordering::SeqCst => 0, Ordering::AcqRel => 1, }; flag.encode(buf); } } impl Encode for Ordered where T: Encode, { fn encode(&self, buf: &mut Vec) { self.ordering.encode(buf); self.inner.encode(buf); } } impl Encode for LoadOrStoreLane<'_> { fn encode(&self, e: &mut Vec) { self.memarg.encode(e); self.lane.encode(e); } } impl Encode for CallIndirect<'_> { fn encode(&self, e: &mut Vec) { self.ty.encode(e); self.table.encode(e); } } impl Encode for TableInit<'_> { fn encode(&self, e: &mut Vec) { self.elem.encode(e); self.table.encode(e); } } impl Encode for TableCopy<'_> { fn encode(&self, e: &mut Vec) { self.dst.encode(e); self.src.encode(e); } } impl Encode for TableArg<'_> { fn encode(&self, e: &mut Vec) { self.dst.encode(e); } } impl Encode for MemoryArg<'_> { fn encode(&self, e: &mut Vec) { self.mem.encode(e); } } impl Encode for MemoryInit<'_> { fn encode(&self, e: &mut Vec) { self.data.encode(e); self.mem.encode(e); } } impl Encode for MemoryCopy<'_> { fn encode(&self, e: &mut Vec) { self.dst.encode(e); self.src.encode(e); } } impl Encode for BrTableIndices<'_> { fn encode(&self, e: &mut Vec) { self.labels.encode(e); self.default.encode(e); } } impl Encode for F32 { fn encode(&self, e: &mut Vec) { e.extend_from_slice(&self.bits.to_le_bytes()); } } impl Encode for F64 { fn encode(&self, e: &mut Vec) { e.extend_from_slice(&self.bits.to_le_bytes()); } } #[derive(Default)] struct Names<'a> { module: Option<&'a str>, funcs: Vec<(u32, &'a str)>, func_idx: u32, locals: Vec<(u32, Vec<(u32, &'a str)>)>, labels: Vec<(u32, Vec<(u32, &'a str)>)>, globals: Vec<(u32, &'a str)>, global_idx: u32, memories: Vec<(u32, &'a str)>, memory_idx: u32, tables: Vec<(u32, &'a str)>, table_idx: u32, tags: Vec<(u32, &'a str)>, tag_idx: u32, types: Vec<(u32, &'a str)>, type_idx: u32, data: Vec<(u32, &'a str)>, data_idx: u32, elems: Vec<(u32, &'a str)>, elem_idx: u32, fields: Vec<(u32, Vec<(u32, &'a str)>)>, } fn find_names<'a>( module_id: &Option>, module_name: &Option>, fields: &[ModuleField<'a>], ) -> Names<'a> { fn get_name<'a>(id: &Option>, name: &Option>) -> Option<&'a str> { name.as_ref().map(|n| n.name).or(id.and_then(|id| { if id.is_gensym() { None } else { Some(id.name()) } })) } enum Name { Type, Global, Func, Memory, Table, Tag, Elem, Data, } let mut ret = Names::default(); ret.module = get_name(module_id, module_name); let mut names = Vec::new(); for field in fields { // Extract the kind/id/name from whatever kind of field this is... let (kind, id, name) = match field { ModuleField::Import(i) => ( match i.item.kind { ItemKind::Func(_) => Name::Func, ItemKind::Table(_) => Name::Table, ItemKind::Memory(_) => Name::Memory, ItemKind::Global(_) => Name::Global, ItemKind::Tag(_) => Name::Tag, }, &i.item.id, &i.item.name, ), ModuleField::Global(g) => (Name::Global, &g.id, &g.name), 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::Type(t) => (Name::Type, &t.id, &t.name), ModuleField::Rec(r) => { for ty in &r.types { names.push((Name::Type, &ty.id, &ty.name, field)); } continue; } 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::Export(_) | ModuleField::Start(_) | ModuleField::Custom(_) => continue, }; names.push((kind, id, name, field)); } for (kind, id, name, field) in names { // .. and using the kind we can figure out where to place this name let (list, idx) = match kind { Name::Func => (&mut ret.funcs, &mut ret.func_idx), 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::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), Name::Data => (&mut ret.data, &mut ret.data_idx), }; if let Some(name) = get_name(id, name) { list.push((*idx, name)); } // Handle module locals separately from above if let ModuleField::Func(f) = field { let mut local_names = Vec::new(); let mut label_names = Vec::new(); let mut local_idx = 0; let mut label_idx = 0; // Consult the inline type listed for local names of parameters. // This is specifically preserved during the name resolution // pass, but only for functions, so here we can look at the // original source's names. if let Some(ty) = &f.ty.inline { for (id, name, _) in ty.params.iter() { if let Some(name) = get_name(id, name) { local_names.push((local_idx, name)); } local_idx += 1; } } if let FuncKind::Inline { locals, expression, .. } = &f.kind { for local in locals.iter() { if let Some(name) = get_name(&local.id, &local.name) { local_names.push((local_idx, name)); } local_idx += 1; } for i in expression.instrs.iter() { match i { Instruction::If(block) | Instruction::Block(block) | Instruction::Loop(block) | Instruction::Try(block) | Instruction::TryTable(TryTable { block, .. }) => { if let Some(name) = get_name(&block.label, &block.label_name) { label_names.push((label_idx, name)); } label_idx += 1; } _ => {} } } } if local_names.len() > 0 { ret.locals.push((*idx, local_names)); } if label_names.len() > 0 { ret.labels.push((*idx, label_names)); } } // Handle struct fields separately from above if let ModuleField::Type(ty) = field { let mut field_names = vec![]; match &ty.def.kind { InnerTypeKind::Func(_) | InnerTypeKind::Array(_) => {} InnerTypeKind::Struct(ty_struct) => { for (idx, field) in ty_struct.fields.iter().enumerate() { if let Some(name) = get_name(&field.id, &None) { field_names.push((idx as u32, name)) } } } } if field_names.len() > 0 { ret.fields.push((*idx, field_names)) } } *idx += 1; } return ret; } impl Names<'_> { fn is_empty(&self) -> bool { self.module.is_none() && self.funcs.is_empty() && self.locals.is_empty() && self.labels.is_empty() && self.globals.is_empty() && self.memories.is_empty() && self.tables.is_empty() && self.types.is_empty() && self.elems.is_empty() && self.data.is_empty() && self.fields.is_empty() && self.tags.is_empty() // NB: specifically don't check modules/instances since they're // not encoded for now. } } impl Encode for Names<'_> { fn encode(&self, dst: &mut Vec) { let mut tmp = Vec::new(); let mut subsec = |id: u8, data: &mut Vec| { dst.push(id); data.encode(dst); data.truncate(0); }; if let Some(id) = self.module { id.encode(&mut tmp); subsec(0, &mut tmp); } if self.funcs.len() > 0 { self.funcs.encode(&mut tmp); subsec(1, &mut tmp); } if self.locals.len() > 0 { self.locals.encode(&mut tmp); subsec(2, &mut tmp); } if self.labels.len() > 0 { self.labels.encode(&mut tmp); subsec(3, &mut tmp); } if self.types.len() > 0 { self.types.encode(&mut tmp); subsec(4, &mut tmp); } if self.tables.len() > 0 { self.tables.encode(&mut tmp); subsec(5, &mut tmp); } if self.memories.len() > 0 { self.memories.encode(&mut tmp); subsec(6, &mut tmp); } if self.globals.len() > 0 { self.globals.encode(&mut tmp); subsec(7, &mut tmp); } if self.elems.len() > 0 { self.elems.encode(&mut tmp); subsec(8, &mut tmp); } if self.data.len() > 0 { self.data.encode(&mut tmp); subsec(9, &mut tmp); } if self.fields.len() > 0 { self.fields.encode(&mut tmp); subsec(10, &mut tmp); } if self.tags.len() > 0 { self.tags.encode(&mut tmp); subsec(11, &mut tmp); } } } impl Encode for Id<'_> { fn encode(&self, dst: &mut Vec) { assert!(!self.is_gensym()); self.name().encode(dst); } } impl<'a> Encode for TryTable<'a> { fn encode(&self, dst: &mut Vec) { self.block.encode(dst); self.catches.encode(dst); } } impl<'a> Encode for TryTableCatch<'a> { fn encode(&self, dst: &mut Vec) { let flag_byte: u8 = match self.kind { TryTableCatchKind::Catch(..) => 0, TryTableCatchKind::CatchRef(..) => 1, TryTableCatchKind::CatchAll => 2, TryTableCatchKind::CatchAllRef => 3, }; flag_byte.encode(dst); match self.kind { TryTableCatchKind::Catch(tag) | TryTableCatchKind::CatchRef(tag) => { tag.encode(dst); } TryTableCatchKind::CatchAll | TryTableCatchKind::CatchAllRef => {} } self.label.encode(dst); } } impl Encode for V128Const { fn encode(&self, dst: &mut Vec) { dst.extend_from_slice(&self.to_le_bytes()); } } impl Encode for I8x16Shuffle { fn encode(&self, dst: &mut Vec) { dst.extend_from_slice(&self.lanes); } } impl<'a> Encode for SelectTypes<'a> { fn encode(&self, dst: &mut Vec) { match &self.tys { Some(list) => { dst.push(0x1c); list.encode(dst); } None => dst.push(0x1b), } } } impl Encode for Custom<'_> { fn encode(&self, e: &mut Vec) { match self { Custom::Raw(r) => r.encode(e), Custom::Producers(p) => p.encode(e), Custom::Dylink0(p) => p.encode(e), } } } impl Encode for RawCustomSection<'_> { fn encode(&self, e: &mut Vec) { for list in self.data.iter() { e.extend_from_slice(list); } } } impl Encode for Producers<'_> { fn encode(&self, e: &mut Vec) { self.fields.encode(e); } } impl Encode for Dylink0<'_> { fn encode(&self, e: &mut Vec) { for section in self.subsections.iter() { e.push(section.id()); let mut tmp = Vec::new(); section.encode(&mut tmp); tmp.encode(e); } } } impl Encode for Dylink0Subsection<'_> { fn encode(&self, e: &mut Vec) { match self { Dylink0Subsection::MemInfo { memory_size, memory_align, table_size, table_align, } => { memory_size.encode(e); memory_align.encode(e); table_size.encode(e); table_align.encode(e); } Dylink0Subsection::Needed(libs) => libs.encode(e), Dylink0Subsection::ExportInfo(list) => list.encode(e), Dylink0Subsection::ImportInfo(list) => list.encode(e), } } } struct FunctionBranchHints { func_index: u32, hints: Vec, } struct BranchHint { branch_func_offset: u32, branch_hint_value: u32, } impl Encode for FunctionBranchHints { fn encode(&self, e: &mut Vec) { self.func_index.encode(e); self.hints.encode(e); } } impl Encode for BranchHint { fn encode(&self, e: &mut Vec) { self.branch_func_offset.encode(e); 1u32.encode(e); self.branch_hint_value.encode(e); } } impl Encode for Tag<'_> { fn encode(&self, e: &mut Vec) { self.ty.encode(e); match &self.kind { TagKind::Inline() => {} _ => panic!("TagKind should be inline during encoding"), } } } impl Encode for TagType<'_> { fn encode(&self, e: &mut Vec) { match self { TagType::Exception(ty) => { e.push(0x00); ty.encode(e); } } } } impl Encode for StructAccess<'_> { fn encode(&self, e: &mut Vec) { self.r#struct.encode(e); self.field.encode(e); } } impl Encode for ArrayFill<'_> { fn encode(&self, e: &mut Vec) { self.array.encode(e); } } impl Encode for ArrayCopy<'_> { fn encode(&self, e: &mut Vec) { self.dest_array.encode(e); self.src_array.encode(e); } } impl Encode for ArrayInit<'_> { fn encode(&self, e: &mut Vec) { self.array.encode(e); self.segment.encode(e); } } impl Encode for ArrayNewFixed<'_> { fn encode(&self, e: &mut Vec) { self.array.encode(e); self.length.encode(e); } } impl Encode for ArrayNewData<'_> { fn encode(&self, e: &mut Vec) { self.array.encode(e); self.data_idx.encode(e); } } impl Encode for ArrayNewElem<'_> { fn encode(&self, e: &mut Vec) { self.array.encode(e); self.elem_idx.encode(e); } } impl Encode for RefTest<'_> { fn encode(&self, e: &mut Vec) { e.push(0xfb); if self.r#type.nullable { e.push(0x15); } else { e.push(0x14); } self.r#type.heap.encode(e); } } impl Encode for RefCast<'_> { fn encode(&self, e: &mut Vec) { e.push(0xfb); if self.r#type.nullable { e.push(0x17); } else { e.push(0x16); } self.r#type.heap.encode(e); } } fn br_on_cast_flags(from_nullable: bool, to_nullable: bool) -> u8 { let mut flag = 0; if from_nullable { flag |= 1 << 0; } if to_nullable { flag |= 1 << 1; } flag } impl Encode for BrOnCast<'_> { fn encode(&self, e: &mut Vec) { e.push(0xfb); e.push(0x18); e.push(br_on_cast_flags( self.from_type.nullable, self.to_type.nullable, )); self.label.encode(e); self.from_type.heap.encode(e); self.to_type.heap.encode(e); } } impl Encode for BrOnCastFail<'_> { fn encode(&self, e: &mut Vec) { e.push(0xfb); e.push(0x19); e.push(br_on_cast_flags( self.from_type.nullable, self.to_type.nullable, )); self.label.encode(e); self.from_type.heap.encode(e); self.to_type.heap.encode(e); } } wast-217.0.0/src/core/custom.rs000064400000000000000000000275221046102023000143600ustar 00000000000000use crate::parser::{Parse, Parser, Result}; use crate::token::{self, Span}; use crate::{annotation, kw}; /// A custom section within a wasm module. #[derive(Debug)] pub enum Custom<'a> { /// A raw custom section with the manual placement and bytes specified. Raw(RawCustomSection<'a>), /// A producers custom section. Producers(Producers<'a>), /// The `dylink.0` custom section Dylink0(Dylink0<'a>), } impl Custom<'_> { /// Where this custom section is placed. pub fn place(&self) -> CustomPlace { match self { Custom::Raw(s) => s.place, Custom::Producers(_) => CustomPlace::AfterLast, Custom::Dylink0(_) => CustomPlace::BeforeFirst, } } /// The name of this custom section pub fn name(&self) -> &str { match self { Custom::Raw(s) => s.name, Custom::Producers(_) => "producers", Custom::Dylink0(_) => "dylink.0", } } } impl<'a> Parse<'a> for Custom<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { Ok(Custom::Producers(parser.parse()?)) } else if parser.peek::()? { Ok(Custom::Dylink0(parser.parse()?)) } else { Ok(Custom::Raw(parser.parse()?)) } } } /// A wasm custom section within a module. #[derive(Debug)] pub struct RawCustomSection<'a> { /// Where this `@custom` was defined. pub span: Span, /// Name of the custom section. pub name: &'a str, /// Where the custom section is being placed, pub place: CustomPlace, /// Payload of this custom section. pub data: Vec<&'a [u8]>, } /// Possible locations to place a custom section within a module. #[derive(Debug, PartialEq, Copy, Clone)] pub enum CustomPlace { /// This custom section will appear before the first section in the module. BeforeFirst, /// This custom section will be placed just before a known section. Before(CustomPlaceAnchor), /// This custom section will be placed just after a known section. After(CustomPlaceAnchor), /// This custom section will appear after the last section in the module. AfterLast, } /// Known sections that custom sections can be placed relative to. #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[allow(missing_docs)] pub enum CustomPlaceAnchor { Type, Import, Func, Table, Memory, Global, Export, Start, Elem, Code, Data, Tag, } impl<'a> Parse<'a> for RawCustomSection<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let name = parser.parse()?; let place = if parser.peek::()? { parser.parens(|p| p.parse())? } else { CustomPlace::AfterLast }; let mut data = Vec::new(); while !parser.is_empty() { data.push(parser.parse()?); } Ok(RawCustomSection { span, name, place, data, }) } } impl<'a> Parse<'a> for CustomPlace { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); let ctor = if l.peek::()? { parser.parse::()?; if l.peek::()? { parser.parse::()?; return Ok(CustomPlace::BeforeFirst); } CustomPlace::Before as fn(CustomPlaceAnchor) -> _ } else if l.peek::()? { parser.parse::()?; if l.peek::()? { parser.parse::()?; return Ok(CustomPlace::AfterLast); } CustomPlace::After } else { return Err(l.error()); }; Ok(ctor(parser.parse()?)) } } impl<'a> Parse<'a> for CustomPlaceAnchor { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Type); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Import); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Func); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Table); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Memory); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Global); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Export); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Start); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Elem); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Code); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Data); } if parser.peek::()? { parser.parse::()?; return Ok(CustomPlaceAnchor::Tag); } Err(parser.error("expected a valid section name")) } } /// A producers custom section #[allow(missing_docs)] #[derive(Debug)] pub struct Producers<'a> { pub fields: Vec<(&'a str, Vec<(&'a str, &'a str)>)>, } impl<'a> Parse<'a> for Producers<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?.0; let mut languages = Vec::new(); let mut sdks = Vec::new(); let mut processed_by = Vec::new(); while !parser.is_empty() { parser.parens(|parser| { let mut l = parser.lookahead1(); let dst = if l.peek::()? { parser.parse::()?; &mut languages } else if l.peek::()? { parser.parse::()?; &mut sdks } else if l.peek::()? { parser.parse::()?; &mut processed_by } else { return Err(l.error()); }; dst.push((parser.parse()?, parser.parse()?)); Ok(()) })?; } let mut fields = Vec::new(); if !languages.is_empty() { fields.push(("language", languages)); } if !sdks.is_empty() { fields.push(("sdk", sdks)); } if !processed_by.is_empty() { fields.push(("processed-by", processed_by)); } Ok(Producers { fields }) } } /// A `dylink.0` custom section #[allow(missing_docs)] #[derive(Debug)] pub struct Dylink0<'a> { pub subsections: Vec>, } /// Possible subsections of the `dylink.0` custom section #[derive(Debug)] #[allow(missing_docs)] pub enum Dylink0Subsection<'a> { MemInfo { memory_size: u32, memory_align: u32, table_size: u32, table_align: u32, }, Needed(Vec<&'a str>), ExportInfo(Vec<(&'a str, u32)>), ImportInfo(Vec<(&'a str, &'a str, u32)>), } impl<'a> Parse<'a> for Dylink0<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?.0; let mut ret = Dylink0 { subsections: Vec::new(), }; while !parser.is_empty() { parser.parens(|p| ret.parse_next(p))?; } Ok(ret) } } impl<'a> Dylink0<'a> { fn parse_next(&mut self, parser: Parser<'a>) -> Result<()> { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; let mut memory_size = 0; let mut memory_align = 0; let mut table_size = 0; let mut table_align = 0; if parser.peek2::()? { parser.parens(|p| { p.parse::()?; memory_size = p.parse()?; memory_align = p.parse()?; Ok(()) })?; } if parser.peek2::()? { parser.parens(|p| { p.parse::()?; table_size = p.parse()?; table_align = p.parse()?; Ok(()) })?; } self.subsections.push(Dylink0Subsection::MemInfo { memory_size, memory_align, table_size, table_align, }); } else if l.peek::()? { parser.parse::()?; let mut names = Vec::new(); while !parser.is_empty() { names.push(parser.parse()?); } self.subsections.push(Dylink0Subsection::Needed(names)); } else if l.peek::()? { parser.parse::()?; let name = parser.parse()?; let flags = parse_sym_flags(parser)?; match self.subsections.last_mut() { Some(Dylink0Subsection::ExportInfo(list)) => list.push((name, flags)), _ => self .subsections .push(Dylink0Subsection::ExportInfo(vec![(name, flags)])), } } else if l.peek::()? { parser.parse::()?; let module = parser.parse()?; let name = parser.parse()?; let flags = parse_sym_flags(parser)?; match self.subsections.last_mut() { Some(Dylink0Subsection::ImportInfo(list)) => list.push((module, name, flags)), _ => self .subsections .push(Dylink0Subsection::ImportInfo(vec![(module, name, flags)])), } } else { return Err(l.error()); } Ok(()) } } fn parse_sym_flags(parser: Parser<'_>) -> Result { let mut flags = 0; while !parser.is_empty() { let mut l = parser.lookahead1(); if l.peek::()? { flags |= parser.parse::()?; continue; } macro_rules! parse_flags { ($($kw:tt = $val:expr,)*) => {$({ custom_keyword!(flag = $kw); if l.peek::()? { parser.parse::()?; flags |= $val; continue; } })*}; } // N.B.: Keep in sync with `print_dylink0_flags` in `crates/wasmprinter/src/lib.rs`. parse_flags! { "binding-weak" = 1 << 0, "binding-local" = 1 << 1, "visibility-hidden" = 1 << 2, "undefined" = 1 << 4, "exported" = 1 << 5, "explicit-name" = 1 << 6, "no-strip" = 1 << 7, "tls" = 1 << 8, "absolute" = 1 << 9, } return Err(l.error()); } Ok(flags) } mod flag {} impl Dylink0Subsection<'_> { /// Returns the byte id of this subsection used to identify it. pub fn id(&self) -> u8 { use Dylink0Subsection::*; match self { MemInfo { .. } => 1, Needed(..) => 2, ExportInfo(..) => 3, ImportInfo(..) => 4, } } } wast-217.0.0/src/core/export.rs000064400000000000000000000075231046102023000143660ustar 00000000000000use 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: 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 item: Index<'a>, } /// Different kinds of elements that can be exported from a WebAssembly module, /// contained in an [`Export`]. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[allow(missing_docs)] pub enum ExportKind { Func, Table, Memory, Global, Tag, } impl<'a> Parse<'a> for Export<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let name = parser.parse()?; let (kind, item) = parser.parens(|p| Ok((p.parse()?, p.parse()?)))?; Ok(Export { span, name, kind, item, }) } } impl<'a> Parse<'a> for ExportKind { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(ExportKind::Func) } else if l.peek::()? { parser.parse::()?; Ok(ExportKind::Table) } else if l.peek::()? { parser.parse::()?; Ok(ExportKind::Memory) } else if l.peek::()? { parser.parse::()?; Ok(ExportKind::Global) } else if l.peek::()? { parser.parse::()?; Ok(ExportKind::Tag) } else { Err(l.error()) } } } impl Peek for ExportKind { fn peek(cursor: Cursor<'_>) -> Result { Ok(kw::func::peek(cursor)? || kw::table::peek(cursor)? || kw::memory::peek(cursor)? || kw::global::peek(cursor)? || kw::tag::peek(cursor)?) } fn display() -> &'static str { "export kind" } } macro_rules! kw_conversions { ($($kw:ident => $kind:ident)*) => ($( impl From for ExportKind { fn from(_: kw::$kw) -> ExportKind { ExportKind::$kind } } impl Default for kw::$kw { fn default() -> kw::$kw { kw::$kw(Span::from_offset(0)) } } )*); } kw_conversions! { func => Func table => Table global => Global tag => Tag memory => Memory } /// A listing of inline `(export "foo")` statements on a WebAssembly item in /// its textual format. #[derive(Debug, Default)] pub struct InlineExport<'a> { /// The extra names to export an item as, if any. pub names: Vec<&'a str>, } impl<'a> Parse<'a> for InlineExport<'a> { fn parse(parser: Parser<'a>) -> Result { let mut names = Vec::new(); while parser.peek::()? { names.push(parser.parens(|p| { p.parse::()?; p.parse::<&str>() })?); } Ok(InlineExport { names }) } } impl Peek for InlineExport<'_> { fn peek(cursor: Cursor<'_>) -> Result { let cursor = match cursor.lparen()? { Some(cursor) => cursor, None => return Ok(false), }; let cursor = match cursor.keyword()? { Some(("export", cursor)) => cursor, _ => return Ok(false), }; let cursor = match cursor.string()? { Some((_, cursor)) => cursor, None => return Ok(false), }; Ok(cursor.rparen()?.is_some()) } fn display() -> &'static str { "inline export" } } wast-217.0.0/src/core/expr.rs000064400000000000000000002447031046102023000140260ustar 00000000000000use crate::annotation; 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. /// /// This expression type will parse s-expression-folded instructions into a flat /// list of instructions for emission later on. The implicit `end` instruction /// at the end of an expression is not included in the `instrs` field. #[derive(Debug)] #[allow(missing_docs)] pub struct Expression<'a> { /// Instructions in this expression. pub instrs: Box<[Instruction<'a>]>, /// Branch hints, if any, found while parsing instructions. pub branch_hints: Box<[BranchHint]>, /// Optionally parsed spans of all instructions in `instrs`. /// /// This value is `None` as it's disabled by default. This can be enabled /// through the /// [`ParseBuffer::track_instr_spans`](crate::parser::ParseBuffer::track_instr_spans) /// function. /// /// This is not tracked by default due to the memory overhead and limited /// use of this field. pub instr_spans: Option>, } /// A `@metadata.code.branch_hint` in the code, associated with a If or BrIf /// This instruction is a placeholder and won't produce anything. Its purpose /// is to store the offset of the following instruction and check that /// it's followed by `br_if` or `if`. #[derive(Debug)] pub struct BranchHint { /// Index of instructions in `instrs` field of `Expression` that this hint /// applies to. pub instr_index: usize, /// The value of this branch hint pub value: u32, } impl<'a> Parse<'a> for Expression<'a> { fn parse(parser: Parser<'a>) -> Result { let mut exprs = ExpressionParser::new(parser); exprs.parse(parser)?; Ok(Expression { instrs: exprs.raw_instrs.into(), branch_hints: exprs.branch_hints.into(), instr_spans: exprs.spans.map(|s| s.into()), }) } } impl<'a> Expression<'a> { /// Creates an expression from the single `instr` specified. pub fn one(instr: Instruction<'a>) -> Expression<'a> { Expression { instrs: [instr].into(), branch_hints: Box::new([]), instr_spans: None, } } /// Parse an expression formed from a single folded instruction. /// /// Attempts to parse an expression formed from a single folded instruction. /// /// This method will mutate the state of `parser` after attempting to parse /// the expression. If an error happens then it is likely fatal and /// there is no guarantee of how many tokens have been consumed from /// `parser`. /// /// # Errors /// /// This function will return an error if the expression could not be /// parsed. Note that creating an [`crate::Error`] is not exactly a cheap /// operation, so [`crate::Error`] is typically fatal and propagated all the /// way back to the top parse call site. pub fn parse_folded_instruction(parser: Parser<'a>) -> Result { let mut exprs = ExpressionParser::new(parser); exprs.parse_folded_instruction(parser)?; Ok(Expression { instrs: exprs.raw_instrs.into(), branch_hints: exprs.branch_hints.into(), instr_spans: exprs.spans.map(|s| s.into()), }) } } /// Helper struct used to parse an `Expression` with helper methods and such. /// /// The primary purpose of this is to avoid defining expression parsing as a /// call-thread-stack recursive function. Since we're parsing user input that /// runs the risk of blowing the call stack, so we want to be sure to use a heap /// stack structure wherever possible. struct ExpressionParser<'a> { /// The flat list of instructions that we've parsed so far, and will /// eventually become the final `Expression`. /// /// Appended to with `push_instr` to ensure that this is the same length of /// `spans` if `spans` is used. raw_instrs: Vec>, /// Descriptor of all our nested s-expr blocks. This only happens when /// instructions themselves are nested. stack: Vec>, /// Related to the branch hints proposal. /// Will be used later to collect the offsets in the final binary. /// <(index of branch instructions, BranchHintAnnotation)> branch_hints: Vec, /// Storage for all span information in `raw_instrs`. Optionally disabled to /// reduce memory consumption of parsing expressions. spans: Option>, } enum Paren { None, Left, Right(Span), } /// A "kind" of nested block that we can be parsing inside of. enum Level<'a> { /// This is a normal `block` or `loop` or similar, where the instruction /// payload here is pushed when the block is exited. EndWith(Instruction<'a>, Option), /// This is a pretty special variant which means that we're parsing an `if` /// statement, and the state of the `if` parsing is tracked internally in /// the payload. If(If<'a>), /// This means we're either parsing inside of `(then ...)` or `(else ...)` /// which don't correspond to terminating instructions, we're just in a /// nested block. IfArm, /// This means we are finishing the parsing of a branch hint annotation. BranchHint, } /// Possible states of "what is currently being parsed?" in an `if` expression. enum If<'a> { /// Only the `if` instruction has been parsed, next thing to parse is the /// clause, if any, of the `if` instruction. /// /// This parse ends when `(then ...)` is encountered. Clause(Instruction<'a>, Span), /// Currently parsing the `then` block, and afterwards a closing paren is /// required or an `(else ...)` expression. Then, /// Parsing the `else` expression, nothing can come after. Else, } impl<'a> ExpressionParser<'a> { fn new(parser: Parser<'a>) -> ExpressionParser<'a> { ExpressionParser { raw_instrs: Vec::new(), stack: Vec::new(), branch_hints: Vec::new(), spans: if parser.track_instr_spans() { Some(Vec::new()) } else { None }, } } fn parse(&mut self, parser: Parser<'a>) -> Result<()> { // Here we parse instructions in a loop, and we do not recursively // invoke this parse function to avoid blowing the stack on // deeply-recursive parses. // // Our loop generally only finishes once there's no more input left int // the `parser`. If there's some unclosed delimiters though (on our // `stack`), then we also keep parsing to generate error messages if // there's no input left. while !parser.is_empty() || !self.stack.is_empty() { // As a small ease-of-life adjustment here, if we're parsing inside // of an `if block then we require that all sub-components are // s-expressions surrounded by `(` and `)`, so verify that here. if let Some(Level::If(_)) = self.stack.last() { if !parser.is_empty() && !parser.peek::()? { return Err(parser.error("expected `(`")); } } match self.paren(parser)? { // No parenthesis seen? Then we just parse the next instruction // and move on. Paren::None => { let span = parser.cur_span(); self.push_instr(parser.parse()?, span); } // If we see a left-parenthesis then things are a little // special. We handle block-like instructions specially // (`block`, `loop`, and `if`), and otherwise all other // instructions simply get appended once we reach the end of the // s-expression. // // In all cases here we push something onto the `stack` to get // popped when the `)` character is seen. Paren::Left => { // First up is handling `if` parsing, which is funky in a // whole bunch of ways. See the method internally for more // information. if self.handle_if_lparen(parser)? { continue; } // Handle the case of a branch hint annotation if parser.peek::()? { self.parse_branch_hint(parser)?; self.stack.push(Level::BranchHint); continue; } let span = parser.cur_span(); match parser.parse()? { // If block/loop show up then we just need to be sure to // push an `end` instruction whenever the `)` token is // seen i @ Instruction::Block(_) | i @ Instruction::Loop(_) | i @ Instruction::TryTable(_) => { self.push_instr(i, span); self.stack .push(Level::EndWith(Instruction::End(None), None)); } // Parsing an `if` instruction is super tricky, so we // push an `If` scope and we let all our scope-based // parsing handle the remaining items. i @ Instruction::If(_) => { self.stack.push(Level::If(If::Clause(i, span))); } // Anything else means that we're parsing a nested form // such as `(i32.add ...)` which means that the // instruction we parsed will be coming at the end. other => self.stack.push(Level::EndWith(other, Some(span))), } } // If we registered a `)` token as being seen, then we're // guaranteed there's an item in the `stack` stack for us to // pop. We peel that off and take a look at what it says to do. Paren::Right(span) => match self.stack.pop().unwrap() { Level::EndWith(i, s) => self.push_instr(i, s.unwrap_or(span)), Level::IfArm => {} Level::BranchHint => {} // If an `if` statement hasn't parsed the clause or `then` // block, then that's an error because there weren't enough // items in the `if` statement. Otherwise we're just careful // to terminate with an `end` instruction. Level::If(If::Clause(..)) => { return Err(parser.error("previous `if` had no `then`")); } Level::If(_) => { self.push_instr(Instruction::End(None), span); } }, } } Ok(()) } fn parse_folded_instruction(&mut self, parser: Parser<'a>) -> Result<()> { let mut done = false; while !done { match self.paren(parser)? { Paren::Left => { let span = parser.cur_span(); self.stack.push(Level::EndWith(parser.parse()?, Some(span))); } Paren::Right(span) => { let (top_instr, span) = match self.stack.pop().unwrap() { Level::EndWith(i, s) => (i, s.unwrap_or(span)), _ => panic!("unknown level type"), }; self.push_instr(top_instr, span); if self.stack.is_empty() { done = true; } } Paren::None => { return Err(parser.error("expected to continue a folded instruction")) } } } Ok(()) } /// Parses either `(`, `)`, or nothing. fn paren(&self, parser: Parser<'a>) -> Result { parser.step(|cursor| { Ok(match cursor.lparen()? { Some(rest) => (Paren::Left, rest), None if self.stack.is_empty() => (Paren::None, cursor), None => match cursor.rparen()? { Some(rest) => (Paren::Right(cursor.cur_span()), rest), None => (Paren::None, cursor), }, }) }) } /// State transitions with parsing an `if` statement. /// /// The syntactical form of an `if` statement looks like: /// /// ```wat /// (if ($clause)... (then $then) (else $else)) /// ``` /// /// THis method is called after a `(` is parsed within the `(if ...` block. /// This determines what to do next. /// /// Returns `true` if the rest of the arm above should be skipped, or /// `false` if we should parse the next item as an instruction (because we /// didn't handle the lparen here). fn handle_if_lparen(&mut self, parser: Parser<'a>) -> Result { // Only execute the code below if there's an `If` listed last. let i = match self.stack.last_mut() { Some(Level::If(i)) => i, _ => return Ok(false), }; match i { // If the clause is still being parsed then interpret this `(` as a // folded instruction unless it starts with `then`, in which case // this transitions to the `Then` state and a new level has been // reached. If::Clause(if_instr, if_instr_span) => { if !parser.peek::()? { return Ok(false); } parser.parse::()?; let instr = mem::replace(if_instr, Instruction::End(None)); let span = *if_instr_span; *i = If::Then; self.push_instr(instr, span); self.stack.push(Level::IfArm); Ok(true) } // Previously we were parsing the `(then ...)` clause so this next // `(` must be followed by `else`. If::Then => { let span = parser.parse::()?.0; *i = If::Else; self.push_instr(Instruction::Else(None), span); self.stack.push(Level::IfArm); Ok(true) } // If after a `(else ...` clause is parsed there's another `(` then // that's not syntactically allowed. If::Else => Err(parser.error("unexpected token: too many payloads inside of `(if)`")), } } fn parse_branch_hint(&mut self, parser: Parser<'a>) -> Result<()> { parser.parse::()?; let hint = parser.parse::()?; let value = match hint.as_bytes() { [0] => 0, [1] => 1, _ => return Err(parser.error("invalid value for branch hint")), }; self.branch_hints.push(BranchHint { instr_index: self.raw_instrs.len(), value, }); Ok(()) } fn push_instr(&mut self, instr: Instruction<'a>, span: Span) { self.raw_instrs.push(instr); if let Some(spans) = &mut self.spans { spans.push(span); } } } // TODO: document this obscenity macro_rules! instructions { (pub enum Instruction<'a> { $( $(#[$doc:meta])* $name:ident $(($($arg:tt)*))? : [$($binary:tt)*] : $instr:tt $( | $deprecated:tt )?, )* }) => ( /// A listing of all WebAssembly instructions that can be in a module /// that this crate currently parses. #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum Instruction<'a> { $( $(#[$doc])* $name $(( instructions!(@ty $($arg)*) ))?, )* } #[allow(non_snake_case)] impl<'a> Parse<'a> for Instruction<'a> { fn parse(parser: Parser<'a>) -> Result { $( fn $name<'a>(_parser: Parser<'a>) -> Result> { Ok(Instruction::$name $(( instructions!(@parse _parser $($arg)*)? ))?) } )* let parse_remainder = parser.step(|c| { let (kw, rest) = match c.keyword() ?{ Some(pair) => pair, None => return Err(c.error("expected an instruction")), }; match kw { $($instr $( | $deprecated )?=> Ok(($name as fn(_) -> _, rest)),)* _ => return Err(c.error("unknown operator or unexpected token")), } })?; parse_remainder(parser) } } impl Encode for Instruction<'_> { #[allow(non_snake_case)] fn encode(&self, v: &mut Vec) { match self { $( Instruction::$name $((instructions!(@first x $($arg)*)))? => { fn encode<'a>($(arg: &instructions!(@ty $($arg)*),)? v: &mut Vec) { instructions!(@encode v $($binary)*); $(::encode(arg, v);)? } encode($( instructions!(@first x $($arg)*), )? v) } )* } } } impl<'a> Instruction<'a> { /// Returns the associated [`MemArg`] if one is available for this /// instruction. #[allow(unused_variables, non_snake_case)] pub fn memarg_mut(&mut self) -> Option<&mut MemArg<'a>> { match self { $( Instruction::$name $((instructions!(@memarg_binding a $($arg)*)))? => { instructions!(@get_memarg a $($($arg)*)?) } )* } } } ); (@ty MemArg<$amt:tt>) => (MemArg<'a>); (@ty LoadOrStoreLane<$amt:tt>) => (LoadOrStoreLane<'a>); (@ty $other:ty) => ($other); (@first $first:ident $($t:tt)*) => ($first); (@parse $parser:ident MemArg<$amt:tt>) => (MemArg::parse($parser, $amt)); (@parse $parser:ident MemArg) => (compile_error!("must specify `MemArg` default")); (@parse $parser:ident LoadOrStoreLane<$amt:tt>) => (LoadOrStoreLane::parse($parser, $amt)); (@parse $parser:ident LoadOrStoreLane) => (compile_error!("must specify `LoadOrStoreLane` default")); (@parse $parser:ident $other:ty) => ($parser.parse::<$other>()); // simd opcodes prefixed with `0xfd` get a varuint32 encoding for their payload (@encode $dst:ident 0xfd, $simd:tt) => ({ $dst.push(0xfd); ::encode(&$simd, $dst); }); (@encode $dst:ident $($bytes:tt)*) => ($dst.extend_from_slice(&[$($bytes)*]);); (@get_memarg $name:ident MemArg<$amt:tt>) => (Some($name)); (@get_memarg $name:ident LoadOrStoreLane<$amt:tt>) => (Some(&mut $name.memarg)); (@get_memarg $($other:tt)*) => (None); (@memarg_binding $name:ident MemArg<$amt:tt>) => ($name); (@memarg_binding $name:ident LoadOrStoreLane<$amt:tt>) => ($name); (@memarg_binding $name:ident $other:ty) => (_); } instructions! { pub enum Instruction<'a> { Block(Box>) : [0x02] : "block", If(Box>) : [0x04] : "if", Else(Option>) : [0x05] : "else", Loop(Box>) : [0x03] : "loop", End(Option>) : [0x0b] : "end", Unreachable : [0x00] : "unreachable", Nop : [0x01] : "nop", Br(Index<'a>) : [0x0c] : "br", BrIf(Index<'a>) : [0x0d] : "br_if", BrTable(BrTableIndices<'a>) : [0x0e] : "br_table", Return : [0x0f] : "return", Call(Index<'a>) : [0x10] : "call", CallIndirect(Box>) : [0x11] : "call_indirect", // tail-call proposal ReturnCall(Index<'a>) : [0x12] : "return_call", ReturnCallIndirect(Box>) : [0x13] : "return_call_indirect", // function-references proposal CallRef(Index<'a>) : [0x14] : "call_ref", ReturnCallRef(Index<'a>) : [0x15] : "return_call_ref", Drop : [0x1a] : "drop", Select(SelectTypes<'a>) : [] : "select", LocalGet(Index<'a>) : [0x20] : "local.get", LocalSet(Index<'a>) : [0x21] : "local.set", LocalTee(Index<'a>) : [0x22] : "local.tee", GlobalGet(Index<'a>) : [0x23] : "global.get", GlobalSet(Index<'a>) : [0x24] : "global.set", TableGet(TableArg<'a>) : [0x25] : "table.get", TableSet(TableArg<'a>) : [0x26] : "table.set", I32Load(MemArg<4>) : [0x28] : "i32.load", I64Load(MemArg<8>) : [0x29] : "i64.load", F32Load(MemArg<4>) : [0x2a] : "f32.load", F64Load(MemArg<8>) : [0x2b] : "f64.load", I32Load8s(MemArg<1>) : [0x2c] : "i32.load8_s", I32Load8u(MemArg<1>) : [0x2d] : "i32.load8_u", I32Load16s(MemArg<2>) : [0x2e] : "i32.load16_s", I32Load16u(MemArg<2>) : [0x2f] : "i32.load16_u", I64Load8s(MemArg<1>) : [0x30] : "i64.load8_s", I64Load8u(MemArg<1>) : [0x31] : "i64.load8_u", I64Load16s(MemArg<2>) : [0x32] : "i64.load16_s", I64Load16u(MemArg<2>) : [0x33] : "i64.load16_u", I64Load32s(MemArg<4>) : [0x34] : "i64.load32_s", I64Load32u(MemArg<4>) : [0x35] : "i64.load32_u", I32Store(MemArg<4>) : [0x36] : "i32.store", I64Store(MemArg<8>) : [0x37] : "i64.store", F32Store(MemArg<4>) : [0x38] : "f32.store", F64Store(MemArg<8>) : [0x39] : "f64.store", I32Store8(MemArg<1>) : [0x3a] : "i32.store8", I32Store16(MemArg<2>) : [0x3b] : "i32.store16", I64Store8(MemArg<1>) : [0x3c] : "i64.store8", I64Store16(MemArg<2>) : [0x3d] : "i64.store16", I64Store32(MemArg<4>) : [0x3e] : "i64.store32", // Lots of bulk memory proposal here as well MemorySize(MemoryArg<'a>) : [0x3f] : "memory.size", MemoryGrow(MemoryArg<'a>) : [0x40] : "memory.grow", MemoryInit(MemoryInit<'a>) : [0xfc, 0x08] : "memory.init", MemoryCopy(MemoryCopy<'a>) : [0xfc, 0x0a] : "memory.copy", MemoryFill(MemoryArg<'a>) : [0xfc, 0x0b] : "memory.fill", MemoryDiscard(MemoryArg<'a>) : [0xfc, 0x12] : "memory.discard", 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", TableSize(TableArg<'a>) : [0xfc, 0x10] : "table.size", TableGrow(TableArg<'a>) : [0xfc, 0x0f] : "table.grow", RefNull(HeapType<'a>) : [0xd0] : "ref.null", RefIsNull : [0xd1] : "ref.is_null", RefFunc(Index<'a>) : [0xd2] : "ref.func", // function-references proposal RefAsNonNull : [0xd4] : "ref.as_non_null", BrOnNull(Index<'a>) : [0xd5] : "br_on_null", BrOnNonNull(Index<'a>) : [0xd6] : "br_on_non_null", // gc proposal: eqref RefEq : [0xd3] : "ref.eq", // gc proposal: struct StructNew(Index<'a>) : [0xfb, 0x00] : "struct.new", StructNewDefault(Index<'a>) : [0xfb, 0x01] : "struct.new_default", StructGet(StructAccess<'a>) : [0xfb, 0x02] : "struct.get", StructGetS(StructAccess<'a>) : [0xfb, 0x03] : "struct.get_s", StructGetU(StructAccess<'a>) : [0xfb, 0x04] : "struct.get_u", StructSet(StructAccess<'a>) : [0xfb, 0x05] : "struct.set", // gc proposal: array ArrayNew(Index<'a>) : [0xfb, 0x06] : "array.new", ArrayNewDefault(Index<'a>) : [0xfb, 0x07] : "array.new_default", ArrayNewFixed(ArrayNewFixed<'a>) : [0xfb, 0x08] : "array.new_fixed", ArrayNewData(ArrayNewData<'a>) : [0xfb, 0x09] : "array.new_data", ArrayNewElem(ArrayNewElem<'a>) : [0xfb, 0x0a] : "array.new_elem", ArrayGet(Index<'a>) : [0xfb, 0x0b] : "array.get", ArrayGetS(Index<'a>) : [0xfb, 0x0c] : "array.get_s", ArrayGetU(Index<'a>) : [0xfb, 0x0d] : "array.get_u", ArraySet(Index<'a>) : [0xfb, 0x0e] : "array.set", ArrayLen : [0xfb, 0x0f] : "array.len", ArrayFill(ArrayFill<'a>) : [0xfb, 0x10] : "array.fill", ArrayCopy(ArrayCopy<'a>) : [0xfb, 0x11] : "array.copy", ArrayInitData(ArrayInit<'a>) : [0xfb, 0x12] : "array.init_data", ArrayInitElem(ArrayInit<'a>) : [0xfb, 0x13] : "array.init_elem", // gc proposal, i31 RefI31 : [0xfb, 0x1c] : "ref.i31", I31GetS : [0xfb, 0x1d] : "i31.get_s", I31GetU : [0xfb, 0x1e] : "i31.get_u", // gc proposal, concrete casting RefTest(RefTest<'a>) : [] : "ref.test", RefCast(RefCast<'a>) : [] : "ref.cast", BrOnCast(Box>) : [] : "br_on_cast", BrOnCastFail(Box>) : [] : "br_on_cast_fail", // gc proposal extern/any coercion operations AnyConvertExtern : [0xfb, 0x1a] : "any.convert_extern", ExternConvertAny : [0xfb, 0x1b] : "extern.convert_any", I32Const(i32) : [0x41] : "i32.const", I64Const(i64) : [0x42] : "i64.const", F32Const(F32) : [0x43] : "f32.const", F64Const(F64) : [0x44] : "f64.const", I32Clz : [0x67] : "i32.clz", I32Ctz : [0x68] : "i32.ctz", I32Popcnt : [0x69] : "i32.popcnt", I32Add : [0x6a] : "i32.add", I32Sub : [0x6b] : "i32.sub", I32Mul : [0x6c] : "i32.mul", I32DivS : [0x6d] : "i32.div_s", I32DivU : [0x6e] : "i32.div_u", I32RemS : [0x6f] : "i32.rem_s", I32RemU : [0x70] : "i32.rem_u", I32And : [0x71] : "i32.and", I32Or : [0x72] : "i32.or", I32Xor : [0x73] : "i32.xor", I32Shl : [0x74] : "i32.shl", I32ShrS : [0x75] : "i32.shr_s", I32ShrU : [0x76] : "i32.shr_u", I32Rotl : [0x77] : "i32.rotl", I32Rotr : [0x78] : "i32.rotr", I64Clz : [0x79] : "i64.clz", I64Ctz : [0x7a] : "i64.ctz", I64Popcnt : [0x7b] : "i64.popcnt", I64Add : [0x7c] : "i64.add", I64Sub : [0x7d] : "i64.sub", I64Mul : [0x7e] : "i64.mul", I64DivS : [0x7f] : "i64.div_s", I64DivU : [0x80] : "i64.div_u", I64RemS : [0x81] : "i64.rem_s", I64RemU : [0x82] : "i64.rem_u", I64And : [0x83] : "i64.and", I64Or : [0x84] : "i64.or", I64Xor : [0x85] : "i64.xor", I64Shl : [0x86] : "i64.shl", I64ShrS : [0x87] : "i64.shr_s", I64ShrU : [0x88] : "i64.shr_u", I64Rotl : [0x89] : "i64.rotl", I64Rotr : [0x8a] : "i64.rotr", F32Abs : [0x8b] : "f32.abs", F32Neg : [0x8c] : "f32.neg", F32Ceil : [0x8d] : "f32.ceil", F32Floor : [0x8e] : "f32.floor", F32Trunc : [0x8f] : "f32.trunc", F32Nearest : [0x90] : "f32.nearest", F32Sqrt : [0x91] : "f32.sqrt", F32Add : [0x92] : "f32.add", F32Sub : [0x93] : "f32.sub", F32Mul : [0x94] : "f32.mul", F32Div : [0x95] : "f32.div", F32Min : [0x96] : "f32.min", F32Max : [0x97] : "f32.max", F32Copysign : [0x98] : "f32.copysign", F64Abs : [0x99] : "f64.abs", F64Neg : [0x9a] : "f64.neg", F64Ceil : [0x9b] : "f64.ceil", F64Floor : [0x9c] : "f64.floor", F64Trunc : [0x9d] : "f64.trunc", F64Nearest : [0x9e] : "f64.nearest", F64Sqrt : [0x9f] : "f64.sqrt", F64Add : [0xa0] : "f64.add", F64Sub : [0xa1] : "f64.sub", F64Mul : [0xa2] : "f64.mul", F64Div : [0xa3] : "f64.div", F64Min : [0xa4] : "f64.min", F64Max : [0xa5] : "f64.max", F64Copysign : [0xa6] : "f64.copysign", I32Eqz : [0x45] : "i32.eqz", I32Eq : [0x46] : "i32.eq", I32Ne : [0x47] : "i32.ne", I32LtS : [0x48] : "i32.lt_s", I32LtU : [0x49] : "i32.lt_u", I32GtS : [0x4a] : "i32.gt_s", I32GtU : [0x4b] : "i32.gt_u", I32LeS : [0x4c] : "i32.le_s", I32LeU : [0x4d] : "i32.le_u", I32GeS : [0x4e] : "i32.ge_s", I32GeU : [0x4f] : "i32.ge_u", I64Eqz : [0x50] : "i64.eqz", I64Eq : [0x51] : "i64.eq", I64Ne : [0x52] : "i64.ne", I64LtS : [0x53] : "i64.lt_s", I64LtU : [0x54] : "i64.lt_u", I64GtS : [0x55] : "i64.gt_s", I64GtU : [0x56] : "i64.gt_u", I64LeS : [0x57] : "i64.le_s", I64LeU : [0x58] : "i64.le_u", I64GeS : [0x59] : "i64.ge_s", I64GeU : [0x5a] : "i64.ge_u", F32Eq : [0x5b] : "f32.eq", F32Ne : [0x5c] : "f32.ne", F32Lt : [0x5d] : "f32.lt", F32Gt : [0x5e] : "f32.gt", F32Le : [0x5f] : "f32.le", F32Ge : [0x60] : "f32.ge", F64Eq : [0x61] : "f64.eq", F64Ne : [0x62] : "f64.ne", F64Lt : [0x63] : "f64.lt", F64Gt : [0x64] : "f64.gt", F64Le : [0x65] : "f64.le", F64Ge : [0x66] : "f64.ge", I32WrapI64 : [0xa7] : "i32.wrap_i64", I32TruncF32S : [0xa8] : "i32.trunc_f32_s", I32TruncF32U : [0xa9] : "i32.trunc_f32_u", I32TruncF64S : [0xaa] : "i32.trunc_f64_s", I32TruncF64U : [0xab] : "i32.trunc_f64_u", I64ExtendI32S : [0xac] : "i64.extend_i32_s", I64ExtendI32U : [0xad] : "i64.extend_i32_u", I64TruncF32S : [0xae] : "i64.trunc_f32_s", I64TruncF32U : [0xaf] : "i64.trunc_f32_u", I64TruncF64S : [0xb0] : "i64.trunc_f64_s", I64TruncF64U : [0xb1] : "i64.trunc_f64_u", F32ConvertI32S : [0xb2] : "f32.convert_i32_s", F32ConvertI32U : [0xb3] : "f32.convert_i32_u", F32ConvertI64S : [0xb4] : "f32.convert_i64_s", F32ConvertI64U : [0xb5] : "f32.convert_i64_u", F32DemoteF64 : [0xb6] : "f32.demote_f64", F64ConvertI32S : [0xb7] : "f64.convert_i32_s", F64ConvertI32U : [0xb8] : "f64.convert_i32_u", F64ConvertI64S : [0xb9] : "f64.convert_i64_s", F64ConvertI64U : [0xba] : "f64.convert_i64_u", F64PromoteF32 : [0xbb] : "f64.promote_f32", I32ReinterpretF32 : [0xbc] : "i32.reinterpret_f32", I64ReinterpretF64 : [0xbd] : "i64.reinterpret_f64", F32ReinterpretI32 : [0xbe] : "f32.reinterpret_i32", F64ReinterpretI64 : [0xbf] : "f64.reinterpret_i64", // non-trapping float to int I32TruncSatF32S : [0xfc, 0x00] : "i32.trunc_sat_f32_s", I32TruncSatF32U : [0xfc, 0x01] : "i32.trunc_sat_f32_u", I32TruncSatF64S : [0xfc, 0x02] : "i32.trunc_sat_f64_s", I32TruncSatF64U : [0xfc, 0x03] : "i32.trunc_sat_f64_u", I64TruncSatF32S : [0xfc, 0x04] : "i64.trunc_sat_f32_s", I64TruncSatF32U : [0xfc, 0x05] : "i64.trunc_sat_f32_u", I64TruncSatF64S : [0xfc, 0x06] : "i64.trunc_sat_f64_s", I64TruncSatF64U : [0xfc, 0x07] : "i64.trunc_sat_f64_u", // sign extension proposal I32Extend8S : [0xc0] : "i32.extend8_s", I32Extend16S : [0xc1] : "i32.extend16_s", I64Extend8S : [0xc2] : "i64.extend8_s", I64Extend16S : [0xc3] : "i64.extend16_s", I64Extend32S : [0xc4] : "i64.extend32_s", // atomics proposal MemoryAtomicNotify(MemArg<4>) : [0xfe, 0x00] : "memory.atomic.notify", MemoryAtomicWait32(MemArg<4>) : [0xfe, 0x01] : "memory.atomic.wait32", MemoryAtomicWait64(MemArg<8>) : [0xfe, 0x02] : "memory.atomic.wait64", AtomicFence : [0xfe, 0x03, 0x00] : "atomic.fence", I32AtomicLoad(MemArg<4>) : [0xfe, 0x10] : "i32.atomic.load", I64AtomicLoad(MemArg<8>) : [0xfe, 0x11] : "i64.atomic.load", I32AtomicLoad8u(MemArg<1>) : [0xfe, 0x12] : "i32.atomic.load8_u", I32AtomicLoad16u(MemArg<2>) : [0xfe, 0x13] : "i32.atomic.load16_u", I64AtomicLoad8u(MemArg<1>) : [0xfe, 0x14] : "i64.atomic.load8_u", I64AtomicLoad16u(MemArg<2>) : [0xfe, 0x15] : "i64.atomic.load16_u", I64AtomicLoad32u(MemArg<4>) : [0xfe, 0x16] : "i64.atomic.load32_u", I32AtomicStore(MemArg<4>) : [0xfe, 0x17] : "i32.atomic.store", I64AtomicStore(MemArg<8>) : [0xfe, 0x18] : "i64.atomic.store", I32AtomicStore8(MemArg<1>) : [0xfe, 0x19] : "i32.atomic.store8", I32AtomicStore16(MemArg<2>) : [0xfe, 0x1a] : "i32.atomic.store16", I64AtomicStore8(MemArg<1>) : [0xfe, 0x1b] : "i64.atomic.store8", I64AtomicStore16(MemArg<2>) : [0xfe, 0x1c] : "i64.atomic.store16", I64AtomicStore32(MemArg<4>) : [0xfe, 0x1d] : "i64.atomic.store32", I32AtomicRmwAdd(MemArg<4>) : [0xfe, 0x1e] : "i32.atomic.rmw.add", I64AtomicRmwAdd(MemArg<8>) : [0xfe, 0x1f] : "i64.atomic.rmw.add", I32AtomicRmw8AddU(MemArg<1>) : [0xfe, 0x20] : "i32.atomic.rmw8.add_u", I32AtomicRmw16AddU(MemArg<2>) : [0xfe, 0x21] : "i32.atomic.rmw16.add_u", I64AtomicRmw8AddU(MemArg<1>) : [0xfe, 0x22] : "i64.atomic.rmw8.add_u", I64AtomicRmw16AddU(MemArg<2>) : [0xfe, 0x23] : "i64.atomic.rmw16.add_u", I64AtomicRmw32AddU(MemArg<4>) : [0xfe, 0x24] : "i64.atomic.rmw32.add_u", I32AtomicRmwSub(MemArg<4>) : [0xfe, 0x25] : "i32.atomic.rmw.sub", I64AtomicRmwSub(MemArg<8>) : [0xfe, 0x26] : "i64.atomic.rmw.sub", I32AtomicRmw8SubU(MemArg<1>) : [0xfe, 0x27] : "i32.atomic.rmw8.sub_u", I32AtomicRmw16SubU(MemArg<2>) : [0xfe, 0x28] : "i32.atomic.rmw16.sub_u", I64AtomicRmw8SubU(MemArg<1>) : [0xfe, 0x29] : "i64.atomic.rmw8.sub_u", I64AtomicRmw16SubU(MemArg<2>) : [0xfe, 0x2a] : "i64.atomic.rmw16.sub_u", I64AtomicRmw32SubU(MemArg<4>) : [0xfe, 0x2b] : "i64.atomic.rmw32.sub_u", I32AtomicRmwAnd(MemArg<4>) : [0xfe, 0x2c] : "i32.atomic.rmw.and", I64AtomicRmwAnd(MemArg<8>) : [0xfe, 0x2d] : "i64.atomic.rmw.and", I32AtomicRmw8AndU(MemArg<1>) : [0xfe, 0x2e] : "i32.atomic.rmw8.and_u", I32AtomicRmw16AndU(MemArg<2>) : [0xfe, 0x2f] : "i32.atomic.rmw16.and_u", I64AtomicRmw8AndU(MemArg<1>) : [0xfe, 0x30] : "i64.atomic.rmw8.and_u", I64AtomicRmw16AndU(MemArg<2>) : [0xfe, 0x31] : "i64.atomic.rmw16.and_u", I64AtomicRmw32AndU(MemArg<4>) : [0xfe, 0x32] : "i64.atomic.rmw32.and_u", I32AtomicRmwOr(MemArg<4>) : [0xfe, 0x33] : "i32.atomic.rmw.or", I64AtomicRmwOr(MemArg<8>) : [0xfe, 0x34] : "i64.atomic.rmw.or", I32AtomicRmw8OrU(MemArg<1>) : [0xfe, 0x35] : "i32.atomic.rmw8.or_u", I32AtomicRmw16OrU(MemArg<2>) : [0xfe, 0x36] : "i32.atomic.rmw16.or_u", I64AtomicRmw8OrU(MemArg<1>) : [0xfe, 0x37] : "i64.atomic.rmw8.or_u", I64AtomicRmw16OrU(MemArg<2>) : [0xfe, 0x38] : "i64.atomic.rmw16.or_u", I64AtomicRmw32OrU(MemArg<4>) : [0xfe, 0x39] : "i64.atomic.rmw32.or_u", I32AtomicRmwXor(MemArg<4>) : [0xfe, 0x3a] : "i32.atomic.rmw.xor", I64AtomicRmwXor(MemArg<8>) : [0xfe, 0x3b] : "i64.atomic.rmw.xor", I32AtomicRmw8XorU(MemArg<1>) : [0xfe, 0x3c] : "i32.atomic.rmw8.xor_u", I32AtomicRmw16XorU(MemArg<2>) : [0xfe, 0x3d] : "i32.atomic.rmw16.xor_u", I64AtomicRmw8XorU(MemArg<1>) : [0xfe, 0x3e] : "i64.atomic.rmw8.xor_u", I64AtomicRmw16XorU(MemArg<2>) : [0xfe, 0x3f] : "i64.atomic.rmw16.xor_u", I64AtomicRmw32XorU(MemArg<4>) : [0xfe, 0x40] : "i64.atomic.rmw32.xor_u", I32AtomicRmwXchg(MemArg<4>) : [0xfe, 0x41] : "i32.atomic.rmw.xchg", I64AtomicRmwXchg(MemArg<8>) : [0xfe, 0x42] : "i64.atomic.rmw.xchg", I32AtomicRmw8XchgU(MemArg<1>) : [0xfe, 0x43] : "i32.atomic.rmw8.xchg_u", I32AtomicRmw16XchgU(MemArg<2>) : [0xfe, 0x44] : "i32.atomic.rmw16.xchg_u", I64AtomicRmw8XchgU(MemArg<1>) : [0xfe, 0x45] : "i64.atomic.rmw8.xchg_u", I64AtomicRmw16XchgU(MemArg<2>) : [0xfe, 0x46] : "i64.atomic.rmw16.xchg_u", I64AtomicRmw32XchgU(MemArg<4>) : [0xfe, 0x47] : "i64.atomic.rmw32.xchg_u", I32AtomicRmwCmpxchg(MemArg<4>) : [0xfe, 0x48] : "i32.atomic.rmw.cmpxchg", I64AtomicRmwCmpxchg(MemArg<8>) : [0xfe, 0x49] : "i64.atomic.rmw.cmpxchg", I32AtomicRmw8CmpxchgU(MemArg<1>) : [0xfe, 0x4a] : "i32.atomic.rmw8.cmpxchg_u", I32AtomicRmw16CmpxchgU(MemArg<2>) : [0xfe, 0x4b] : "i32.atomic.rmw16.cmpxchg_u", I64AtomicRmw8CmpxchgU(MemArg<1>) : [0xfe, 0x4c] : "i64.atomic.rmw8.cmpxchg_u", I64AtomicRmw16CmpxchgU(MemArg<2>) : [0xfe, 0x4d] : "i64.atomic.rmw16.cmpxchg_u", I64AtomicRmw32CmpxchgU(MemArg<4>) : [0xfe, 0x4e] : "i64.atomic.rmw32.cmpxchg_u", // proposal: shared-everything-threads GlobalAtomicGet(Ordered>) : [0xfe, 0x4f] : "global.atomic.get", GlobalAtomicSet(Ordered>) : [0xfe, 0x50] : "global.atomic.set", GlobalAtomicRmwAdd(Ordered>) : [0xfe, 0x51] : "global.atomic.rmw.add", GlobalAtomicRmwSub(Ordered>) : [0xfe, 0x52] : "global.atomic.rmw.sub", GlobalAtomicRmwAnd(Ordered>) : [0xfe, 0x53] : "global.atomic.rmw.and", GlobalAtomicRmwOr(Ordered>) : [0xfe, 0x54] : "global.atomic.rmw.or", GlobalAtomicRmwXor(Ordered>) : [0xfe, 0x55] : "global.atomic.rmw.xor", GlobalAtomicRmwXchg(Ordered>) : [0xfe, 0x56] : "global.atomic.rmw.xchg", GlobalAtomicRmwCmpxchg(Ordered>) : [0xfe, 0x57] : "global.atomic.rmw.cmpxchg", TableAtomicGet(Ordered>) : [0xfe, 0x58] : "table.atomic.get", TableAtomicSet(Ordered>) : [0xfe, 0x59] : "table.atomic.set", TableAtomicRmwXchg(Ordered>) : [0xfe, 0x5a] : "table.atomic.rmw.xchg", TableAtomicRmwCmpxchg(Ordered>) : [0xfe, 0x5b] : "table.atomic.rmw.cmpxchg", StructAtomicGet(Ordered>) : [0xfe, 0x5c] : "struct.atomic.get", StructAtomicGetS(Ordered>) : [0xfe, 0x5d] : "struct.atomic.get_s", StructAtomicGetU(Ordered>) : [0xfe, 0x5e] : "struct.atomic.get_u", StructAtomicSet(Ordered>) : [0xfe, 0x5f] : "struct.atomic.set", StructAtomicRmwAdd(Ordered>) : [0xfe, 0x60] : "struct.atomic.rmw.add", StructAtomicRmwSub(Ordered>) : [0xfe, 0x61] : "struct.atomic.rmw.sub", StructAtomicRmwAnd(Ordered>) : [0xfe, 0x62] : "struct.atomic.rmw.and", StructAtomicRmwOr(Ordered>) : [0xfe, 0x63] : "struct.atomic.rmw.or", StructAtomicRmwXor(Ordered>) : [0xfe, 0x64] : "struct.atomic.rmw.xor", StructAtomicRmwXchg(Ordered>) : [0xfe, 0x65] : "struct.atomic.rmw.xchg", StructAtomicRmwCmpxchg(Ordered>) : [0xfe, 0x66] : "struct.atomic.rmw.cmpxchg", ArrayAtomicGet(Ordered>) : [0xfe, 0x67] : "array.atomic.get", ArrayAtomicGetS(Ordered>) : [0xfe, 0x68] : "array.atomic.get_s", ArrayAtomicGetU(Ordered>) : [0xfe, 0x69] : "array.atomic.get_u", ArrayAtomicSet(Ordered>) : [0xfe, 0x6a] : "array.atomic.set", ArrayAtomicRmwAdd(Ordered>) : [0xfe, 0x6b] : "array.atomic.rmw.add", ArrayAtomicRmwSub(Ordered>) : [0xfe, 0x6c] : "array.atomic.rmw.sub", ArrayAtomicRmwAnd(Ordered>) : [0xfe, 0x6d] : "array.atomic.rmw.and", ArrayAtomicRmwOr(Ordered>) : [0xfe, 0x6e] : "array.atomic.rmw.or", ArrayAtomicRmwXor(Ordered>) : [0xfe, 0x6f] : "array.atomic.rmw.xor", ArrayAtomicRmwXchg(Ordered>) : [0xfe, 0x70] : "array.atomic.rmw.xchg", ArrayAtomicRmwCmpxchg(Ordered>) : [0xfe, 0x71] : "array.atomic.rmw.cmpxchg", RefI31Shared : [0xfe, 0x72] : "ref.i31_shared", // proposal: simd // // https://webassembly.github.io/simd/core/binary/instructions.html V128Load(MemArg<16>) : [0xfd, 0] : "v128.load", V128Load8x8S(MemArg<8>) : [0xfd, 1] : "v128.load8x8_s", V128Load8x8U(MemArg<8>) : [0xfd, 2] : "v128.load8x8_u", V128Load16x4S(MemArg<8>) : [0xfd, 3] : "v128.load16x4_s", V128Load16x4U(MemArg<8>) : [0xfd, 4] : "v128.load16x4_u", V128Load32x2S(MemArg<8>) : [0xfd, 5] : "v128.load32x2_s", V128Load32x2U(MemArg<8>) : [0xfd, 6] : "v128.load32x2_u", V128Load8Splat(MemArg<1>) : [0xfd, 7] : "v128.load8_splat", V128Load16Splat(MemArg<2>) : [0xfd, 8] : "v128.load16_splat", V128Load32Splat(MemArg<4>) : [0xfd, 9] : "v128.load32_splat", V128Load64Splat(MemArg<8>) : [0xfd, 10] : "v128.load64_splat", V128Load32Zero(MemArg<4>) : [0xfd, 92] : "v128.load32_zero", V128Load64Zero(MemArg<8>) : [0xfd, 93] : "v128.load64_zero", V128Store(MemArg<16>) : [0xfd, 11] : "v128.store", V128Load8Lane(LoadOrStoreLane<1>) : [0xfd, 84] : "v128.load8_lane", V128Load16Lane(LoadOrStoreLane<2>) : [0xfd, 85] : "v128.load16_lane", V128Load32Lane(LoadOrStoreLane<4>) : [0xfd, 86] : "v128.load32_lane", V128Load64Lane(LoadOrStoreLane<8>): [0xfd, 87] : "v128.load64_lane", V128Store8Lane(LoadOrStoreLane<1>) : [0xfd, 88] : "v128.store8_lane", V128Store16Lane(LoadOrStoreLane<2>) : [0xfd, 89] : "v128.store16_lane", V128Store32Lane(LoadOrStoreLane<4>) : [0xfd, 90] : "v128.store32_lane", V128Store64Lane(LoadOrStoreLane<8>) : [0xfd, 91] : "v128.store64_lane", V128Const(V128Const) : [0xfd, 12] : "v128.const", I8x16Shuffle(I8x16Shuffle) : [0xfd, 13] : "i8x16.shuffle", I8x16ExtractLaneS(LaneArg) : [0xfd, 21] : "i8x16.extract_lane_s", I8x16ExtractLaneU(LaneArg) : [0xfd, 22] : "i8x16.extract_lane_u", I8x16ReplaceLane(LaneArg) : [0xfd, 23] : "i8x16.replace_lane", I16x8ExtractLaneS(LaneArg) : [0xfd, 24] : "i16x8.extract_lane_s", I16x8ExtractLaneU(LaneArg) : [0xfd, 25] : "i16x8.extract_lane_u", I16x8ReplaceLane(LaneArg) : [0xfd, 26] : "i16x8.replace_lane", I32x4ExtractLane(LaneArg) : [0xfd, 27] : "i32x4.extract_lane", I32x4ReplaceLane(LaneArg) : [0xfd, 28] : "i32x4.replace_lane", I64x2ExtractLane(LaneArg) : [0xfd, 29] : "i64x2.extract_lane", I64x2ReplaceLane(LaneArg) : [0xfd, 30] : "i64x2.replace_lane", F32x4ExtractLane(LaneArg) : [0xfd, 31] : "f32x4.extract_lane", F32x4ReplaceLane(LaneArg) : [0xfd, 32] : "f32x4.replace_lane", F64x2ExtractLane(LaneArg) : [0xfd, 33] : "f64x2.extract_lane", F64x2ReplaceLane(LaneArg) : [0xfd, 34] : "f64x2.replace_lane", I8x16Swizzle : [0xfd, 14] : "i8x16.swizzle", I8x16Splat : [0xfd, 15] : "i8x16.splat", I16x8Splat : [0xfd, 16] : "i16x8.splat", I32x4Splat : [0xfd, 17] : "i32x4.splat", I64x2Splat : [0xfd, 18] : "i64x2.splat", F32x4Splat : [0xfd, 19] : "f32x4.splat", F64x2Splat : [0xfd, 20] : "f64x2.splat", I8x16Eq : [0xfd, 35] : "i8x16.eq", I8x16Ne : [0xfd, 36] : "i8x16.ne", I8x16LtS : [0xfd, 37] : "i8x16.lt_s", I8x16LtU : [0xfd, 38] : "i8x16.lt_u", I8x16GtS : [0xfd, 39] : "i8x16.gt_s", I8x16GtU : [0xfd, 40] : "i8x16.gt_u", I8x16LeS : [0xfd, 41] : "i8x16.le_s", I8x16LeU : [0xfd, 42] : "i8x16.le_u", I8x16GeS : [0xfd, 43] : "i8x16.ge_s", I8x16GeU : [0xfd, 44] : "i8x16.ge_u", I16x8Eq : [0xfd, 45] : "i16x8.eq", I16x8Ne : [0xfd, 46] : "i16x8.ne", I16x8LtS : [0xfd, 47] : "i16x8.lt_s", I16x8LtU : [0xfd, 48] : "i16x8.lt_u", I16x8GtS : [0xfd, 49] : "i16x8.gt_s", I16x8GtU : [0xfd, 50] : "i16x8.gt_u", I16x8LeS : [0xfd, 51] : "i16x8.le_s", I16x8LeU : [0xfd, 52] : "i16x8.le_u", I16x8GeS : [0xfd, 53] : "i16x8.ge_s", I16x8GeU : [0xfd, 54] : "i16x8.ge_u", I32x4Eq : [0xfd, 55] : "i32x4.eq", I32x4Ne : [0xfd, 56] : "i32x4.ne", I32x4LtS : [0xfd, 57] : "i32x4.lt_s", I32x4LtU : [0xfd, 58] : "i32x4.lt_u", I32x4GtS : [0xfd, 59] : "i32x4.gt_s", I32x4GtU : [0xfd, 60] : "i32x4.gt_u", I32x4LeS : [0xfd, 61] : "i32x4.le_s", I32x4LeU : [0xfd, 62] : "i32x4.le_u", I32x4GeS : [0xfd, 63] : "i32x4.ge_s", I32x4GeU : [0xfd, 64] : "i32x4.ge_u", I64x2Eq : [0xfd, 214] : "i64x2.eq", I64x2Ne : [0xfd, 215] : "i64x2.ne", I64x2LtS : [0xfd, 216] : "i64x2.lt_s", I64x2GtS : [0xfd, 217] : "i64x2.gt_s", I64x2LeS : [0xfd, 218] : "i64x2.le_s", I64x2GeS : [0xfd, 219] : "i64x2.ge_s", F32x4Eq : [0xfd, 65] : "f32x4.eq", F32x4Ne : [0xfd, 66] : "f32x4.ne", F32x4Lt : [0xfd, 67] : "f32x4.lt", F32x4Gt : [0xfd, 68] : "f32x4.gt", F32x4Le : [0xfd, 69] : "f32x4.le", F32x4Ge : [0xfd, 70] : "f32x4.ge", F64x2Eq : [0xfd, 71] : "f64x2.eq", F64x2Ne : [0xfd, 72] : "f64x2.ne", F64x2Lt : [0xfd, 73] : "f64x2.lt", F64x2Gt : [0xfd, 74] : "f64x2.gt", F64x2Le : [0xfd, 75] : "f64x2.le", F64x2Ge : [0xfd, 76] : "f64x2.ge", V128Not : [0xfd, 77] : "v128.not", V128And : [0xfd, 78] : "v128.and", V128Andnot : [0xfd, 79] : "v128.andnot", V128Or : [0xfd, 80] : "v128.or", V128Xor : [0xfd, 81] : "v128.xor", V128Bitselect : [0xfd, 82] : "v128.bitselect", V128AnyTrue : [0xfd, 83] : "v128.any_true", I8x16Abs : [0xfd, 96] : "i8x16.abs", I8x16Neg : [0xfd, 97] : "i8x16.neg", I8x16Popcnt : [0xfd, 98] : "i8x16.popcnt", I8x16AllTrue : [0xfd, 99] : "i8x16.all_true", I8x16Bitmask : [0xfd, 100] : "i8x16.bitmask", I8x16NarrowI16x8S : [0xfd, 101] : "i8x16.narrow_i16x8_s", I8x16NarrowI16x8U : [0xfd, 102] : "i8x16.narrow_i16x8_u", I8x16Shl : [0xfd, 107] : "i8x16.shl", I8x16ShrS : [0xfd, 108] : "i8x16.shr_s", I8x16ShrU : [0xfd, 109] : "i8x16.shr_u", I8x16Add : [0xfd, 110] : "i8x16.add", I8x16AddSatS : [0xfd, 111] : "i8x16.add_sat_s", I8x16AddSatU : [0xfd, 112] : "i8x16.add_sat_u", I8x16Sub : [0xfd, 113] : "i8x16.sub", I8x16SubSatS : [0xfd, 114] : "i8x16.sub_sat_s", I8x16SubSatU : [0xfd, 115] : "i8x16.sub_sat_u", I8x16MinS : [0xfd, 118] : "i8x16.min_s", I8x16MinU : [0xfd, 119] : "i8x16.min_u", I8x16MaxS : [0xfd, 120] : "i8x16.max_s", I8x16MaxU : [0xfd, 121] : "i8x16.max_u", I8x16AvgrU : [0xfd, 123] : "i8x16.avgr_u", I16x8ExtAddPairwiseI8x16S : [0xfd, 124] : "i16x8.extadd_pairwise_i8x16_s", I16x8ExtAddPairwiseI8x16U : [0xfd, 125] : "i16x8.extadd_pairwise_i8x16_u", I16x8Abs : [0xfd, 128] : "i16x8.abs", I16x8Neg : [0xfd, 129] : "i16x8.neg", I16x8Q15MulrSatS : [0xfd, 130] : "i16x8.q15mulr_sat_s", I16x8AllTrue : [0xfd, 131] : "i16x8.all_true", I16x8Bitmask : [0xfd, 132] : "i16x8.bitmask", I16x8NarrowI32x4S : [0xfd, 133] : "i16x8.narrow_i32x4_s", I16x8NarrowI32x4U : [0xfd, 134] : "i16x8.narrow_i32x4_u", I16x8ExtendLowI8x16S : [0xfd, 135] : "i16x8.extend_low_i8x16_s", I16x8ExtendHighI8x16S : [0xfd, 136] : "i16x8.extend_high_i8x16_s", I16x8ExtendLowI8x16U : [0xfd, 137] : "i16x8.extend_low_i8x16_u", I16x8ExtendHighI8x16u : [0xfd, 138] : "i16x8.extend_high_i8x16_u", I16x8Shl : [0xfd, 139] : "i16x8.shl", I16x8ShrS : [0xfd, 140] : "i16x8.shr_s", I16x8ShrU : [0xfd, 141] : "i16x8.shr_u", I16x8Add : [0xfd, 142] : "i16x8.add", I16x8AddSatS : [0xfd, 143] : "i16x8.add_sat_s", I16x8AddSatU : [0xfd, 144] : "i16x8.add_sat_u", I16x8Sub : [0xfd, 145] : "i16x8.sub", I16x8SubSatS : [0xfd, 146] : "i16x8.sub_sat_s", I16x8SubSatU : [0xfd, 147] : "i16x8.sub_sat_u", I16x8Mul : [0xfd, 149] : "i16x8.mul", I16x8MinS : [0xfd, 150] : "i16x8.min_s", I16x8MinU : [0xfd, 151] : "i16x8.min_u", I16x8MaxS : [0xfd, 152] : "i16x8.max_s", I16x8MaxU : [0xfd, 153] : "i16x8.max_u", I16x8AvgrU : [0xfd, 155] : "i16x8.avgr_u", I16x8ExtMulLowI8x16S : [0xfd, 156] : "i16x8.extmul_low_i8x16_s", I16x8ExtMulHighI8x16S : [0xfd, 157] : "i16x8.extmul_high_i8x16_s", I16x8ExtMulLowI8x16U : [0xfd, 158] : "i16x8.extmul_low_i8x16_u", I16x8ExtMulHighI8x16U : [0xfd, 159] : "i16x8.extmul_high_i8x16_u", I32x4ExtAddPairwiseI16x8S : [0xfd, 126] : "i32x4.extadd_pairwise_i16x8_s", I32x4ExtAddPairwiseI16x8U : [0xfd, 127] : "i32x4.extadd_pairwise_i16x8_u", I32x4Abs : [0xfd, 160] : "i32x4.abs", I32x4Neg : [0xfd, 161] : "i32x4.neg", I32x4AllTrue : [0xfd, 163] : "i32x4.all_true", I32x4Bitmask : [0xfd, 164] : "i32x4.bitmask", I32x4ExtendLowI16x8S : [0xfd, 167] : "i32x4.extend_low_i16x8_s", I32x4ExtendHighI16x8S : [0xfd, 168] : "i32x4.extend_high_i16x8_s", I32x4ExtendLowI16x8U : [0xfd, 169] : "i32x4.extend_low_i16x8_u", I32x4ExtendHighI16x8U : [0xfd, 170] : "i32x4.extend_high_i16x8_u", I32x4Shl : [0xfd, 171] : "i32x4.shl", I32x4ShrS : [0xfd, 172] : "i32x4.shr_s", I32x4ShrU : [0xfd, 173] : "i32x4.shr_u", I32x4Add : [0xfd, 174] : "i32x4.add", I32x4Sub : [0xfd, 177] : "i32x4.sub", I32x4Mul : [0xfd, 181] : "i32x4.mul", I32x4MinS : [0xfd, 182] : "i32x4.min_s", I32x4MinU : [0xfd, 183] : "i32x4.min_u", I32x4MaxS : [0xfd, 184] : "i32x4.max_s", I32x4MaxU : [0xfd, 185] : "i32x4.max_u", I32x4DotI16x8S : [0xfd, 186] : "i32x4.dot_i16x8_s", I32x4ExtMulLowI16x8S : [0xfd, 188] : "i32x4.extmul_low_i16x8_s", I32x4ExtMulHighI16x8S : [0xfd, 189] : "i32x4.extmul_high_i16x8_s", I32x4ExtMulLowI16x8U : [0xfd, 190] : "i32x4.extmul_low_i16x8_u", I32x4ExtMulHighI16x8U : [0xfd, 191] : "i32x4.extmul_high_i16x8_u", I64x2Abs : [0xfd, 192] : "i64x2.abs", I64x2Neg : [0xfd, 193] : "i64x2.neg", I64x2AllTrue : [0xfd, 195] : "i64x2.all_true", I64x2Bitmask : [0xfd, 196] : "i64x2.bitmask", I64x2ExtendLowI32x4S : [0xfd, 199] : "i64x2.extend_low_i32x4_s", I64x2ExtendHighI32x4S : [0xfd, 200] : "i64x2.extend_high_i32x4_s", I64x2ExtendLowI32x4U : [0xfd, 201] : "i64x2.extend_low_i32x4_u", I64x2ExtendHighI32x4U : [0xfd, 202] : "i64x2.extend_high_i32x4_u", I64x2Shl : [0xfd, 203] : "i64x2.shl", I64x2ShrS : [0xfd, 204] : "i64x2.shr_s", I64x2ShrU : [0xfd, 205] : "i64x2.shr_u", I64x2Add : [0xfd, 206] : "i64x2.add", I64x2Sub : [0xfd, 209] : "i64x2.sub", I64x2Mul : [0xfd, 213] : "i64x2.mul", I64x2ExtMulLowI32x4S : [0xfd, 220] : "i64x2.extmul_low_i32x4_s", I64x2ExtMulHighI32x4S : [0xfd, 221] : "i64x2.extmul_high_i32x4_s", I64x2ExtMulLowI32x4U : [0xfd, 222] : "i64x2.extmul_low_i32x4_u", I64x2ExtMulHighI32x4U : [0xfd, 223] : "i64x2.extmul_high_i32x4_u", F32x4Ceil : [0xfd, 103] : "f32x4.ceil", F32x4Floor : [0xfd, 104] : "f32x4.floor", F32x4Trunc : [0xfd, 105] : "f32x4.trunc", F32x4Nearest : [0xfd, 106] : "f32x4.nearest", F32x4Abs : [0xfd, 224] : "f32x4.abs", F32x4Neg : [0xfd, 225] : "f32x4.neg", F32x4Sqrt : [0xfd, 227] : "f32x4.sqrt", F32x4Add : [0xfd, 228] : "f32x4.add", F32x4Sub : [0xfd, 229] : "f32x4.sub", F32x4Mul : [0xfd, 230] : "f32x4.mul", F32x4Div : [0xfd, 231] : "f32x4.div", F32x4Min : [0xfd, 232] : "f32x4.min", F32x4Max : [0xfd, 233] : "f32x4.max", F32x4PMin : [0xfd, 234] : "f32x4.pmin", F32x4PMax : [0xfd, 235] : "f32x4.pmax", F64x2Ceil : [0xfd, 116] : "f64x2.ceil", F64x2Floor : [0xfd, 117] : "f64x2.floor", F64x2Trunc : [0xfd, 122] : "f64x2.trunc", F64x2Nearest : [0xfd, 148] : "f64x2.nearest", F64x2Abs : [0xfd, 236] : "f64x2.abs", F64x2Neg : [0xfd, 237] : "f64x2.neg", F64x2Sqrt : [0xfd, 239] : "f64x2.sqrt", F64x2Add : [0xfd, 240] : "f64x2.add", F64x2Sub : [0xfd, 241] : "f64x2.sub", F64x2Mul : [0xfd, 242] : "f64x2.mul", F64x2Div : [0xfd, 243] : "f64x2.div", F64x2Min : [0xfd, 244] : "f64x2.min", F64x2Max : [0xfd, 245] : "f64x2.max", F64x2PMin : [0xfd, 246] : "f64x2.pmin", F64x2PMax : [0xfd, 247] : "f64x2.pmax", I32x4TruncSatF32x4S : [0xfd, 248] : "i32x4.trunc_sat_f32x4_s", I32x4TruncSatF32x4U : [0xfd, 249] : "i32x4.trunc_sat_f32x4_u", F32x4ConvertI32x4S : [0xfd, 250] : "f32x4.convert_i32x4_s", F32x4ConvertI32x4U : [0xfd, 251] : "f32x4.convert_i32x4_u", I32x4TruncSatF64x2SZero : [0xfd, 252] : "i32x4.trunc_sat_f64x2_s_zero", I32x4TruncSatF64x2UZero : [0xfd, 253] : "i32x4.trunc_sat_f64x2_u_zero", F64x2ConvertLowI32x4S : [0xfd, 254] : "f64x2.convert_low_i32x4_s", F64x2ConvertLowI32x4U : [0xfd, 255] : "f64x2.convert_low_i32x4_u", F32x4DemoteF64x2Zero : [0xfd, 94] : "f32x4.demote_f64x2_zero", F64x2PromoteLowF32x4 : [0xfd, 95] : "f64x2.promote_low_f32x4", // Exception handling proposal ThrowRef : [0x0a] : "throw_ref", TryTable(TryTable<'a>) : [0x1f] : "try_table", Throw(Index<'a>) : [0x08] : "throw", // Deprecated exception handling opcodes Try(Box>) : [0x06] : "try", Catch(Index<'a>) : [0x07] : "catch", Rethrow(Index<'a>) : [0x09] : "rethrow", Delegate(Index<'a>) : [0x18] : "delegate", CatchAll : [0x19] : "catch_all", // Relaxed SIMD proposal I8x16RelaxedSwizzle : [0xfd, 0x100]: "i8x16.relaxed_swizzle", I32x4RelaxedTruncF32x4S : [0xfd, 0x101]: "i32x4.relaxed_trunc_f32x4_s", I32x4RelaxedTruncF32x4U : [0xfd, 0x102]: "i32x4.relaxed_trunc_f32x4_u", I32x4RelaxedTruncF64x2SZero : [0xfd, 0x103]: "i32x4.relaxed_trunc_f64x2_s_zero", I32x4RelaxedTruncF64x2UZero : [0xfd, 0x104]: "i32x4.relaxed_trunc_f64x2_u_zero", F32x4RelaxedMadd : [0xfd, 0x105]: "f32x4.relaxed_madd", F32x4RelaxedNmadd : [0xfd, 0x106]: "f32x4.relaxed_nmadd", F64x2RelaxedMadd : [0xfd, 0x107]: "f64x2.relaxed_madd", F64x2RelaxedNmadd : [0xfd, 0x108]: "f64x2.relaxed_nmadd", I8x16RelaxedLaneselect : [0xfd, 0x109]: "i8x16.relaxed_laneselect", I16x8RelaxedLaneselect : [0xfd, 0x10A]: "i16x8.relaxed_laneselect", I32x4RelaxedLaneselect : [0xfd, 0x10B]: "i32x4.relaxed_laneselect", I64x2RelaxedLaneselect : [0xfd, 0x10C]: "i64x2.relaxed_laneselect", F32x4RelaxedMin : [0xfd, 0x10D]: "f32x4.relaxed_min", F32x4RelaxedMax : [0xfd, 0x10E]: "f32x4.relaxed_max", F64x2RelaxedMin : [0xfd, 0x10F]: "f64x2.relaxed_min", F64x2RelaxedMax : [0xfd, 0x110]: "f64x2.relaxed_max", I16x8RelaxedQ15mulrS: [0xfd, 0x111]: "i16x8.relaxed_q15mulr_s", I16x8RelaxedDotI8x16I7x16S: [0xfd, 0x112]: "i16x8.relaxed_dot_i8x16_i7x16_s", I32x4RelaxedDotI8x16I7x16AddS: [0xfd, 0x113]: "i32x4.relaxed_dot_i8x16_i7x16_add_s", } } // As shown in #1095 the size of this variant is somewhat performance-sensitive // since big `*.wat` files will have a lot of these. This is a small ratchet to // make sure that this enum doesn't become larger than it already is, although // ideally it also wouldn't be as large as it is now. const _: () = { let size = std::mem::size_of::>(); let pointer = std::mem::size_of::(); assert!(size <= pointer * 10); }; impl<'a> Instruction<'a> { pub(crate) fn needs_data_count(&self) -> bool { match self { Instruction::MemoryInit(_) | Instruction::DataDrop(_) | Instruction::ArrayNewData(_) | Instruction::ArrayInitData(_) => true, _ => false, } } } /// Extra information associated with block-related instructions. /// /// This is used to label blocks and also annotate what types are expected for /// the block. #[derive(Debug, Clone)] #[allow(missing_docs)] pub struct BlockType<'a> { pub label: Option>, pub label_name: Option>, pub ty: TypeUse<'a, FunctionType<'a>>, } impl<'a> Parse<'a> for BlockType<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(BlockType { label: parser.parse()?, label_name: parser.parse()?, ty: parser .parse::>>()? .into(), }) } } #[derive(Debug, Clone)] #[allow(missing_docs)] pub struct TryTable<'a> { pub block: Box>, pub catches: Vec>, } impl<'a> Parse<'a> for TryTable<'a> { fn parse(parser: Parser<'a>) -> Result { let block = parser.parse()?; let mut catches = Vec::new(); while parser.peek::()? && (parser.peek2::()? || parser.peek2::()? || parser.peek2::()? || parser.peek2::()?) { catches.push(parser.parens(|p| { let kind = if parser.peek::()? { p.parse::()?; TryTableCatchKind::CatchRef(p.parse()?) } else if parser.peek::()? { p.parse::()?; TryTableCatchKind::Catch(p.parse()?) } else if parser.peek::()? { p.parse::()?; TryTableCatchKind::CatchAll } else { p.parse::()?; TryTableCatchKind::CatchAllRef }; Ok(TryTableCatch { kind, label: p.parse()?, }) })?); } Ok(TryTable { block, catches }) } } #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum TryTableCatchKind<'a> { // Catch a tagged exception, do not capture an exnref. Catch(Index<'a>), // Catch a tagged exception, and capture the exnref. CatchRef(Index<'a>), // Catch any exception, do not capture an exnref. CatchAll, // Catch any exception, and capture the exnref. CatchAllRef, } impl<'a> TryTableCatchKind<'a> { #[allow(missing_docs)] pub fn tag_index_mut(&mut self) -> Option<&mut Index<'a>> { match self { TryTableCatchKind::Catch(tag) | TryTableCatchKind::CatchRef(tag) => Some(tag), TryTableCatchKind::CatchAll | TryTableCatchKind::CatchAllRef => None, } } } #[derive(Debug, Clone)] #[allow(missing_docs)] pub struct TryTableCatch<'a> { pub kind: TryTableCatchKind<'a>, pub label: Index<'a>, } /// Extra information associated with the `br_table` instruction. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct BrTableIndices<'a> { pub labels: Vec>, pub default: Index<'a>, } impl<'a> Parse<'a> for BrTableIndices<'a> { fn parse(parser: Parser<'a>) -> Result { let mut labels = vec![parser.parse()?]; while parser.peek::()? { labels.push(parser.parse()?); } let default = labels.pop().unwrap(); Ok(BrTableIndices { labels, default }) } } /// Payload for lane-related instructions. Unsigned with no + prefix. #[derive(Debug, Clone)] pub struct LaneArg { /// The lane argument. pub lane: u8, } impl<'a> Parse<'a> for LaneArg { fn parse(parser: Parser<'a>) -> Result { let lane = parser.step(|c| { if let Some((i, rest)) = c.integer()? { if i.sign() == None { let (src, radix) = i.val(); let val = u8::from_str_radix(src, radix) .map_err(|_| c.error("malformed lane index"))?; Ok((val, rest)) } else { Err(c.error("unexpected token")) } } else { Err(c.error("expected a lane index")) } })?; Ok(LaneArg { lane }) } } /// Payload for memory-related instructions indicating offset/alignment of /// memory accesses. #[derive(Debug, Clone)] pub struct MemArg<'a> { /// The alignment of this access. /// /// This is not stored as a log, this is the actual alignment (e.g. 1, 2, 4, /// 8, etc). pub align: u32, /// The offset, in bytes of this access. pub offset: u64, /// The memory index we're accessing pub memory: Index<'a>, } impl<'a> MemArg<'a> { fn parse(parser: Parser<'a>, default_align: u32) -> Result { fn parse_field( name: &str, parser: Parser<'_>, f: impl FnOnce(Cursor<'_>, &str, u32) -> Result, ) -> Result> { parser.step(|c| { let (kw, rest) = match c.keyword()? { Some(p) => p, None => return Ok((None, c)), }; if !kw.starts_with(name) { return Ok((None, c)); } let kw = &kw[name.len()..]; if !kw.starts_with('=') { return Ok((None, c)); } let num = &kw[1..]; let num = if let Some(stripped) = num.strip_prefix("0x") { f(c, stripped, 16)? } else { f(c, num, 10)? }; Ok((Some(num), rest)) }) } fn parse_u32(name: &str, parser: Parser<'_>) -> Result> { parse_field(name, parser, |c, num, radix| { u32::from_str_radix(num, radix).map_err(|_| c.error("i32 constant out of range")) }) } fn parse_u64(name: &str, parser: Parser<'_>) -> Result> { parse_field(name, parser, |c, num, radix| { u64::from_str_radix(num, radix).map_err(|_| c.error("i64 constant out of range")) }) } let memory = parser .parse::>()? .unwrap_or_else(|| 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() => { return Err(parser.error("alignment must be a power of two")) } n => n.unwrap_or(default_align), }; Ok(MemArg { offset, align, memory, }) } } /// Extra data associated with the `loadN_lane` and `storeN_lane` instructions. #[derive(Debug, Clone)] pub struct LoadOrStoreLane<'a> { /// The memory argument for this instruction. pub memarg: MemArg<'a>, /// The lane argument for this instruction. pub lane: LaneArg, } impl<'a> LoadOrStoreLane<'a> { fn parse(parser: Parser<'a>, default_align: u32) -> Result { // This is sort of funky. The first integer we see could be the lane // index, but it could also be the memory index. To determine what it is // then if we see a second integer we need to look further. let has_memarg = parser.step(|c| match c.integer()? { Some((_, after_int)) => { // Two integers in a row? That means that the first one is the // memory index and the second must be the lane index. if after_int.integer()?.is_some() { return Ok((true, c)); } // If the first integer is trailed by `offset=...` or // `align=...` then this is definitely a memarg. if let Some((kw, _)) = after_int.keyword()? { if kw.starts_with("offset=") || kw.starts_with("align=") { return Ok((true, c)); } } // Otherwise the first integer was trailed by something that // didn't look like a memarg, so this must be the lane index. Ok((false, c)) } // Not an integer here? That must mean that this must be the memarg // first followed by the trailing index. None => Ok((true, c)), })?; Ok(LoadOrStoreLane { memarg: if has_memarg { MemArg::parse(parser, default_align)? } else { MemArg { align: default_align, offset: 0, memory: Index::Num(0, parser.prev_span()), } }, lane: LaneArg::parse(parser)?, }) } } /// Extra data associated with the `call_indirect` instruction. #[derive(Debug, Clone)] pub struct CallIndirect<'a> { /// The table that this call is going to be indexing. pub table: Index<'a>, /// Type type signature that this `call_indirect` instruction is using. pub ty: TypeUse<'a, FunctionType<'a>>, } impl<'a> Parse<'a> for CallIndirect<'a> { fn parse(parser: Parser<'a>) -> Result { let prev_span = parser.prev_span(); let table: Option<_> = parser.parse()?; let ty = parser.parse::>>()?; Ok(CallIndirect { table: table.unwrap_or(Index::Num(0, prev_span)), ty: ty.into(), }) } } /// Extra data associated with the `table.init` instruction #[derive(Debug, Clone)] pub struct TableInit<'a> { /// The index of the table we're copying into. pub table: Index<'a>, /// The index of the element segment we're copying into a table. pub elem: Index<'a>, } impl<'a> Parse<'a> for TableInit<'a> { fn parse(parser: Parser<'a>) -> Result { let prev_span = parser.prev_span(); let (elem, table) = if parser.peek2::()? { let table = parser.parse()?; (parser.parse()?, table) } else { (parser.parse()?, Index::Num(0, prev_span)) }; Ok(TableInit { table, elem }) } } /// Extra data associated with the `table.copy` instruction. #[derive(Debug, Clone)] pub struct TableCopy<'a> { /// The index of the destination table to copy into. pub dst: Index<'a>, /// The index of the source table to copy from. pub src: Index<'a>, } impl<'a> Parse<'a> for TableCopy<'a> { fn parse(parser: Parser<'a>) -> Result { let (dst, src) = match parser.parse::>()? { Some(dst) => (dst, parser.parse()?), None => ( Index::Num(0, parser.prev_span()), Index::Num(0, parser.prev_span()), ), }; Ok(TableCopy { dst, src }) } } /// Extra data associated with unary table instructions. #[derive(Debug, Clone)] pub struct TableArg<'a> { /// The index of the table argument. pub dst: Index<'a>, } // `TableArg` could be an unwrapped as an `Index` if not for this custom parse // behavior: if we cannot parse a table index, we default to table `0`. impl<'a> Parse<'a> for TableArg<'a> { fn parse(parser: Parser<'a>) -> Result { let dst = if let Some(dst) = parser.parse()? { dst } else { Index::Num(0, parser.prev_span()) }; Ok(TableArg { dst }) } } /// Extra data associated with unary memory instructions. #[derive(Debug, Clone)] pub struct MemoryArg<'a> { /// The index of the memory space. pub mem: Index<'a>, } impl<'a> Parse<'a> for MemoryArg<'a> { fn parse(parser: Parser<'a>) -> Result { let mem = if let Some(mem) = parser.parse()? { mem } else { Index::Num(0, parser.prev_span()) }; Ok(MemoryArg { mem }) } } /// Extra data associated with the `memory.init` instruction #[derive(Debug, Clone)] pub struct MemoryInit<'a> { /// The index of the data segment we're copying into memory. pub data: Index<'a>, /// The index of the memory we're copying into, pub mem: Index<'a>, } impl<'a> Parse<'a> for MemoryInit<'a> { fn parse(parser: Parser<'a>) -> Result { let prev_span = parser.prev_span(); let (data, mem) = if parser.peek2::()? { let memory = parser.parse()?; (parser.parse()?, memory) } else { (parser.parse()?, Index::Num(0, prev_span)) }; Ok(MemoryInit { data, mem }) } } /// Extra data associated with the `memory.copy` instruction #[derive(Debug, Clone)] pub struct MemoryCopy<'a> { /// The index of the memory we're copying from. pub src: Index<'a>, /// The index of the memory we're copying to. pub dst: Index<'a>, } impl<'a> Parse<'a> for MemoryCopy<'a> { fn parse(parser: Parser<'a>) -> Result { let (src, dst) = match parser.parse()? { Some(dst) => (parser.parse()?, dst), None => ( Index::Num(0, parser.prev_span()), Index::Num(0, parser.prev_span()), ), }; Ok(MemoryCopy { src, dst }) } } /// Extra data associated with the `struct.get/set` instructions #[derive(Debug, Clone)] pub struct StructAccess<'a> { /// The index of the struct type we're accessing. pub r#struct: Index<'a>, /// The index of the field of the struct we're accessing pub field: Index<'a>, } impl<'a> Parse<'a> for StructAccess<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(StructAccess { r#struct: parser.parse()?, field: parser.parse()?, }) } } /// Extra data associated with the `array.fill` instruction #[derive(Debug, Clone)] pub struct ArrayFill<'a> { /// The index of the array type we're filling. pub array: Index<'a>, } impl<'a> Parse<'a> for ArrayFill<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(ArrayFill { array: parser.parse()?, }) } } /// Extra data associated with the `array.copy` instruction #[derive(Debug, Clone)] pub struct ArrayCopy<'a> { /// The index of the array type we're copying to. pub dest_array: Index<'a>, /// The index of the array type we're copying from. pub src_array: Index<'a>, } impl<'a> Parse<'a> for ArrayCopy<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(ArrayCopy { dest_array: parser.parse()?, src_array: parser.parse()?, }) } } /// Extra data associated with the `array.init_[data/elem]` instruction #[derive(Debug, Clone)] pub struct ArrayInit<'a> { /// The index of the array type we're initializing. pub array: Index<'a>, /// The index of the data or elem segment we're reading from. pub segment: Index<'a>, } impl<'a> Parse<'a> for ArrayInit<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(ArrayInit { array: parser.parse()?, segment: parser.parse()?, }) } } /// Extra data associated with the `array.new_fixed` instruction #[derive(Debug, Clone)] pub struct ArrayNewFixed<'a> { /// The index of the array type we're accessing. pub array: Index<'a>, /// The amount of values to initialize the array with. pub length: u32, } impl<'a> Parse<'a> for ArrayNewFixed<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(ArrayNewFixed { array: parser.parse()?, length: parser.parse()?, }) } } /// Extra data associated with the `array.new_data` instruction #[derive(Debug, Clone)] pub struct ArrayNewData<'a> { /// The index of the array type we're accessing. pub array: Index<'a>, /// The data segment to initialize from. pub data_idx: Index<'a>, } impl<'a> Parse<'a> for ArrayNewData<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(ArrayNewData { array: parser.parse()?, data_idx: parser.parse()?, }) } } /// Extra data associated with the `array.new_elem` instruction #[derive(Debug, Clone)] pub struct ArrayNewElem<'a> { /// The index of the array type we're accessing. pub array: Index<'a>, /// The elem segment to initialize from. pub elem_idx: Index<'a>, } impl<'a> Parse<'a> for ArrayNewElem<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(ArrayNewElem { array: parser.parse()?, elem_idx: parser.parse()?, }) } } /// Extra data associated with the `ref.cast` instruction #[derive(Debug, Clone)] pub struct RefCast<'a> { /// The type to cast to. pub r#type: RefType<'a>, } impl<'a> Parse<'a> for RefCast<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(RefCast { r#type: parser.parse()?, }) } } /// Extra data associated with the `ref.test` instruction #[derive(Debug, Clone)] pub struct RefTest<'a> { /// The type to test for. pub r#type: RefType<'a>, } impl<'a> Parse<'a> for RefTest<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(RefTest { r#type: parser.parse()?, }) } } /// Extra data associated with the `br_on_cast` instruction #[derive(Debug, Clone)] pub struct BrOnCast<'a> { /// The label to branch to. pub label: Index<'a>, /// The type we're casting from. pub from_type: RefType<'a>, /// The type we're casting to. pub to_type: RefType<'a>, } impl<'a> Parse<'a> for BrOnCast<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(BrOnCast { label: parser.parse()?, from_type: parser.parse()?, to_type: parser.parse()?, }) } } /// Extra data associated with the `br_on_cast_fail` instruction #[derive(Debug, Clone)] pub struct BrOnCastFail<'a> { /// The label to branch to. pub label: Index<'a>, /// The type we're casting from. pub from_type: RefType<'a>, /// The type we're casting to. pub to_type: RefType<'a>, } impl<'a> Parse<'a> for BrOnCastFail<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(BrOnCastFail { label: parser.parse()?, from_type: parser.parse()?, to_type: parser.parse()?, }) } } /// The memory ordering for atomic instructions. /// /// For an in-depth explanation of memory orderings, see the C++ documentation /// for [`memory_order`] or the Rust documentation for [`atomic::Ordering`]. /// /// [`memory_order`]: https://en.cppreference.com/w/cpp/atomic/memory_order /// [`atomic::Ordering`]: https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html #[derive(Clone, Debug)] pub enum Ordering { /// Like `AcqRel` but all threads see all sequentially consistent operations /// in the same order. AcqRel, /// For a load, it acquires; this orders all operations before the last /// "releasing" store. For a store, it releases; this orders all operations /// before it at the next "acquiring" load. SeqCst, } impl<'a> Parse<'a> for Ordering { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { parser.parse::()?; Ok(Ordering::SeqCst) } else if parser.peek::()? { parser.parse::()?; Ok(Ordering::AcqRel) } else { Err(parser.error("expected a memory ordering: `seq_cst` or `acq_rel`")) } } } /// Add a memory [`Ordering`] to the argument `T` of some instruction. /// /// This is helpful for many kinds of `*.atomic.*` instructions introduced by /// the shared-everything-threads proposal. Many of these instructions "build /// on" existing instructions by simply adding a memory order to them. #[derive(Clone, Debug)] pub struct Ordered { /// The memory ordering for this atomic instruction. pub ordering: Ordering, /// The original argument type. pub inner: T, } impl<'a, T> Parse<'a> for Ordered where T: Parse<'a>, { fn parse(parser: Parser<'a>) -> Result { let ordering = parser.parse()?; let inner = parser.parse()?; Ok(Ordered { ordering, inner }) } } /// Different ways to specify a `v128.const` instruction #[derive(Clone, Debug)] #[allow(missing_docs)] pub enum V128Const { I8x16([i8; 16]), I16x8([i16; 8]), I32x4([i32; 4]), I64x2([i64; 2]), F32x4([F32; 4]), F64x2([F64; 2]), } impl V128Const { /// Returns the raw little-ended byte sequence used to represent this /// `v128` constant` /// /// This is typically suitable for encoding as the payload of the /// `v128.const` instruction. #[rustfmt::skip] pub fn to_le_bytes(&self) -> [u8; 16] { match self { V128Const::I8x16(arr) => [ arr[0] as u8, arr[1] as u8, arr[2] as u8, arr[3] as u8, arr[4] as u8, arr[5] as u8, arr[6] as u8, arr[7] as u8, arr[8] as u8, arr[9] as u8, arr[10] as u8, arr[11] as u8, arr[12] as u8, arr[13] as u8, arr[14] as u8, arr[15] as u8, ], V128Const::I16x8(arr) => { let a1 = arr[0].to_le_bytes(); let a2 = arr[1].to_le_bytes(); let a3 = arr[2].to_le_bytes(); let a4 = arr[3].to_le_bytes(); let a5 = arr[4].to_le_bytes(); let a6 = arr[5].to_le_bytes(); let a7 = arr[6].to_le_bytes(); let a8 = arr[7].to_le_bytes(); [ a1[0], a1[1], a2[0], a2[1], a3[0], a3[1], a4[0], a4[1], a5[0], a5[1], a6[0], a6[1], a7[0], a7[1], a8[0], a8[1], ] } V128Const::I32x4(arr) => { let a1 = arr[0].to_le_bytes(); let a2 = arr[1].to_le_bytes(); let a3 = arr[2].to_le_bytes(); let a4 = arr[3].to_le_bytes(); [ a1[0], a1[1], a1[2], a1[3], a2[0], a2[1], a2[2], a2[3], a3[0], a3[1], a3[2], a3[3], a4[0], a4[1], a4[2], a4[3], ] } V128Const::I64x2(arr) => { let a1 = arr[0].to_le_bytes(); let a2 = arr[1].to_le_bytes(); [ a1[0], a1[1], a1[2], a1[3], a1[4], a1[5], a1[6], a1[7], a2[0], a2[1], a2[2], a2[3], a2[4], a2[5], a2[6], a2[7], ] } V128Const::F32x4(arr) => { let a1 = arr[0].bits.to_le_bytes(); let a2 = arr[1].bits.to_le_bytes(); let a3 = arr[2].bits.to_le_bytes(); let a4 = arr[3].bits.to_le_bytes(); [ a1[0], a1[1], a1[2], a1[3], a2[0], a2[1], a2[2], a2[3], a3[0], a3[1], a3[2], a3[3], a4[0], a4[1], a4[2], a4[3], ] } V128Const::F64x2(arr) => { let a1 = arr[0].bits.to_le_bytes(); let a2 = arr[1].bits.to_le_bytes(); [ a1[0], a1[1], a1[2], a1[3], a1[4], a1[5], a1[6], a1[7], a2[0], a2[1], a2[2], a2[3], a2[4], a2[5], a2[6], a2[7], ] } } } } impl<'a> Parse<'a> for V128Const { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(V128Const::I8x16([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Const::I16x8([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Const::I32x4([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Const::I64x2([parser.parse()?, parser.parse()?])) } else if l.peek::()? { parser.parse::()?; Ok(V128Const::F32x4([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Const::F64x2([parser.parse()?, parser.parse()?])) } else { Err(l.error()) } } } /// Lanes being shuffled in the `i8x16.shuffle` instruction #[derive(Debug, Clone)] pub struct I8x16Shuffle { #[allow(missing_docs)] pub lanes: [u8; 16], } impl<'a> Parse<'a> for I8x16Shuffle { fn parse(parser: Parser<'a>) -> Result { Ok(I8x16Shuffle { lanes: [ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ], }) } } /// Payload of the `select` instructions #[derive(Debug, Clone)] pub struct SelectTypes<'a> { #[allow(missing_docs)] pub tys: Option>>, } impl<'a> Parse<'a> for SelectTypes<'a> { fn parse(parser: Parser<'a>) -> Result { let mut found = false; let mut list = Vec::new(); while parser.peek2::()? { found = true; parser.parens(|p| { p.parse::()?; while !p.is_empty() { list.push(p.parse()?); } Ok(()) })?; } Ok(SelectTypes { tys: if found { Some(list) } else { None }, }) } } wast-217.0.0/src/core/func.rs000064400000000000000000000101611046102023000137700ustar 00000000000000use 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. /// /// This is a member of both the function and code sections. #[derive(Debug)] pub struct Func<'a> { /// Where this `func` was defined. pub span: Span, /// An identifier that this function is resolved with (optionally) for name /// resolution. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. 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: TypeUse<'a, FunctionType<'a>>, } /// Possible ways to define a function in the text format. #[derive(Debug)] pub enum FuncKind<'a> { /// A function which is actually defined as an import, such as: /// /// ```text /// (func (type 3) (import "foo" "bar")) /// ``` Import(InlineImport<'a>), /// Almost all functions, those defined inline in a wasm module. Inline { /// The list of locals, if any, for this function. locals: Box<[Local<'a>]>, /// The instructions of the function. expression: Expression<'a>, }, } impl<'a> Parse<'a> for Func<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; let (ty, kind) = if let Some(import) = parser.parse()? { (parser.parse()?, FuncKind::Import(import)) } else { let ty = parser.parse()?; let locals = Local::parse_remainder(parser)?.into(); ( ty, FuncKind::Inline { locals, expression: parser.parse()?, }, ) }; Ok(Func { span, id, name, exports, ty, kind, }) } } /// A local for a `func` or `let` instruction. /// /// Each local has an optional identifier for name resolution, an optional name /// for the custom `name` section, and a value type. #[derive(Debug, Clone)] pub struct Local<'a> { /// An identifier that this local is resolved with (optionally) for name /// resolution. pub id: Option>, /// An optional name for this local stored in the custom `name` section. pub name: Option>, /// The value type of this local. pub ty: ValType<'a>, } /// Parser for `local` instruction. /// /// A single `local` instruction can generate multiple locals, hence this parser pub struct LocalParser<'a> { /// All the locals associated with this `local` instruction. pub locals: Vec>, } impl<'a> Parse<'a> for LocalParser<'a> { fn parse(parser: Parser<'a>) -> Result { let mut locals = Vec::new(); parser.parse::()?; if !parser.is_empty() { let id: Option<_> = parser.parse()?; let name: Option<_> = parser.parse()?; let ty = parser.parse()?; let parse_more = id.is_none() && name.is_none(); locals.push(Local { id, name, ty }); while parse_more && !parser.is_empty() { locals.push(Local { id: None, name: None, ty: parser.parse()?, }); } } Ok(LocalParser { locals }) } } impl<'a> Local<'a> { pub(crate) fn parse_remainder(parser: Parser<'a>) -> Result>> { let mut locals = Vec::new(); while parser.peek2::()? { parser.parens(|p| { locals.extend(p.parse::()?.locals); Ok(()) })?; } Ok(locals) } } wast-217.0.0/src/core/global.rs000064400000000000000000000033701046102023000143010ustar 00000000000000use 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: Span, /// An optional name to reference this global by pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: InlineExport<'a>, /// The type of this global, both its value type and whether it's mutable. pub ty: GlobalType<'a>, /// What kind of global this defined as. pub kind: GlobalKind<'a>, } /// Different kinds of globals that can be defined in a module. #[derive(Debug)] pub enum GlobalKind<'a> { /// A global which is actually defined as an import, such as: /// /// ```text /// (global i32 (import "foo" "bar")) /// ``` Import(InlineImport<'a>), /// A global defined inline in the module itself Inline(Expression<'a>), } impl<'a> Parse<'a> for Global<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; let (ty, kind) = if let Some(import) = parser.parse()? { (parser.parse()?, GlobalKind::Import(import)) } else { (parser.parse()?, GlobalKind::Inline(parser.parse()?)) }; Ok(Global { span, id, name, exports, ty, kind, }) } } wast-217.0.0/src/core/import.rs000064400000000000000000000112061046102023000143500ustar 00000000000000use 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: 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: &'a str, /// The item that's being imported. pub item: ItemSig<'a>, } impl<'a> Parse<'a> for Import<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let module = parser.parse()?; let field = parser.parse()?; let item = parser.parens(|p| p.parse())?; Ok(Import { span, module, field, item, }) } } #[derive(Debug, Clone)] #[allow(missing_docs)] pub struct ItemSig<'a> { /// Where this item is defined in the source. pub span: Span, /// An optional identifier used during name resolution to refer to this item /// from the rest of the module. pub id: Option>, /// An optional name which, for functions, will be stored in the /// custom `name` section. pub name: Option>, /// What kind of item this is. pub kind: ItemKind<'a>, } #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum ItemKind<'a> { Func(TypeUse<'a, FunctionType<'a>>), Table(TableType<'a>), Memory(MemoryType), Global(GlobalType<'a>), Tag(TagType<'a>), } impl<'a> Parse<'a> for ItemSig<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { let span = parser.parse::()?.0; Ok(ItemSig { span, id: parser.parse()?, name: parser.parse()?, kind: ItemKind::Func(parser.parse()?), }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(ItemSig { span, id: parser.parse()?, name: None, kind: ItemKind::Table(parser.parse()?), }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(ItemSig { span, id: parser.parse()?, name: None, kind: ItemKind::Memory(parser.parse()?), }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(ItemSig { span, id: parser.parse()?, name: None, kind: ItemKind::Global(parser.parse()?), }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(ItemSig { span, id: parser.parse()?, name: None, kind: ItemKind::Tag(parser.parse()?), }) } else { Err(l.error()) } } } /// A listing of a inline `(import "foo")` statement. /// /// Note that when parsing this type it is somewhat unconventional that it /// parses its own surrounding parentheses. This is typically an optional type, /// so it's so far been a bit nicer to have the optionality handled through /// `Peek` rather than `Option`. #[derive(Debug, Copy, Clone)] #[allow(missing_docs)] pub struct InlineImport<'a> { pub module: &'a str, pub field: &'a str, } impl<'a> Parse<'a> for InlineImport<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parens(|p| { p.parse::()?; Ok(InlineImport { module: p.parse()?, field: p.parse()?, }) }) } } impl Peek for InlineImport<'_> { fn peek(cursor: Cursor<'_>) -> Result { let cursor = match cursor.lparen()? { Some(cursor) => cursor, None => return Ok(false), }; let cursor = match cursor.keyword()? { Some(("import", cursor)) => cursor, _ => return Ok(false), }; let cursor = match cursor.string()? { Some((_, cursor)) => cursor, None => return Ok(false), }; let cursor = match cursor.string()? { Some((_, cursor)) => cursor, None => return Ok(false), }; Ok(cursor.rparen()?.is_some()) } fn display() -> &'static str { "inline import" } } wast-217.0.0/src/core/memory.rs000064400000000000000000000222501046102023000143470ustar 00000000000000use 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: Span, /// An optional name to refer to this memory by. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: InlineExport<'a>, /// How this memory is defined in the module. pub kind: MemoryKind<'a>, } /// Different syntactical ways a memory can be defined in a module. #[derive(Debug)] pub enum MemoryKind<'a> { /// This memory is actually an inlined import definition. #[allow(missing_docs)] Import { import: InlineImport<'a>, ty: MemoryType, }, /// A typical memory definition which simply says the limits of the memory Normal(MemoryType), /// The data of this memory, starting from 0, explicitly listed Inline { /// Whether or not this will be creating a 64-bit memory is64: bool, /// The inline data specified for this memory data: Vec>, }, } impl<'a> Parse<'a> for Memory<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; // Afterwards figure out which style this is, either: // // * `(import "a" "b") limits` // * `(data ...)` // * `limits` let mut l = parser.lookahead1(); let kind = if let Some(import) = parser.parse()? { MemoryKind::Import { import, ty: parser.parse()?, } } else if l.peek::()? || ((parser.peek::()? || parser.peek::()?) && parser.peek2::()?) { let is64 = if parser.parse::>()?.is_some() { false } else { parser.parse::>()?.is_some() }; let data = parser.parens(|parser| { parser.parse::()?; let mut data = Vec::new(); while !parser.is_empty() { data.push(parser.parse()?); } Ok(data) })?; MemoryKind::Inline { data, is64 } } else if l.peek::()? || l.peek::()? || l.peek::()? { MemoryKind::Normal(parser.parse()?) } else { return Err(l.error()); }; Ok(Memory { span, id, name, exports, kind, }) } } /// A `data` directive in a WebAssembly module. #[derive(Debug)] pub struct Data<'a> { /// Where this `data` was defined pub span: Span, /// The optional name of this data segment pub id: Option>, /// An optional name for this data stored in the custom `name` section. pub name: Option>, /// Whether this data segment is passive or active pub kind: DataKind<'a>, /// Bytes for this `Data` segment, viewed as the concatenation of all the /// contained slices. pub data: Vec>, } /// Different kinds of data segments, either passive or active. #[derive(Debug)] pub enum DataKind<'a> { /// A passive data segment which isn't associated with a memory and is /// referenced from various instructions. Passive, /// An active data segment which is associated and loaded into a particular /// memory on module instantiation. Active { /// The memory that this `Data` will be associated with. memory: Index<'a>, /// Initial offset to load this data segment at offset: Expression<'a>, }, } impl<'a> Parse<'a> for Data<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let kind = if parser.peek::<&[u8]>()? || parser.peek::()? { DataKind::Passive // ... and otherwise we must be attached to a particular memory as well // as having an initialization offset. } else { let memory = if parser.peek::()? { // 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::()? { parser.parens(|p| { p.parse::()?; p.parse() })? } else { Index::Num(0, span) }; let offset = parser.parens(|parser| { if parser.peek::()? { parser.parse::()?; parser.parse() } else { // This is all that the spec allows, which is that if // `offset` isn't present then this is "sugar" for a // single-instruction expression. let insn = parser.parse()?; if parser.is_empty() { return Ok(Expression::one(insn)); } // This is support for what is currently invalid syntax // according to the strict specification but is otherwise // present in the spec test suite: // // (data (i32.add (i32.const 0) (i32.const 0))) // // Technically the spec says this should be: // // (data (offset ...)) // // but alas let mut expr: Expression = parser.parse()?; let mut instrs = Vec::from(expr.instrs); instrs.push(insn); expr.instrs = instrs.into(); Ok(expr) } })?; DataKind::Active { memory, offset } }; let mut data = Vec::new(); while !parser.is_empty() { data.push(parser.parse()?); } Ok(Data { span, id, name, kind, data, }) } } /// Differnet ways the value of a data segment can be defined. #[derive(Debug)] #[allow(missing_docs)] pub enum DataVal<'a> { String(&'a [u8]), Integral(Vec), } 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) { 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 { if !parser.peek::()? { 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::(p, l, r, |u, v| v.push(u as u8))? || consume::(p, l, r, |u, v| v.extend(&u.to_le_bytes()))? || consume::(p, l, r, |u, v| v.extend(&u.to_le_bytes()))? || consume::(p, l, r, |u, v| v.extend(&u.to_le_bytes()))? || consume::(p, l, r, |u, v| v.extend(&u.bits.to_le_bytes()))? || consume::(p, l, r, |u, v| v.extend(&u.bits.to_le_bytes()))? || consume::(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, push: F, ) -> Result where F: Fn(U, &mut Vec), { if !lookahead.peek::()? { return Ok(false); } parser.parse::()?; while !parser.is_empty() { let val = parser.parse::()?; push(val, dst); } Ok(true) } } } wast-217.0.0/src/core/module.rs000064400000000000000000000167061046102023000143350ustar 00000000000000use crate::core::binary::EncodeOptions; use crate::core::*; use crate::parser::{Parse, Parser, Result}; use crate::token::{Id, Index, NameAnnotation, Span}; use crate::{annotation, kw}; pub use crate::core::resolve::Names; /// A parsed WebAssembly core module. #[derive(Debug)] pub struct Module<'a> { /// Where this `module` was defined pub span: Span, /// An optional identifier this module is known by pub id: Option>, /// An optional `@name` annotation for this module pub name: Option>, /// What kind of module this was parsed as. pub kind: ModuleKind<'a>, } /// The different kinds of ways to define a module. #[derive(Debug)] pub enum ModuleKind<'a> { /// A module defined in the textual s-expression format. Text(Vec>), /// A module that had its raw binary bytes defined via the `binary` /// directive. Binary(Vec<&'a [u8]>), } impl<'a> Module<'a> { /// Performs a name resolution pass on this [`Module`], resolving all /// symbolic names to indices. /// /// The WAT format contains a number of shorthands to make it easier to /// write, such as inline exports, inline imports, inline type definitions, /// etc. Additionally it allows using symbolic names such as `$foo` instead /// of using indices. This module will postprocess an AST to remove all of /// this syntactic sugar, preparing the AST for binary emission. This is /// where expansion and name resolution happens. /// /// This function will mutate the AST of this [`Module`] and replace all /// [`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 /// name lookups on the result you can do so as well. /// /// # Errors /// /// If an error happens during resolution, such a name resolution error or /// items are found in the wrong order, then an error is returned. pub fn resolve(&mut self) -> std::result::Result, crate::Error> { 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. /// /// This function will take the textual representation in [`Module`] and /// perform all steps necessary to convert it to a binary WebAssembly /// module, suitable for writing to a `*.wasm` file. This function may /// internally modify the [`Module`], for example: /// /// * Name resolution is performed to ensure that `Index::Id` isn't present /// anywhere in the AST. /// /// * Inline shorthands such as imports/exports/types are all expanded to be /// dedicated fields of the module. /// /// * Module fields may be shuffled around to preserve index ordering from /// expansions. /// /// After all of this expansion has happened the module will be converted to /// its binary form and returned as a `Vec`. 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, crate::Error> { EncodeOptions::default().encode_module(self) } pub(crate) fn validate(&self, parser: Parser<'_>) -> Result<()> { let mut starts = 0; if let ModuleKind::Text(fields) = &self.kind { for item in fields.iter() { if let ModuleField::Start(_) = item { starts += 1; } } } if starts > 1 { return Err(parser.error("multiple start sections found")); } Ok(()) } pub(crate) fn parse_without_module_keyword( module_keyword_span: Span, parser: Parser<'a>, ) -> Result { let id = parser.parse()?; let name = parser.parse()?; let kind = if parser.peek::()? { parser.parse::()?; let mut data = Vec::new(); while !parser.is_empty() { data.push(parser.parse()?); } ModuleKind::Binary(data) } else { ModuleKind::Text(ModuleField::parse_remaining(parser)?) }; Ok(Module { span: module_keyword_span, id, name, kind, }) } } impl<'a> Parse<'a> for Module<'a> { fn parse(parser: Parser<'a>) -> Result { parser.with_standard_annotations_registered(|parser| { let span = parser.parse::()?.0; Self::parse_without_module_keyword(span, parser) }) } } /// A listing of all possible fields that can make up a WebAssembly module. #[allow(missing_docs)] #[derive(Debug)] pub enum ModuleField<'a> { Type(Type<'a>), Rec(Rec<'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> { pub(crate) fn parse_remaining(parser: Parser<'a>) -> Result>> { let mut fields = Vec::new(); while !parser.is_empty() { fields.push(parser.parens(ModuleField::parse)?); } Ok(fields) } } impl<'a> Parse<'a> for ModuleField<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::>()? { return Ok(ModuleField::Type(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Rec(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Import(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Func(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Table(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Memory(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Global(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Export(parser.parse()?)); } if parser.peek::()? { parser.parse::()?; return Ok(ModuleField::Start(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Elem(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Data(parser.parse()?)); } if parser.peek::()? { return Ok(ModuleField::Tag(parser.parse()?)); } if parser.peek::()? || parser.peek::()? || parser.peek::()? { return Ok(ModuleField::Custom(parser.parse()?)); } Err(parser.error("expected valid module field")) } } wast-217.0.0/src/core/resolve/deinline_import_export.rs000064400000000000000000000217711046102023000213070ustar 00000000000000use crate::core::*; use crate::gensym; use crate::token::{Id, Index, Span}; use std::mem; pub fn run(fields: &mut Vec) { for mut item in mem::take(fields) { match &mut item { ModuleField::Func(f) => { for name in f.exports.names.drain(..) { fields.push(export(f.span, name, ExportKind::Func, &mut f.id)); } match f.kind { FuncKind::Import(import) => { item = ModuleField::Import(Import { span: f.span, module: import.module, field: import.field, item: ItemSig { span: f.span, id: f.id, name: f.name, kind: ItemKind::Func(f.ty.clone()), }, }); } FuncKind::Inline { .. } => {} } } ModuleField::Memory(m) => { for name in m.exports.names.drain(..) { fields.push(export(m.span, name, ExportKind::Memory, &mut m.id)); } match m.kind { MemoryKind::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: None, kind: ItemKind::Memory(ty), }, }); } // If data is defined inline insert an explicit `data` module // field here instead, switching this to a `Normal` memory. MemoryKind::Inline { is64, ref data } => { let len = data.iter().map(|l| l.len()).sum::() as u64; let pages = (len + default_page_size() - 1) / default_page_size(); let kind = MemoryKind::Normal(MemoryType { limits: Limits { is64, min: pages, max: Some(pages), }, shared: false, page_size_log2: None, }); let data = match mem::replace(&mut m.kind, kind) { MemoryKind::Inline { data, .. } => data, _ => unreachable!(), }; let id = gensym::fill(m.span, &mut m.id); fields.push(ModuleField::Data(Data { span: m.span, id: None, name: None, kind: DataKind::Active { memory: Index::Id(id), offset: Expression::one(if is64 { Instruction::I64Const(0) } else { Instruction::I32Const(0) }), }, data, })); } MemoryKind::Normal(_) => {} } } ModuleField::Table(t) => { for name in t.exports.names.drain(..) { fields.push(export(t.span, name, ExportKind::Table, &mut t.id)); } match &mut t.kind { TableKind::Import { import, ty } => { item = ModuleField::Import(Import { span: t.span, module: import.module, field: import.field, item: ItemSig { span: t.span, id: t.id, name: None, kind: ItemKind::Table(*ty), }, }); } // If data is defined inline insert an explicit `data` // module field here instead, switching this to a `Normal` // memory. TableKind::Inline { payload, elem, shared, is64, } => { let is64 = *is64; let len = match payload { ElemPayload::Indices(v) => v.len(), ElemPayload::Exprs { exprs, .. } => exprs.len(), }; let kind = TableKind::Normal { ty: TableType { limits: Limits { min: len as u64, max: Some(len as u64), is64, }, elem: *elem, shared: *shared, }, init_expr: None, }; let payload = match mem::replace(&mut t.kind, kind) { TableKind::Inline { payload, .. } => payload, _ => unreachable!(), }; let id = gensym::fill(t.span, &mut t.id); fields.push(ModuleField::Elem(Elem { span: t.span, id: None, name: None, kind: ElemKind::Active { table: Index::Id(id), offset: Expression::one(if is64 { Instruction::I64Const(0) } else { Instruction::I32Const(0) }), }, payload, })); } TableKind::Normal { .. } => {} } } ModuleField::Global(g) => { for name in g.exports.names.drain(..) { fields.push(export(g.span, name, ExportKind::Global, &mut g.id)); } match g.kind { GlobalKind::Import(import) => { item = ModuleField::Import(Import { span: g.span, module: import.module, field: import.field, item: ItemSig { span: g.span, id: g.id, name: None, kind: ItemKind::Global(g.ty), }, }); } GlobalKind::Inline { .. } => {} } } ModuleField::Tag(e) => { for name in e.exports.names.drain(..) { fields.push(export(e.span, name, ExportKind::Tag, &mut e.id)); } match e.kind { TagKind::Import(import) => { item = ModuleField::Import(Import { span: e.span, module: import.module, field: import.field, item: ItemSig { span: e.span, id: e.id, name: None, kind: ItemKind::Tag(e.ty.clone()), }, }); } TagKind::Inline { .. } => {} } } ModuleField::Import(_) | ModuleField::Type(_) | ModuleField::Rec(_) | ModuleField::Export(_) | ModuleField::Start(_) | ModuleField::Elem(_) | ModuleField::Data(_) | ModuleField::Custom(_) => {} } fields.push(item); } fn default_page_size() -> u64 { 1 << 16 } } fn export<'a>( span: Span, name: &'a str, kind: ExportKind, id: &mut Option>, ) -> ModuleField<'a> { let id = gensym::fill(span, id); ModuleField::Export(Export { span, name, kind, item: Index::Id(id), }) } wast-217.0.0/src/core/resolve/mod.rs000064400000000000000000000074101046102023000152760ustar 00000000000000use crate::core::*; use crate::token::Index; use crate::{gensym, Error}; mod deinline_import_export; mod names; pub(crate) mod types; pub(crate) use names::ResolveCoreType; #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub enum Ns { Func, Table, Global, Memory, Tag, Type, } pub fn resolve<'a>(fields: &mut Vec>) -> Result, 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(); // First up, de-inline import/export annotations. // // This ensures we only have to deal with inline definitions and to // calculate exports we only have to look for a particular kind of module // field. deinline_import_export::run(fields); // With a canonical form of imports make sure that imports are all listed // first. let mut last = None; for field in fields.iter() { match field { ModuleField::Import(i) => { if let Some(name) = last { return Err(Error::new(i.span, format!("import after {}", name))); } } ModuleField::Memory(_) => last = Some("memory"), ModuleField::Func(_) => last = Some("function"), ModuleField::Table(_) => last = Some("table"), ModuleField::Global(_) => last = Some("global"), _ => continue, } } // Expand all `TypeUse` annotations so all necessary `type` nodes are // present in the AST. types::expand(fields); // Perform name resolution over all `Index` items to resolve them all to // indices instead of symbolic names. 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::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>, } impl<'a> Names<'a> { /// Resolves `idx` within the function namespace. /// /// If `idx` is a `Num`, it is ignored, but if it's an `Id` then it will be /// looked up in the function namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_func(&self, idx: &mut Index<'a>) -> Result<(), Error> { self.resolver.resolve(idx, Ns::Func)?; Ok(()) } /// Resolves `idx` within the memory namespace. /// /// If `idx` is a `Num`, it is ignored, but if it's an `Id` then it will be /// looked up in the memory namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_memory(&self, idx: &mut Index<'a>) -> Result<(), Error> { self.resolver.resolve(idx, Ns::Memory)?; Ok(()) } /// Resolves `idx` within the table namespace. /// /// If `idx` is a `Num`, it is ignored, but if it's an `Id` then it will be /// looked up in the table namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_table(&self, idx: &mut Index<'a>) -> Result<(), Error> { self.resolver.resolve(idx, Ns::Table)?; Ok(()) } /// Resolves `idx` within the global namespace. /// /// If `idx` is a `Num`, it is ignored, but if it's an `Id` then it will be /// looked up in the global namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_global(&self, idx: &mut Index<'a>) -> Result<(), Error> { self.resolver.resolve(idx, Ns::Global)?; Ok(()) } } wast-217.0.0/src/core/resolve/names.rs000064400000000000000000000704601046102023000156270ustar 00000000000000use 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; pub fn resolve<'a>(fields: &mut Vec>) -> Result, Error> { let mut resolver = Resolver::default(); resolver.process(fields)?; Ok(resolver) } /// Context structure used to perform name resolution. #[derive(Default)] pub struct Resolver<'a> { // Namespaces within each module. 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 module and inject // type annotations if necessary. funcs: Namespace<'a>, globals: Namespace<'a>, tables: Namespace<'a>, memories: Namespace<'a>, types: Namespace<'a>, tags: Namespace<'a>, datas: Namespace<'a>, elems: Namespace<'a>, fields: HashMap>, type_info: Vec>, } impl<'a> Resolver<'a> { fn process(&mut self, fields: &mut Vec>) -> Result<(), Error> { // Number everything in the module, recording what names correspond to // what indices. for field in fields.iter_mut() { self.register(field)?; } // 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)?; } Ok(()) } fn register_type(&mut self, ty: &Type<'a>) -> Result<(), Error> { let type_index = self.types.register(ty.id, "type")?; match &ty.def.kind { // For GC structure types we need to be sure to populate the // field namespace here as well. // // The field namespace is relative to the struct fields are defined in InnerTypeKind::Struct(r#struct) => { for (i, field) in r#struct.fields.iter().enumerate() { if let Some(id) = field.id { self.fields .entry(type_index) .or_insert(Namespace::default()) .register_specific(id, i as u32, "field")?; } } } InnerTypeKind::Array(_) | InnerTypeKind::Func(_) => {} } // Record function signatures as we see them to so we can // generate errors for mismatches in references such as // `call_indirect`. match &ty.def.kind { InnerTypeKind::Func(f) => { let params = f.params.iter().map(|p| p.2).collect(); let results = f.results.clone(); self.type_info.push(TypeInfo::Func { params, results }); } _ => self.type_info.push(TypeInfo::Other), } Ok(()) } fn register(&mut self, item: &ModuleField<'a>) -> Result<(), Error> { match item { 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::Type(i) => { return self.register_type(i); } ModuleField::Rec(i) => { for ty in &i.types { self.register_type(ty)?; } return Ok(()); } 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")?, // These fields don't define any items in any index space. ModuleField::Export(_) | ModuleField::Start(_) | ModuleField::Custom(_) => { return Ok(()) } }; Ok(()) } fn resolve_field(&self, field: &mut ModuleField<'a>) -> Result<(), Error> { match field { ModuleField::Import(i) => { self.resolve_item_sig(&mut i.item)?; Ok(()) } ModuleField::Type(ty) => self.resolve_type(ty), ModuleField::Rec(rec) => { for ty in &mut rec.types { self.resolve_type(ty)?; } Ok(()) } ModuleField::Func(f) => { let (idx, inline) = self.resolve_type_use(&mut f.ty)?; let n = match idx { Index::Num(n, _) => *n, Index::Id(_) => panic!("expected `Num`"), }; if let FuncKind::Inline { locals, expression } = &mut f.kind { // Resolve (ref T) in locals for local in locals.iter_mut() { self.resolve_valtype(&mut local.ty)?; } // Build a scope with a local namespace for the function // body let mut scope = Namespace::default(); // Parameters come first in the scope... if let Some(inline) = &inline { for (id, _, _) in inline.params.iter() { scope.register(*id, "local")?; } } else if let Some(TypeInfo::Func { params, .. }) = self.type_info.get(n as usize) { for _ in 0..params.len() { scope.register(None, "local")?; } } // .. followed by locals themselves for local in locals.iter() { scope.register(local.id, "local")?; } // Initialize the expression resolver with this scope let mut resolver = ExprResolver::new(self, scope); // and then we can resolve the expression! resolver.resolve(expression)?; // specifically save the original `sig`, if it was present, // because that's what we're using for local names. f.ty.inline = inline; } Ok(()) } ModuleField::Elem(e) => { match &mut e.kind { ElemKind::Active { table, offset } => { self.resolve(table, Ns::Table)?; self.resolve_expr(offset)?; } ElemKind::Passive { .. } | ElemKind::Declared { .. } => {} } match &mut e.payload { ElemPayload::Indices(elems) => { for idx in elems { self.resolve(idx, Ns::Func)?; } } ElemPayload::Exprs { exprs, ty } => { for expr in exprs { self.resolve_expr(expr)?; } self.resolve_heaptype(&mut ty.heap)?; } } Ok(()) } ModuleField::Data(d) => { if let DataKind::Active { memory, offset } = &mut d.kind { self.resolve(memory, Ns::Memory)?; self.resolve_expr(offset)?; } Ok(()) } ModuleField::Start(i) => { self.resolve(i, Ns::Func)?; Ok(()) } ModuleField::Export(e) => { 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, }, )?; Ok(()) } ModuleField::Global(g) => { self.resolve_valtype(&mut g.ty.ty)?; if let GlobalKind::Inline(expr) = &mut g.kind { self.resolve_expr(expr)?; } Ok(()) } ModuleField::Tag(t) => { match &mut t.ty { TagType::Exception(ty) => { self.resolve_type_use(ty)?; } } Ok(()) } ModuleField::Table(t) => { if let TableKind::Normal { ty, init_expr } = &mut t.kind { self.resolve_heaptype(&mut ty.elem.heap)?; if let Some(init_expr) = init_expr { self.resolve_expr(init_expr)?; } } Ok(()) } ModuleField::Memory(_) | ModuleField::Custom(_) => Ok(()), } } fn resolve_item_sig(&self, item: &mut ItemSig<'a>) -> Result<(), Error> { match &mut item.kind { ItemKind::Func(t) | ItemKind::Tag(TagType::Exception(t)) => { self.resolve_type_use(t)?; } ItemKind::Global(t) => self.resolve_valtype(&mut t.ty)?, ItemKind::Table(t) => { self.resolve_heaptype(&mut t.elem.heap)?; } ItemKind::Memory(_) => {} } Ok(()) } fn resolve_type_use<'b, T>( &self, ty: &'b mut TypeUse<'a, T>, ) -> Result<(&'b Index<'a>, Option), Error> where T: TypeReference<'a>, { let idx = ty.index.as_mut().unwrap(); 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. // // Note that we resolve the type first to transform all names to // indices to ensure that all the indices line up. if let Some(inline) = &mut ty.inline { inline.resolve(self)?; inline.check_matches(idx, self)?; } Ok((idx, ty.inline.take())) } fn resolve_expr(&self, expr: &mut Expression<'a>) -> Result<(), Error> { ExprResolver::new(self, Namespace::default()).resolve(expr) } pub fn resolve(&self, idx: &mut Index<'a>, ns: Ns) -> Result { 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"), } } } #[derive(Debug, Clone)] struct ExprBlock<'a> { // The label of the block label: Option>, // Whether this block pushed a new scope for resolving locals pushed_scope: bool, } struct ExprResolver<'a, 'b> { resolver: &'b Resolver<'a>, // Scopes tracks the local namespace and dynamically grows as we enter/exit // `let` blocks scopes: Vec>, blocks: Vec>, } impl<'a, 'b> ExprResolver<'a, 'b> { fn new(resolver: &'b Resolver<'a>, initial_scope: Namespace<'a>) -> ExprResolver<'a, 'b> { ExprResolver { resolver, scopes: vec![initial_scope], blocks: Vec::new(), } } fn resolve(&mut self, expr: &mut Expression<'a>) -> Result<(), Error> { for instr in expr.instrs.iter_mut() { self.resolve_instr(instr)?; } Ok(()) } fn resolve_block_type(&mut self, bt: &mut BlockType<'a>) -> Result<(), Error> { // If the index is specified on this block type then that's the source // of resolution and the resolver step here will verify the inline type // matches. Note that indexes may come from the source text itself but // may also come from being injected as part of the type expansion phase // of resolution. // // If no type is present then that means that the inline type is not // present or has 0-1 results. In that case the nested value types are // resolved, if they're there, to get encoded later on. if bt.ty.index.is_some() { self.resolver.resolve_type_use(&mut bt.ty)?; } else if let Some(inline) = &mut bt.ty.inline { inline.resolve(self.resolver)?; } Ok(()) } fn resolve_instr(&mut self, instr: &mut Instruction<'a>) -> Result<(), Error> { use Instruction::*; if let Some(m) = instr.memarg_mut() { self.resolver.resolve(&mut m.memory, Ns::Memory)?; } match instr { MemorySize(i) | MemoryGrow(i) | MemoryFill(i) | MemoryDiscard(i) => { self.resolver.resolve(&mut i.mem, Ns::Memory)?; } MemoryInit(i) => { self.resolver.datas.resolve(&mut i.data, "data")?; self.resolver.resolve(&mut i.mem, Ns::Memory)?; } MemoryCopy(i) => { self.resolver.resolve(&mut i.src, Ns::Memory)?; self.resolver.resolve(&mut i.dst, Ns::Memory)?; } DataDrop(i) => { self.resolver.datas.resolve(i, "data")?; } TableInit(i) => { self.resolver.elems.resolve(&mut i.elem, "elem")?; self.resolver.resolve(&mut i.table, Ns::Table)?; } ElemDrop(i) => { self.resolver.elems.resolve(i, "elem")?; } TableCopy(i) => { 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(&mut i.dst, Ns::Table)?; } TableAtomicGet(i) | TableAtomicSet(i) | TableAtomicRmwXchg(i) | TableAtomicRmwCmpxchg(i) => { self.resolver.resolve(&mut i.inner.dst, Ns::Table)?; } GlobalSet(i) | GlobalGet(i) => { self.resolver.resolve(i, Ns::Global)?; } GlobalAtomicSet(i) | GlobalAtomicGet(i) | GlobalAtomicRmwAdd(i) | GlobalAtomicRmwSub(i) | GlobalAtomicRmwAnd(i) | GlobalAtomicRmwOr(i) | GlobalAtomicRmwXor(i) | GlobalAtomicRmwXchg(i) | GlobalAtomicRmwCmpxchg(i) => { self.resolver.resolve(&mut i.inner, Ns::Global)?; } LocalSet(i) | LocalGet(i) | LocalTee(i) => { assert!(self.scopes.len() > 0); // Resolve a local by iterating over scopes from most recent // to less recent. This allows locals added by `let` blocks to // shadow less recent locals. for (depth, scope) in self.scopes.iter().enumerate().rev() { if let Err(e) = scope.resolve(i, "local") { if depth == 0 { // There are no more scopes left, report this as // the result return Err(e); } } else { break; } } // We must have taken the `break` and resolved the local assert!(i.is_resolved()); } Call(i) | RefFunc(i) | ReturnCall(i) => { self.resolver.resolve(i, Ns::Func)?; } CallIndirect(c) | ReturnCallIndirect(c) => { self.resolver.resolve(&mut c.table, Ns::Table)?; self.resolver.resolve_type_use(&mut c.ty)?; } CallRef(i) | ReturnCallRef(i) => { self.resolver.resolve(i, Ns::Type)?; } Block(bt) | If(bt) | Loop(bt) | Try(bt) => { self.blocks.push(ExprBlock { label: bt.label, pushed_scope: false, }); self.resolve_block_type(bt)?; } TryTable(try_table) => { self.resolve_block_type(&mut try_table.block)?; for catch in &mut try_table.catches { if let Some(tag) = catch.kind.tag_index_mut() { self.resolver.resolve(tag, Ns::Tag)?; } self.resolve_label(&mut catch.label)?; } self.blocks.push(ExprBlock { label: try_table.block.label, pushed_scope: false, }); } // On `End` instructions we pop a label from the stack, and for both // `End` and `Else` instructions if they have labels listed we // verify that they match the label at the beginning of the block. Else(_) | End(_) => { let (matching_block, label) = match &instr { Else(label) => (self.blocks.last().cloned(), label), End(label) => (self.blocks.pop(), label), _ => unreachable!(), }; let matching_block = match matching_block { Some(l) => l, None => return Ok(()), }; // Reset the local scopes to before this block was entered if matching_block.pushed_scope { if let End(_) = instr { self.scopes.pop(); } } let label = match label { Some(l) => l, None => return Ok(()), }; if Some(*label) == matching_block.label { return Ok(()); } return Err(Error::new( label.span(), "mismatching labels between end and block".to_string(), )); } Br(i) | BrIf(i) | BrOnNull(i) | BrOnNonNull(i) => { self.resolve_label(i)?; } BrTable(i) => { for label in i.labels.iter_mut() { self.resolve_label(label)?; } self.resolve_label(&mut i.default)?; } Throw(i) | Catch(i) => { self.resolver.resolve(i, Ns::Tag)?; } Rethrow(i) => { self.resolve_label(i)?; } Delegate(i) => { // Since a delegate starts counting one layer out from the // current try-delegate block, we pop before we resolve labels. self.blocks.pop(); self.resolve_label(i)?; } Select(s) => { if let Some(list) = &mut s.tys { for ty in list { self.resolver.resolve_valtype(ty)?; } } } RefTest(i) => { self.resolver.resolve_reftype(&mut i.r#type)?; } RefCast(i) => { self.resolver.resolve_reftype(&mut i.r#type)?; } BrOnCast(i) => { self.resolve_label(&mut i.label)?; self.resolver.resolve_reftype(&mut i.to_type)?; self.resolver.resolve_reftype(&mut i.from_type)?; } BrOnCastFail(i) => { self.resolve_label(&mut i.label)?; self.resolver.resolve_reftype(&mut i.to_type)?; self.resolver.resolve_reftype(&mut i.from_type)?; } StructNew(i) | StructNewDefault(i) | ArrayNew(i) | ArrayNewDefault(i) | ArrayGet(i) | ArrayGetS(i) | ArrayGetU(i) | ArraySet(i) => { self.resolver.resolve(i, Ns::Type)?; } StructSet(s) | StructGet(s) | StructGetS(s) | StructGetU(s) => { self.resolve_field(s)?; } StructAtomicGet(s) | StructAtomicGetS(s) | StructAtomicGetU(s) | StructAtomicSet(s) | StructAtomicRmwAdd(s) | StructAtomicRmwSub(s) | StructAtomicRmwAnd(s) | StructAtomicRmwOr(s) | StructAtomicRmwXor(s) | StructAtomicRmwXchg(s) | StructAtomicRmwCmpxchg(s) => { self.resolve_field(&mut s.inner)?; } ArrayNewFixed(a) => { self.resolver.resolve(&mut a.array, Ns::Type)?; } ArrayNewData(a) => { self.resolver.resolve(&mut a.array, Ns::Type)?; self.resolver.datas.resolve(&mut a.data_idx, "data")?; } ArrayNewElem(a) => { self.resolver.resolve(&mut a.array, Ns::Type)?; self.resolver.elems.resolve(&mut a.elem_idx, "elem")?; } ArrayFill(a) => { self.resolver.resolve(&mut a.array, Ns::Type)?; } ArrayCopy(a) => { self.resolver.resolve(&mut a.dest_array, Ns::Type)?; self.resolver.resolve(&mut a.src_array, Ns::Type)?; } ArrayInitData(a) => { self.resolver.resolve(&mut a.array, Ns::Type)?; self.resolver.datas.resolve(&mut a.segment, "data")?; } ArrayInitElem(a) => { self.resolver.resolve(&mut a.array, Ns::Type)?; self.resolver.elems.resolve(&mut a.segment, "elem")?; } ArrayAtomicGet(i) | ArrayAtomicGetS(i) | ArrayAtomicGetU(i) | ArrayAtomicSet(i) | ArrayAtomicRmwAdd(i) | ArrayAtomicRmwSub(i) | ArrayAtomicRmwAnd(i) | ArrayAtomicRmwOr(i) | ArrayAtomicRmwXor(i) | ArrayAtomicRmwXchg(i) | ArrayAtomicRmwCmpxchg(i) => { self.resolver.resolve(&mut i.inner, Ns::Type)?; } RefNull(ty) => self.resolver.resolve_heaptype(ty)?, _ => {} } Ok(()) } fn resolve_label(&self, label: &mut Index<'a>) -> Result<(), Error> { let id = match label { Index::Num(..) => return Ok(()), Index::Id(id) => *id, }; let idx = self .blocks .iter() .rev() .enumerate() .filter_map(|(i, b)| b.label.map(|l| (i, l))) .find(|(_, l)| *l == id); match idx { Some((idx, _)) => { *label = Index::Num(idx as u32, id.span()); Ok(()) } None => Err(resolve_error(id, "label")), } } fn resolve_field(&self, s: &mut StructAccess<'a>) -> Result<(), Error> { let type_index = self.resolver.resolve(&mut s.r#struct, Ns::Type)?; if let Index::Id(field_id) = s.field { self.resolver .fields .get(&type_index) .ok_or(Error::new(field_id.span(), format!("accessing a named field `{}` in a struct without named fields, type index {}", field_id.name(), type_index)))? .resolve(&mut s.field, "field")?; } Ok(()) } } enum TypeInfo<'a> { Func { params: Box<[ValType<'a>]>, results: Box<[ValType<'a>]>, }, Other, } trait TypeReference<'a> { fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error>; fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error>; } impl<'a> TypeReference<'a> for FunctionType<'a> { fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error> { let n = match idx { Index::Num(n, _) => *n, Index::Id(_) => panic!("expected `Num`"), }; let (params, results) = match cx.type_info.get(n as usize) { Some(TypeInfo::Func { params, results }) => (params, results), _ => return Ok(()), }; // Here we need to check that the inline type listed (ourselves) matches // what was listed in the module itself (the `params` and `results` // above). The listed values in `types` are not resolved yet, although // we should be resolved. In any case we do name resolution // opportunistically here to see if the values are equal. let types_not_equal = |a: &ValType, b: &ValType| { let mut a = *a; let mut b = *b; drop(cx.resolve_valtype(&mut a)); drop(cx.resolve_valtype(&mut b)); a != b }; let not_equal = params.len() != self.params.len() || results.len() != self.results.len() || params .iter() .zip(self.params.iter()) .any(|(a, (_, _, b))| types_not_equal(a, b)) || results .iter() .zip(self.results.iter()) .any(|(a, b)| types_not_equal(a, b)); if not_equal { return Err(Error::new( idx.span(), format!("inline function type doesn't match type reference"), )); } Ok(()) } fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> { cx.resolve_type_func(self) } } pub(crate) trait ResolveCoreType<'a> { fn resolve_type_name(&self, name: &mut Index<'a>) -> Result; fn resolve_type(&self, ty: &mut Type<'a>) -> Result<(), Error> { self.resolve_type_def(&mut ty.def)?; if let Some(parent) = &mut ty.parent { self.resolve_type_name(parent)?; } Ok(()) } fn resolve_type_def(&self, ty: &mut TypeDef<'a>) -> Result<(), Error> { match &mut ty.kind { InnerTypeKind::Func(func) => self.resolve_type_func(func), InnerTypeKind::Struct(struct_) => { for field in &mut struct_.fields { self.resolve_storagetype(&mut field.ty)?; } Ok(()) } InnerTypeKind::Array(array) => self.resolve_storagetype(&mut array.ty), } } fn resolve_type_func(&self, ty: &mut FunctionType<'a>) -> Result<(), Error> { // Resolve the (ref T) value types in the final function type for param in ty.params.iter_mut() { self.resolve_valtype(&mut param.2)?; } for result in ty.results.iter_mut() { self.resolve_valtype(result)?; } Ok(()) } fn resolve_valtype(&self, ty: &mut ValType<'a>) -> Result<(), Error> { match ty { ValType::Ref(ty) => self.resolve_reftype(ty), ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => Ok(()), } } fn resolve_reftype(&self, ty: &mut RefType<'a>) -> Result<(), Error> { self.resolve_heaptype(&mut ty.heap) } fn resolve_heaptype(&self, ty: &mut HeapType<'a>) -> Result<(), Error> { match ty { HeapType::Concrete(i) => { self.resolve_type_name(i)?; } HeapType::Abstract { .. } => {} } Ok(()) } fn resolve_storagetype(&self, ty: &mut StorageType<'a>) -> Result<(), Error> { match ty { StorageType::Val(ty) => self.resolve_valtype(ty), StorageType::I8 | StorageType::I16 => Ok(()), } } } impl<'a> ResolveCoreType<'a> for Resolver<'a> { fn resolve_type_name(&self, name: &mut Index<'a>) -> Result { self.resolve(name, Ns::Type) } } wast-217.0.0/src/core/resolve/types.rs000064400000000000000000000225141046102023000156650ustar 00000000000000use crate::core::*; use crate::gensym; use crate::token::{Index, Span}; use std::collections::HashMap; pub fn expand<'a>(fields: &mut Vec>) { let mut expander = Expander::default(); expander.process(fields); } #[derive(Default)] 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, Index<'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>, } impl<'a> Expander<'a> { fn process(&mut self, fields: &mut Vec>) { // 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. let mut cur = 0; while cur < fields.len() { self.expand_header(&mut fields[cur]); for item in self.to_prepend.drain(..) { fields.insert(cur, item); cur += 1; } cur += 1; } // Next after we've done that we expand remaining fields. Note that // after this we actually append instead of prepend. This is because // injected types are intended to come at the end of the type section // and types will be sorted before all other items processed here in the // final module anyway. for field in fields.iter_mut() { self.expand(field); } fields.append(&mut self.to_prepend); } fn expand_header(&mut self, item: &mut ModuleField<'a>) { match item { ModuleField::Type(ty) => { let id = gensym::fill(ty.span, &mut ty.id); match &mut ty.def.kind { InnerTypeKind::Func(f) => { f.key().insert(self, Index::Id(id)); } InnerTypeKind::Array(_) | InnerTypeKind::Struct(_) => {} } } _ => {} } } fn expand(&mut self, item: &mut ModuleField<'a>) { match item { // This is pre-expanded above ModuleField::Type(_) => {} ModuleField::Rec(_) => {} ModuleField::Import(i) => { self.expand_item_sig(&mut i.item); } ModuleField::Func(f) => { self.expand_type_use(&mut f.ty); if let FuncKind::Inline { expression, .. } = &mut f.kind { self.expand_expression(expression); } } ModuleField::Global(g) => { if let GlobalKind::Inline(expr) = &mut g.kind { self.expand_expression(expr); } } ModuleField::Data(d) => { if let DataKind::Active { offset, .. } = &mut d.kind { self.expand_expression(offset); } } ModuleField::Elem(e) => { if let ElemKind::Active { offset, .. } = &mut e.kind { self.expand_expression(offset); } if let ElemPayload::Exprs { exprs, .. } = &mut e.payload { for expr in exprs { self.expand_expression(expr); } } } ModuleField::Tag(t) => match &mut t.ty { TagType::Exception(ty) => { self.expand_type_use(ty); } }, ModuleField::Table(t) => match &mut t.kind { TableKind::Normal { init_expr, .. } => { if let Some(expr) = init_expr { self.expand_expression(expr); } } TableKind::Import { .. } | TableKind::Inline { .. } => {} }, ModuleField::Memory(_) | ModuleField::Start(_) | ModuleField::Export(_) | ModuleField::Custom(_) => {} } } fn expand_item_sig(&mut self, item: &mut ItemSig<'a>) { match &mut item.kind { ItemKind::Func(t) | ItemKind::Tag(TagType::Exception(t)) => { self.expand_type_use(t); } ItemKind::Global(_) | ItemKind::Table(_) | ItemKind::Memory(_) => {} } } fn expand_expression(&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>) { match instr { Instruction::Block(bt) | Instruction::If(bt) | Instruction::Loop(bt) | Instruction::Try(bt) | Instruction::TryTable(TryTable { block: bt, .. }) => { // No expansion necessary, a type reference is already here. // We'll verify that it's the same as the inline type, if any, // later. if bt.ty.index.is_some() { return; } match &bt.ty.inline { // Only actually expand `TypeUse` with an index which appends a // type if it looks like we need one. This way if the // multi-value proposal isn't enabled and/or used we won't // encode it. Some(inline) => { if inline.params.len() == 0 && inline.results.len() <= 1 { return; } } // If we didn't have either an index or an inline type // listed then assume our block has no inputs/outputs, so // fill in the inline type here. // // Do not fall through to expanding the `TypeUse` because // this doesn't force an empty function type to go into the // type section. None => { bt.ty.inline = Some(FunctionType::default()); return; } } self.expand_type_use(&mut bt.ty); } Instruction::CallIndirect(c) | Instruction::ReturnCallIndirect(c) => { self.expand_type_use(&mut c.ty); } _ => {} } } fn expand_type_use(&mut self, item: &mut TypeUse<'a, T>) -> Index<'a> where T: TypeReference<'a>, { if let Some(idx) = &item.index { return *idx; } let key = match item.inline.as_mut() { Some(ty) => { ty.expand(self); ty.key() } None => T::default().key(), }; let span = Span::from_offset(0); // FIXME(#613): don't manufacture let idx = self.key_to_idx(span, key); item.index = Some(idx); idx } fn key_to_idx(&mut self, span: Span, key: impl TypeKey<'a>) -> Index<'a> { // First see if this `key` already exists in the type definitions we've // seen so far... if let Some(idx) = key.lookup(self) { return idx; } // ... and failing that we insert a new type definition. let id = gensym::gen(span); self.to_prepend.push(ModuleField::Type(Type { span, id: Some(id), name: None, // Currently, there is no way in the WebAssembly text format to mark // a function `shared` inline; a `shared` function must use an // explicit type index, e.g., `(func (type $ft))`. def: key.to_def(span, /* shared = */ false), parent: None, final_type: None, })); let idx = Index::Id(id); key.insert(self, idx); idx } } pub(crate) trait TypeReference<'a>: Default { type Key: TypeKey<'a>; fn key(&self) -> Self::Key; fn expand(&mut self, cx: &mut Expander<'a>); } pub(crate) trait TypeKey<'a> { fn lookup(&self, cx: &Expander<'a>) -> Option>; fn to_def(&self, span: Span, shared: bool) -> TypeDef<'a>; fn insert(&self, cx: &mut Expander<'a>, id: Index<'a>); } pub(crate) type FuncKey<'a> = (Box<[ValType<'a>]>, Box<[ValType<'a>]>); impl<'a> TypeReference<'a> for FunctionType<'a> { type Key = FuncKey<'a>; fn key(&self) -> Self::Key { let params = self.params.iter().map(|p| p.2).collect(); let results = self.results.clone(); (params, results) } fn expand(&mut self, _cx: &mut Expander<'a>) {} } impl<'a> TypeKey<'a> for FuncKey<'a> { fn lookup(&self, cx: &Expander<'a>) -> Option> { cx.func_type_to_idx.get(self).cloned() } fn to_def(&self, _span: Span, shared: bool) -> TypeDef<'a> { TypeDef { kind: InnerTypeKind::Func(FunctionType { params: self.0.iter().map(|t| (None, None, *t)).collect(), results: self.1.clone(), }), shared, } } fn insert(&self, cx: &mut Expander<'a>, idx: Index<'a>) { cx.func_type_to_idx.entry(self.clone()).or_insert(idx); } } wast-217.0.0/src/core/table.rs000064400000000000000000000243151046102023000141320ustar 00000000000000use crate::core::*; use crate::kw; use crate::parser::{Parse, Parser, Peek, 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: Span, /// An optional name to refer to this table by. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// If present, inline export annotations which indicate names this /// definition should be exported under. pub exports: InlineExport<'a>, /// How this table is textually defined in the module. pub kind: TableKind<'a>, } /// Different ways to textually define a table. #[derive(Debug)] pub enum TableKind<'a> { /// This table is actually an inlined import definition. #[allow(missing_docs)] Import { import: InlineImport<'a>, ty: TableType<'a>, }, /// A typical memory definition which simply says the limits of the table. Normal { /// Table type. ty: TableType<'a>, /// Optional items initializer expression. init_expr: Option>, }, /// The elem segments of this table, starting from 0, explicitly listed. Inline { /// The element type of this table. elem: RefType<'a>, /// Whether or not this will be creating a 64-bit table. is64: bool, /// Whether this table is shared or not. shared: bool, /// The element table entries to have, and the length of this list is /// the limits of the table as well. payload: ElemPayload<'a>, }, } impl<'a> Parse<'a> for Table<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; // Afterwards figure out which style this is, either: // // * inline: `elemtype (elem ...)` // * normal: `tabletype` // * import: `(import "a" "b") tabletype` // // Where `tabletype := shared? index_type limits reftype` let mut l = parser.lookahead1(); let is_shared = l.peek::()?; let has_index_type = l.peek::()? | l.peek::()?; let kind = if l.peek::()? || ((is_shared || has_index_type) && parser.peek2::()?) { let shared = parser.parse::>()?.is_some(); let is64 = if parser.parse::>()?.is_some() { false } else { parser.parse::>()?.is_some() }; let elem = parser.parse()?; let payload = parser.parens(|p| { p.parse::()?; if p.peek::()? { ElemPayload::parse_exprs(p, elem) } else { ElemPayload::parse_indices(p, Some(elem)) } })?; TableKind::Inline { elem, is64, shared, payload, } } else if is_shared || has_index_type || l.peek::()? { TableKind::Normal { ty: parser.parse()?, init_expr: if !parser.is_empty() { Some(parser.parse::()?) } else { None }, } } else if let Some(import) = parser.parse()? { TableKind::Import { import, ty: parser.parse()?, } } else { return Err(l.error()); }; Ok(Table { span, id, name, exports, kind, }) } } /// An `elem` segment in a WebAssembly module. #[derive(Debug)] pub struct Elem<'a> { /// Where this `elem` was defined. pub span: Span, /// An optional name by which to refer to this segment. pub id: Option>, /// An optional name for this element stored in the custom `name` section. pub name: Option>, /// The way this segment was defined in the module. pub kind: ElemKind<'a>, /// The payload of this element segment, typically a list of functions. pub payload: ElemPayload<'a>, } /// Different ways to define an element segment in an mdoule. #[derive(Debug)] pub enum ElemKind<'a> { /// A passive segment that isn't associated with a table and can be used in /// various bulk-memory instructions. Passive, /// A declared element segment that is purely used to declare function /// references. Declared, /// An active segment associated with a table. Active { /// The table this `elem` is initializing. table: Index<'a>, /// The offset within `table` that we'll initialize at. offset: Expression<'a>, }, } /// Different ways to define the element segment payload in a module. #[derive(Debug)] pub enum ElemPayload<'a> { /// This element segment has a contiguous list of function indices Indices(Vec>), /// 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: RefType<'a>, /// The expressions in this segment. exprs: Vec>, }, } impl<'a> Parse<'a> for Elem<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; // Element segments can start in a number of different ways: // // * `(elem ...` // * `(elem declare ...` // * `(elem (table ...` // * `(elem (offset ...` // * `(elem ( ...` (omitted `offset`) let mut table_omitted = false; let kind = if parser.peek::()? { parser.parse::()?; ElemKind::Declared } else if parser.peek::()? || (parser.peek::()? && !parser.peek::()?) { let table = if parser.peek::()? { // 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. table_omitted = true; Index::Num(parser.parse()?, span) } else if parser.peek2::()? { parser.parens(|p| { p.parse::()?; p.parse() })? } else { table_omitted = true; Index::Num(0, span) }; let offset = parse_expr_or_single_instr::(parser)?; ElemKind::Active { table, offset } } else { ElemKind::Passive }; // Element segments can have a number of ways to specify their element // lists: // // * `func 0 1 ...` - list of indices // * ` (ref.null func) ...` - list of expressions // * `0 1 ...` - list of indices, only if the table was omitted for the // legacy way tables were printed. let indices = if parser.peek::()? { parser.parse::()?; true } else if parser.peek::()? { false } else if table_omitted { true } else { false // this will fall through to failing to parse a `RefType` }; let payload = if indices { ElemPayload::parse_indices(parser, None)? } else { let ty = parser.parse()?; ElemPayload::parse_exprs(parser, ty)? }; Ok(Elem { span, id, name, kind, payload, }) } } impl<'a> ElemPayload<'a> { fn parse_indices(parser: Parser<'a>, ty: Option>) -> Result { let mut ret = match ty { // If there is no requested type, then it's ok to parse a list of // indices. None => ElemPayload::Indices(Vec::new()), // If the requested type is a `funcref` type then a list of indices // can be parsed. This is because the list-of-indices encoding in // the binary format can only accomodate the `funcref` type. Some(ty) if ty == RefType::func() => ElemPayload::Indices(Vec::new()), // Otherwise silently translate this list-of-indices into a // list-of-expressions because that's the only way to accomodate a // non-funcref type. Some(ty) => ElemPayload::Exprs { ty, exprs: Vec::new(), }, }; while !parser.is_empty() { let func = parser.parse()?; match &mut ret { ElemPayload::Indices(list) => list.push(func), ElemPayload::Exprs { exprs, .. } => { let expr = Expression::one(Instruction::RefFunc(func)); exprs.push(expr); } } } Ok(ret) } fn parse_exprs(parser: Parser<'a>, ty: RefType<'a>) -> Result { let mut exprs = Vec::new(); while !parser.is_empty() { let expr = parse_expr_or_single_instr::(parser)?; exprs.push(expr); } Ok(ElemPayload::Exprs { exprs, ty }) } } // Parses either `(T expr)` or `(instr)`, returning the resulting expression. fn parse_expr_or_single_instr<'a, T>(parser: Parser<'a>) -> Result> where T: Parse<'a> + Peek, { if parser.peek2::()? { parser.parens(|parser| { parser.parse::()?; parser.parse() }) } else { // Without `T` this is "sugar" for a single instruction (still possibly folded). Ok(Expression::parse_folded_instruction(parser)?) } } wast-217.0.0/src/core/tag.rs000064400000000000000000000040641046102023000136150ustar 00000000000000use 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: Span, /// An optional name by which to refer to this tag in name resolution. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// Optional export directives for this tag. pub exports: InlineExport<'a>, /// The type of tag that is defined. pub ty: TagType<'a>, /// What kind of tag this is defined as. pub kind: TagKind<'a>, } /// Listing of various types of tags that can be defined in a wasm module. #[derive(Clone, Debug)] pub enum TagType<'a> { /// An exception tag, where the payload is the type signature of the tag /// (constructor parameters, etc). Exception(TypeUse<'a, FunctionType<'a>>), } /// Different kinds of tags that can be defined in a module. #[derive(Debug)] pub enum TagKind<'a> { /// An tag which is actually defined as an import, such as: /// /// ```text /// (tag (type 0) (import "foo" "bar")) /// ``` Import(InlineImport<'a>), /// A tag defined inline in the module itself Inline(), } impl<'a> Parse<'a> for Tag<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let exports = parser.parse()?; let (ty, kind) = if let Some(import) = parser.parse()? { (parser.parse()?, TagKind::Import(import)) } else { (parser.parse()?, TagKind::Inline()) }; Ok(Tag { span, id, name, exports, ty, kind, }) } } impl<'a> Parse<'a> for TagType<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(TagType::Exception(parser.parse()?)) } } wast-217.0.0/src/core/types.rs000064400000000000000000000773031046102023000142140ustar 00000000000000use crate::core::*; use crate::kw; use crate::parser::Lookahead1; use crate::parser::{Cursor, Parse, Parser, Peek, Result}; use crate::token::{Id, Index, LParen, NameAnnotation, Span}; use crate::Error; use std::mem; /// The value types for a wasm module. #[allow(missing_docs)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum ValType<'a> { I32, I64, F32, F64, V128, Ref(RefType<'a>), } impl<'a> Parse<'a> for ValType<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(ValType::I32) } else if l.peek::()? { parser.parse::()?; Ok(ValType::I64) } else if l.peek::()? { parser.parse::()?; Ok(ValType::F32) } else if l.peek::()? { parser.parse::()?; Ok(ValType::F64) } else if l.peek::()? { parser.parse::()?; Ok(ValType::V128) } else if l.peek::()? { Ok(ValType::Ref(parser.parse()?)) } else { Err(l.error()) } } } impl<'a> Peek for ValType<'a> { fn peek(cursor: Cursor<'_>) -> Result { Ok(kw::i32::peek(cursor)? || kw::i64::peek(cursor)? || kw::f32::peek(cursor)? || kw::f64::peek(cursor)? || kw::v128::peek(cursor)? || RefType::peek(cursor)?) } fn display() -> &'static str { "valtype" } } /// A heap type for a reference type. #[allow(missing_docs)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum HeapType<'a> { /// An abstract reference. With the shared-everything-threads proposal, /// these types can also be marked `shared`. Abstract { shared: bool, ty: AbstractHeapType }, /// A reference to a concrete function, struct, or array type defined by /// Wasm: `ref T`. This is part of the function references and GC proposals. Concrete(Index<'a>), } impl<'a> Parse<'a> for HeapType<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { Ok(HeapType::Concrete(parser.parse()?)) } else if l.peek::()? { parser.parens(|p| { p.parse::()?; Ok(HeapType::Abstract { shared: true, ty: p.parse()?, }) }) } else if l.peek::()? { Ok(HeapType::Abstract { shared: false, ty: parser.parse()?, }) } else { Err(l.error()) } } } impl<'a> Peek for HeapType<'a> { fn peek(cursor: Cursor<'_>) -> Result { Ok(AbstractHeapType::peek(cursor)? || (LParen::peek(cursor)? && kw::shared::peek2(cursor)?) || (LParen::peek(cursor)? && kw::r#type::peek2(cursor)?)) } fn display() -> &'static str { "heaptype" } } /// An abstract heap type. #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum AbstractHeapType { /// An untyped function reference: funcref. This is part of the reference /// types proposal. Func, /// A reference to any host value: externref. This is part of the reference /// types proposal. Extern, /// A reference to a wasm exception. This is part of the exceptions proposal. Exn, /// A reference to any reference value: anyref. This is part of the GC /// proposal. Any, /// A reference that has an identity that can be compared: eqref. This is /// part of the GC proposal. Eq, /// A reference to a GC struct. This is part of the GC proposal. Struct, /// A reference to a GC array. This is part of the GC proposal. Array, /// An unboxed 31-bit integer: i31ref. Part of the GC proposal. I31, /// The bottom type of the funcref hierarchy. Part of the GC proposal. NoFunc, /// The bottom type of the externref hierarchy. Part of the GC proposal. NoExtern, /// The bottom type of the anyref hierarchy. Part of the GC proposal. None, /// The bottom type of the exnref hierarchy. Part of the exceptions proposal. NoExn, } impl<'a> Parse<'a> for AbstractHeapType { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::Func) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::Extern) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::Exn) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::Any) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::Eq) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::Struct) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::Array) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::I31) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::NoFunc) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::NoExtern) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::NoExn) } else if l.peek::()? { parser.parse::()?; Ok(AbstractHeapType::None) } else { Err(l.error()) } } } impl<'a> Peek for AbstractHeapType { fn peek(cursor: Cursor<'_>) -> Result { Ok(kw::func::peek(cursor)? || kw::r#extern::peek(cursor)? || kw::exn::peek(cursor)? || kw::any::peek(cursor)? || kw::eq::peek(cursor)? || kw::r#struct::peek(cursor)? || kw::array::peek(cursor)? || kw::i31::peek(cursor)? || kw::nofunc::peek(cursor)? || kw::noextern::peek(cursor)? || kw::noexn::peek(cursor)? || kw::none::peek(cursor)?) } fn display() -> &'static str { "absheaptype" } } /// A reference type in a wasm module. #[allow(missing_docs)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub struct RefType<'a> { pub nullable: bool, pub heap: HeapType<'a>, } impl<'a> RefType<'a> { /// A `funcref` as an abbreviation for `(ref null func)`. pub fn func() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Func, }, } } /// An `externref` as an abbreviation for `(ref null extern)`. pub fn r#extern() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern, }, } } /// An `exnrefr` as an abbreviation for `(ref null exn)`. pub fn exn() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Exn, }, } } /// An `anyref` as an abbreviation for `(ref null any)`. pub fn any() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Any, }, } } /// An `eqref` as an abbreviation for `(ref null eq)`. pub fn eq() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Eq, }, } } /// An `structref` as an abbreviation for `(ref null struct)`. pub fn r#struct() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Struct, }, } } /// An `arrayref` as an abbreviation for `(ref null array)`. pub fn array() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::Array, }, } } /// An `i31ref` as an abbreviation for `(ref null i31)`. pub fn i31() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::I31, }, } } /// A `nullfuncref` as an abbreviation for `(ref null nofunc)`. pub fn nullfuncref() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::NoFunc, }, } } /// A `nullexternref` as an abbreviation for `(ref null noextern)`. pub fn nullexternref() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::NoExtern, }, } } /// A `nullref` as an abbreviation for `(ref null none)`. pub fn nullref() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::None, }, } } /// A `nullexnref` as an abbreviation for `(ref null noexn)`. pub fn nullexnref() -> Self { RefType { nullable: true, heap: HeapType::Abstract { shared: false, ty: AbstractHeapType::NoExn, }, } } /// Make the reference type a `shared` one. /// /// Note that this is not possible for concrete references (e.g., `(ref /// $t)`) so `None` is returned in that case. pub fn shared(self) -> Option { match self.heap { HeapType::Abstract { ty, .. } => Some(RefType { nullable: self.nullable, heap: HeapType::Abstract { shared: true, ty }, }), _ => None, } } /// Helper for checking if shorthand forms of reference types can be parsed /// next; e.g., `funcref`. fn peek_shorthand(l: &mut Lookahead1) -> Result { Ok(l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()? || l.peek::()?) } /// Helper for parsing shorthand forms of reference types; e.g., `funcref`. fn parse_shorthand(mut l: Lookahead1, parser: Parser<'a>) -> Result { if l.peek::()? { parser.parse::()?; Ok(RefType::func()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::r#extern()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::exn()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::any()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::eq()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::r#struct()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::array()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::i31()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::nullfuncref()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::nullexternref()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::nullexnref()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::nullref()) } else { Err(l.error()) } } } impl<'a> Parse<'a> for RefType<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if RefType::peek_shorthand(&mut l)? { // I.e., `*ref`. RefType::parse_shorthand(l, parser) } else if l.peek::()? { parser.parens(|p| { let mut l = parser.lookahead1(); if l.peek::()? { // I.e., `(ref null? ...)`. p.parse::()?; let mut nullable = false; if parser.peek::()? { parser.parse::()?; nullable = true; } Ok(RefType { nullable, heap: parser.parse()?, }) } else if l.peek::()? { // I.e., `(shared *ref)`. p.parse::()?; let reftype = RefType::parse_shorthand(l, parser)?; Ok(reftype.shared().expect("only abstract heap types are used")) } else { Err(l.error()) } }) } else { Err(l.error()) } } } impl<'a> Peek for RefType<'a> { fn peek(cursor: Cursor<'_>) -> Result { Ok(kw::funcref::peek(cursor)? || kw::externref::peek(cursor)? || kw::exnref::peek(cursor)? || kw::anyref::peek(cursor)? || kw::eqref::peek(cursor)? || kw::structref::peek(cursor)? || kw::arrayref::peek(cursor)? || kw::i31ref::peek(cursor)? || kw::nullfuncref::peek(cursor)? || kw::nullexternref::peek(cursor)? || kw::nullexnref::peek(cursor)? || kw::nullref::peek(cursor)? || (LParen::peek(cursor)? && kw::shared::peek2(cursor)?) || (LParen::peek(cursor)? && kw::r#ref::peek2(cursor)?)) } fn display() -> &'static str { "reftype" } } /// The types of values that may be used in a struct or array. #[allow(missing_docs)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum StorageType<'a> { I8, I16, Val(ValType<'a>), } impl<'a> Parse<'a> for StorageType<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(StorageType::I8) } else if l.peek::()? { parser.parse::()?; Ok(StorageType::I16) } else if l.peek::()? { Ok(StorageType::Val(parser.parse()?)) } else { Err(l.error()) } } } /// Type for a `global` in a wasm module #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct GlobalType<'a> { /// The element type of this `global` pub ty: ValType<'a>, /// Whether or not the global is mutable or not. pub mutable: bool, /// Whether or not the global is shared. pub shared: bool, } impl<'a> Parse<'a> for GlobalType<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek2::()? || parser.peek2::()? { parser.parens(|p| { let mut shared = false; let mut mutable = false; if p.peek::()? { p.parse::()?; shared = true; } if p.peek::()? { p.parse::()?; mutable = true; } Ok(GlobalType { ty: p.parse()?, mutable, shared, }) }) } else { Ok(GlobalType { ty: parser.parse()?, mutable: false, shared: false, }) } } } /// Min/max limits used for tables/memories. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Limits { /// Whether or not these limits are for 64-bit tables/memories or not. pub is64: bool, /// The minimum number of units for this type. pub min: u64, /// An optional maximum number of units for this type. pub max: Option, } impl<'a> Parse<'a> for Limits { fn parse(parser: Parser<'a>) -> Result { let is64 = if parser.peek::()? { parser.parse::()?; false } else if parser.peek::()? { parser.parse::()?; true } else { false }; let parse = || { if is64 { parser.parse::() } else { parser.parse::().map(|x| x.into()) } }; let min = parse()?; let max = if parser.peek::()? { Some(parse()?) } else { None }; Ok(Limits { is64, min, max }) } } /// Configuration for a table of a wasm module. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct TableType<'a> { /// Limits on the element sizes of this table pub limits: Limits, /// The type of element stored in this table pub elem: RefType<'a>, /// Whether or not this is a shared table. pub shared: bool, } impl<'a> Parse<'a> for TableType<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(TableType { shared: parser.parse::>()?.is_some(), limits: parser.parse()?, elem: parser.parse()?, }) } } /// Configuration for a memory of a wasm module #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct MemoryType { /// Limits on the page sizes of this memory pub limits: Limits, /// Whether or not this is a shared (atomic) memory type pub shared: bool, /// The custom page size for this memory, if any. pub page_size_log2: Option, } fn page_size(parser: Parser<'_>) -> Result> { if parser.peek::()? { Ok(Some(parser.parens(|parser| { parser.parse::()?; let span = parser.cur_span(); let size = parser.parse::()?; if size.is_power_of_two() { Ok(size.ilog2()) } else { Err(Error::new( span, format!("invalid custom page size: {size}"), )) } })?)) } else { Ok(None) } } impl<'a> Parse<'a> for MemoryType { fn parse(parser: Parser<'a>) -> Result { let limits = parser.parse()?; let shared = parser.parse::>()?.is_some(); let page_size = page_size(parser)?; Ok(MemoryType { limits, shared, page_size_log2: page_size, }) } } /// A function type with parameters and results. #[derive(Clone, Debug, Default)] pub struct FunctionType<'a> { /// The parameters of a function, optionally each having an identifier for /// name resolution and a name for the custom `name` section. pub params: Box<[(Option>, Option>, ValType<'a>)]>, /// The results types of a function. pub results: Box<[ValType<'a>]>, } impl<'a> FunctionType<'a> { fn finish_parse(&mut self, allow_names: bool, parser: Parser<'a>) -> Result<()> { let mut params = Vec::from(mem::take(&mut self.params)); let mut results = Vec::from(mem::take(&mut self.results)); while parser.peek2::()? || parser.peek2::()? { parser.parens(|p| { let mut l = p.lookahead1(); if l.peek::()? { if results.len() > 0 { return Err(p.error( "result before parameter (or unexpected token): \ cannot list params after results", )); } p.parse::()?; if p.is_empty() { return Ok(()); } let (id, name) = if allow_names { (p.parse::>()?, p.parse::>()?) } else { (None, None) }; let parse_more = id.is_none() && name.is_none(); let ty = p.parse()?; params.push((id, name, ty)); while parse_more && !p.is_empty() { params.push((None, None, p.parse()?)); } } else if l.peek::()? { p.parse::()?; while !p.is_empty() { results.push(p.parse()?); } } else { return Err(l.error()); } Ok(()) })?; } self.params = params.into(); self.results = results.into(); Ok(()) } } impl<'a> Parse<'a> for FunctionType<'a> { fn parse(parser: Parser<'a>) -> Result { let mut ret = FunctionType { params: Box::new([]), results: Box::new([]), }; ret.finish_parse(true, parser)?; Ok(ret) } } impl<'a> Peek for FunctionType<'a> { fn peek(cursor: Cursor<'_>) -> Result { if let Some(next) = cursor.lparen()? { match next.keyword()? { Some(("param", _)) | Some(("result", _)) => return Ok(true), _ => {} } } Ok(false) } fn display() -> &'static str { "function type" } } /// A function type with parameters and results. #[derive(Clone, Debug, Default)] pub struct FunctionTypeNoNames<'a>(pub FunctionType<'a>); impl<'a> Parse<'a> for FunctionTypeNoNames<'a> { fn parse(parser: Parser<'a>) -> Result { let mut ret = FunctionType { params: Box::new([]), results: Box::new([]), }; ret.finish_parse(false, parser)?; Ok(FunctionTypeNoNames(ret)) } } impl<'a> Peek for FunctionTypeNoNames<'a> { fn peek(cursor: Cursor<'_>) -> Result { FunctionType::peek(cursor) } fn display() -> &'static str { FunctionType::display() } } impl<'a> From> for FunctionType<'a> { fn from(ty: FunctionTypeNoNames<'a>) -> FunctionType<'a> { ty.0 } } /// A struct type with fields. #[derive(Clone, Debug)] pub struct StructType<'a> { /// The fields of the struct pub fields: Vec>, } impl<'a> Parse<'a> for StructType<'a> { fn parse(parser: Parser<'a>) -> Result { let mut ret = StructType { fields: Vec::new() }; while !parser.is_empty() { parser.parens(|parser| { parser.parse::()?; if parser.peek::()? { let field = StructField::parse(parser, true); ret.fields.push(field?); } else { while !parser.is_empty() { let field = StructField::parse(parser, false); ret.fields.push(field?); } } Ok(()) })?; } Ok(ret) } } /// A field of a struct type. #[derive(Clone, Debug)] pub struct StructField<'a> { /// An optional identifier for name resolution. pub id: Option>, /// Whether this field may be mutated or not. pub mutable: bool, /// The storage type stored in this field. pub ty: StorageType<'a>, } impl<'a> StructField<'a> { fn parse(parser: Parser<'a>, with_id: bool) -> Result { let id = if with_id { parser.parse()? } else { None }; let (ty, mutable) = if parser.peek2::()? { let ty = parser.parens(|parser| { parser.parse::()?; parser.parse() })?; (ty, true) } else { (parser.parse::>()?, false) }; Ok(StructField { id, mutable, ty }) } } /// An array type with fields. #[derive(Clone, Debug)] pub struct ArrayType<'a> { /// Whether this field may be mutated or not. pub mutable: bool, /// The storage type stored in this field. pub ty: StorageType<'a>, } impl<'a> Parse<'a> for ArrayType<'a> { fn parse(parser: Parser<'a>) -> Result { let (ty, mutable) = if parser.peek2::()? { let ty = parser.parens(|parser| { parser.parse::()?; parser.parse() })?; (ty, true) } else { (parser.parse::>()?, false) }; Ok(ArrayType { mutable, ty }) } } /// 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: 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 ExportType<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let name = parser.parse()?; let item = parser.parens(|p| p.parse())?; Ok(ExportType { span, name, item }) } } /// The inner kind of a type definition. #[derive(Debug)] pub enum InnerTypeKind<'a> { /// A function type definition. Func(FunctionType<'a>), /// A struct type definition. Struct(StructType<'a>), /// An array type definition. Array(ArrayType<'a>), } impl<'a> Parse<'a> for InnerTypeKind<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(InnerTypeKind::Func(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; Ok(InnerTypeKind::Struct(parser.parse()?)) } else if l.peek::()? { parser.parse::()?; Ok(InnerTypeKind::Array(parser.parse()?)) } else { Err(l.error()) } } } /// A definition of a type. #[derive(Debug)] pub struct TypeDef<'a> { /// The inner definition. pub kind: InnerTypeKind<'a>, /// Whether the type is shared or not. pub shared: bool, } impl<'a> Parse<'a> for TypeDef<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; parser.parens(|parser| { let kind = parser.parse()?; Ok(TypeDef { shared: true, kind }) }) } else { let kind = parser.parse()?; Ok(TypeDef { shared: false, kind, }) } } } /// A type declaration in a module #[derive(Debug)] pub struct Type<'a> { /// Where this type was defined. pub span: Span, /// An optional identifier to refer to this `type` by as part of name /// resolution. pub id: Option>, /// An optional name for this function stored in the custom `name` section. pub name: Option>, /// The type that we're declaring. pub def: TypeDef<'a>, /// The declared parent type of this definition. pub parent: Option>, /// Whether this type is final or not. By default types are final. pub final_type: Option, } impl<'a> Peek for Type<'a> { fn peek(cursor: Cursor<'_>) -> Result { kw::r#type::peek(cursor) } fn display() -> &'static str { "type" } } impl<'a> Parse<'a> for Type<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let id = parser.parse()?; let name = parser.parse()?; let (parent, def, final_type) = if parser.peek2::()? { parser.parens(|parser| { parser.parse::()?; let final_type: Option = if parser.peek::()? { parser.parse::()?; Some(true) } else { Some(false) }; let parent = if parser.peek::>()? { parser.parse()? } else { None }; let def = parser.parens(|parser| parser.parse())?; Ok((parent, def, final_type)) })? } else { (None, parser.parens(|parser| parser.parse())?, None) }; Ok(Type { span, id, name, def, parent, final_type, }) } } /// A recursion group declaration in a module #[derive(Debug)] pub struct Rec<'a> { /// Where this recursion group was defined. pub span: Span, /// The types that we're defining in this group. pub types: Vec>, } impl<'a> Parse<'a> for Rec<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let mut types = Vec::new(); while parser.peek2::>()? { types.push(parser.parens(|p| p.parse())?); } Ok(Rec { span, types }) } } /// A reference to a type defined in this module. #[derive(Clone, Debug)] pub struct TypeUse<'a, T> { /// The type that we're referencing, if it was present. pub index: Option>, /// The inline type, if present. pub inline: Option, } 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: Index<'a>) -> TypeUse<'a, T> { TypeUse { index: Some(idx), inline: None, } } } impl<'a, T: Peek + Parse<'a>> Parse<'a> for TypeUse<'a, T> { fn parse(parser: Parser<'a>) -> Result { let index = if parser.peek2::()? { Some(parser.parens(|p| { p.parse::()?; p.parse() })?) } else { None }; let inline = parser.parse()?; Ok(TypeUse { index, inline }) } } impl<'a> From>> for TypeUse<'a, FunctionType<'a>> { fn from(src: TypeUse<'a, FunctionTypeNoNames<'a>>) -> TypeUse<'a, FunctionType<'a>> { TypeUse { index: src.index, inline: src.inline.map(|x| x.into()), } } } wast-217.0.0/src/core/wast.rs000064400000000000000000000174411046102023000140230ustar 00000000000000use crate::core::{HeapType, V128Const}; use crate::kw; use crate::parser::{Cursor, Parse, Parser, Peek, Result}; use crate::token::{Index, F32, F64}; /// Expression that can be used inside of `invoke` expressions for core wasm /// functions. #[derive(Debug)] #[allow(missing_docs)] pub enum WastArgCore<'a> { I32(i32), I64(i64), F32(F32), F64(F64), V128(V128Const), RefNull(HeapType<'a>), RefExtern(u32), RefHost(u32), } static ARGS: &[(&str, fn(Parser<'_>) -> Result>)] = { use WastArgCore::*; &[ ("i32.const", |p| Ok(I32(p.parse()?))), ("i64.const", |p| Ok(I64(p.parse()?))), ("f32.const", |p| Ok(F32(p.parse()?))), ("f64.const", |p| Ok(F64(p.parse()?))), ("v128.const", |p| Ok(V128(p.parse()?))), ("ref.null", |p| Ok(RefNull(p.parse()?))), ("ref.extern", |p| Ok(RefExtern(p.parse()?))), ("ref.host", |p| Ok(RefHost(p.parse()?))), ] }; impl<'a> Parse<'a> for WastArgCore<'a> { fn parse(parser: Parser<'a>) -> Result { let parse = parser.step(|c| { if let Some((kw, rest)) = c.keyword()? { if let Some(i) = ARGS.iter().position(|(name, _)| *name == kw) { return Ok((ARGS[i].1, rest)); } } Err(c.error("expected a [type].const expression")) })?; parse(parser) } } impl Peek for WastArgCore<'_> { fn peek(cursor: Cursor<'_>) -> Result { let kw = match cursor.keyword()? { Some((kw, _)) => kw, None => return Ok(false), }; Ok(ARGS.iter().find(|(name, _)| *name == kw).is_some()) } fn display() -> &'static str { "core wasm argument" } } /// Expressions that can be used inside of `assert_return` to validate the /// return value of a core wasm function. #[derive(Debug)] #[allow(missing_docs)] pub enum WastRetCore<'a> { I32(i32), I64(i64), F32(NanPattern), F64(NanPattern), V128(V128Pattern), /// A null reference is expected, optionally with a specified type. RefNull(Option>), /// A non-null externref is expected which should contain the specified /// value. RefExtern(Option), /// A non-null anyref is expected which should contain the specified host value. RefHost(u32), /// A non-null funcref is expected. RefFunc(Option>), /// A non-null anyref is expected. RefAny, /// A non-null eqref is expected. RefEq, /// A non-null arrayref is expected. RefArray, /// A non-null structref is expected. RefStruct, /// A non-null i31ref is expected. RefI31, /// A non-null, shared i31ref is expected. RefI31Shared, Either(Vec>), } static RETS: &[(&str, fn(Parser<'_>) -> Result>)] = { use WastRetCore::*; &[ ("i32.const", |p| Ok(I32(p.parse()?))), ("i64.const", |p| Ok(I64(p.parse()?))), ("f32.const", |p| Ok(F32(p.parse()?))), ("f64.const", |p| Ok(F64(p.parse()?))), ("v128.const", |p| Ok(V128(p.parse()?))), ("ref.null", |p| Ok(RefNull(p.parse()?))), ("ref.extern", |p| Ok(RefExtern(p.parse()?))), ("ref.host", |p| Ok(RefHost(p.parse()?))), ("ref.func", |p| Ok(RefFunc(p.parse()?))), ("ref.any", |_| Ok(RefAny)), ("ref.eq", |_| Ok(RefEq)), ("ref.array", |_| Ok(RefArray)), ("ref.struct", |_| Ok(RefStruct)), ("ref.i31", |_| Ok(RefI31)), ("ref.i31_shared", |_| Ok(RefI31Shared)), ("either", |p| { p.depth_check()?; let mut cases = Vec::new(); while !p.is_empty() { cases.push(p.parens(|p| p.parse())?); } Ok(Either(cases)) }), ] }; impl<'a> Parse<'a> for WastRetCore<'a> { fn parse(parser: Parser<'a>) -> Result { let parse = parser.step(|c| { if let Some((kw, rest)) = c.keyword()? { if let Some(i) = RETS.iter().position(|(name, _)| *name == kw) { return Ok((RETS[i].1, rest)); } } Err(c.error("expected a [type].const expression")) })?; parse(parser) } } impl Peek for WastRetCore<'_> { fn peek(cursor: Cursor<'_>) -> Result { let kw = match cursor.keyword()? { Some((kw, _)) => kw, None => return Ok(false), }; Ok(RETS.iter().find(|(name, _)| *name == kw).is_some()) } fn display() -> &'static str { "core wasm return value" } } /// Either a NaN pattern (`nan:canonical`, `nan:arithmetic`) or a value of type `T`. #[derive(Copy, Clone, Debug, PartialEq)] #[allow(missing_docs)] pub enum NanPattern { CanonicalNan, ArithmeticNan, Value(T), } impl<'a, T> Parse<'a> for NanPattern where T: Parse<'a>, { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { parser.parse::()?; Ok(NanPattern::CanonicalNan) } else if parser.peek::()? { parser.parse::()?; Ok(NanPattern::ArithmeticNan) } else { let val = parser.parse()?; Ok(NanPattern::Value(val)) } } } /// A version of `V128Const` that allows `NanPattern`s. /// /// This implementation is necessary because only float types can include NaN patterns; otherwise /// it is largely similar to the implementation of `V128Const`. #[derive(Clone, Debug)] #[allow(missing_docs)] pub enum V128Pattern { I8x16([i8; 16]), I16x8([i16; 8]), I32x4([i32; 4]), I64x2([i64; 2]), F32x4([NanPattern; 4]), F64x2([NanPattern; 2]), } impl<'a> Parse<'a> for V128Pattern { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { parser.parse::()?; Ok(V128Pattern::I8x16([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Pattern::I16x8([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Pattern::I32x4([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Pattern::I64x2([parser.parse()?, parser.parse()?])) } else if l.peek::()? { parser.parse::()?; Ok(V128Pattern::F32x4([ parser.parse()?, parser.parse()?, parser.parse()?, parser.parse()?, ])) } else if l.peek::()? { parser.parse::()?; Ok(V128Pattern::F64x2([parser.parse()?, parser.parse()?])) } else { Err(l.error()) } } } wast-217.0.0/src/core.rs000064400000000000000000000011121046102023000130310ustar 00000000000000//! 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; mod wast; pub use self::binary::{EncodeOptions, GenerateDwarf}; 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 use self::wast::*; pub(crate) mod binary; pub(crate) mod resolve; wast-217.0.0/src/encode.rs000064400000000000000000000034621046102023000133500ustar 00000000000000pub(crate) trait Encode { fn encode(&self, e: &mut Vec); } impl Encode for &'_ T { fn encode(&self, e: &mut Vec) { T::encode(self, e) } } impl Encode for Box { fn encode(&self, e: &mut Vec) { T::encode(self, e) } } impl Encode for [T] { fn encode(&self, e: &mut Vec) { self.len().encode(e); for item in self { item.encode(e); } } } impl Encode for Vec { fn encode(&self, e: &mut Vec) { <[T]>::encode(self, e) } } impl Encode for str { fn encode(&self, e: &mut Vec) { self.len().encode(e); e.extend_from_slice(self.as_bytes()); } } impl Encode for usize { fn encode(&self, e: &mut Vec) { assert!(*self <= u32::max_value() as usize); (*self as u32).encode(e) } } impl Encode for u8 { fn encode(&self, e: &mut Vec) { e.push(*self); } } impl Encode for u32 { fn encode(&self, e: &mut Vec) { leb128::write::unsigned(e, (*self).into()).unwrap(); } } impl Encode for i32 { fn encode(&self, e: &mut Vec) { leb128::write::signed(e, (*self).into()).unwrap(); } } impl Encode for u64 { fn encode(&self, e: &mut Vec) { leb128::write::unsigned(e, *self).unwrap(); } } impl Encode for i64 { fn encode(&self, e: &mut Vec) { leb128::write::signed(e, *self).unwrap(); } } impl Encode for (T, U) { fn encode(&self, e: &mut Vec) { self.0.encode(e); self.1.encode(e); } } impl Encode for (T, U, V) { fn encode(&self, e: &mut Vec) { self.0.encode(e); self.1.encode(e); self.2.encode(e); } } wast-217.0.0/src/error.rs000064400000000000000000000133741046102023000132470ustar 00000000000000use 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, } #[derive(Debug)] struct ErrorInner { text: Option, file: Option, 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); 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); 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(""); 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 } } } wast-217.0.0/src/gensym.rs000064400000000000000000000006541046102023000134150ustar 00000000000000use crate::token::{Id, Span}; use std::cell::Cell; thread_local!(static NEXT: Cell = Cell::new(0)); pub fn reset() { NEXT.with(|c| c.set(0)); } pub fn gen(span: Span) -> Id<'static> { NEXT.with(|next| { let gen = next.get() + 1; next.set(gen); Id::gensym(span, gen) }) } pub fn fill<'a>(span: Span, slot: &mut Option>) -> Id<'a> { *slot.get_or_insert_with(|| gen(span)) } wast-217.0.0/src/lexer.rs000064400000000000000000001561611046102023000132370ustar 00000000000000//! Definition of a lexer for the WebAssembly text format. //! //! This module provides a [`Lexer`][] type which is an iterate over the raw //! tokens of a WebAssembly text file. A [`Lexer`][] accounts for every single //! byte in a WebAssembly text field, returning tokens even for comments and //! whitespace. Typically you'll ignore comments and whitespace, however. //! //! If you'd like to iterate over the tokens in a file you can do so via: //! //! ``` //! # fn foo() -> Result<(), wast::Error> { //! use wast::lexer::Lexer; //! //! let wat = "(module (func $foo))"; //! for token in Lexer::new(wat).iter(0) { //! println!("{:?}", token?); //! } //! # Ok(()) //! # } //! ``` //! //! Note that you'll typically not use this module but will rather use //! [`ParseBuffer`](crate::parser::ParseBuffer) instead. //! //! [`Lexer`]: crate::lexer::Lexer use crate::token::Span; use crate::Error; use std::borrow::Cow; use std::char; use std::fmt; use std::slice; use std::str; use std::str::Utf8Error; /// A structure used to lex the s-expression syntax of WAT files. /// /// 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)] pub struct Lexer<'a> { input: &'a str, allow_confusing_unicode: bool, } /// A single token parsed from a `Lexer`. #[derive(Copy, Clone, Debug, PartialEq)] pub struct Token { /// The kind of token this represents, such as whether it's whitespace, a /// keyword, etc. pub kind: TokenKind, /// The byte offset within the original source for where this token came /// from. pub offset: usize, /// The byte length of this token as it resides in the original source. // // NB: this is `u32` to enable packing `Token` into two pointers of size. // This does limit a single token to being at most 4G large, but that seems // probably ok. pub len: u32, } #[test] fn token_is_not_too_big() { assert!(std::mem::size_of::() <= std::mem::size_of::() * 2); } /// Classification of what was parsed from the input stream. /// /// This enumeration contains all kinds of fragments, including comments and /// whitespace. #[derive(Copy, Clone, Debug, PartialEq)] pub enum TokenKind { /// A line comment, preceded with `;;` LineComment, /// A block comment, surrounded by `(;` and `;)`. Note that these can be /// nested. BlockComment, /// A fragment of source that represents whitespace. Whitespace, /// A left-parenthesis, including the source text for where it comes from. LParen, /// A right-parenthesis, including the source text for where it comes from. RParen, /// A string literal, which is actually a list of bytes. String, /// An identifier (like `$foo`). /// /// All identifiers start with `$` and the payload here is the original /// source text. Id, /// A keyword, or something that starts with an alphabetic character. /// /// The payload here is the original source text. Keyword, /// An annotation (like `@foo`). /// /// All annotations start with `@` and the payload will be the name of the /// annotation. Annotation, /// A reserved series of `idchar` symbols. Unknown what this is meant to be /// used for, you'll probably generate an error about an unexpected token. Reserved, /// An integer. Integer(IntegerKind), /// A float. Float(FloatKind), } /// Description of the parsed integer from the source. #[derive(Copy, Clone, Debug, PartialEq)] pub struct IntegerKind { sign: Option, has_underscores: bool, hex: bool, } /// Description of a parsed float from the source. #[allow(missing_docs)] #[derive(Copy, Clone, Debug, PartialEq)] pub enum FloatKind { #[doc(hidden)] Inf { negative: bool }, #[doc(hidden)] Nan { negative: bool }, #[doc(hidden)] NanVal { negative: bool, has_underscores: bool, }, #[doc(hidden)] Normal { has_underscores: bool, hex: bool }, } enum ReservedKind { /// "..." String, /// anything that's just a sequence of `idchars!()` Idchars, /// $"..." IdString, /// @"..." AnnotationString, /// everything else (a conglomeration of strings, idchars, etc) Reserved, } /// Errors that can be generated while lexing. /// /// All lexing errors have line/colum/position information as well as a /// `LexError` indicating what kind of error happened while lexing. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum LexError { /// A dangling block comment was found with an unbalanced `(;` which was /// never terminated in the file. DanglingBlockComment, /// An unexpected character was encountered when generally parsing and /// looking for something else. Unexpected(char), /// An invalid `char` in a string literal was found. InvalidStringElement(char), /// An invalid string escape letter was found (the thing after the `\` in /// string literals) InvalidStringEscape(char), /// An invalid hexadecimal digit was found. InvalidHexDigit(char), /// An invalid base-10 digit was found. InvalidDigit(char), /// Parsing expected `wanted` but ended up finding `found` instead where the /// two characters aren't the same. Expected { /// The character that was expected to be found wanted: char, /// The character that was actually found found: char, }, /// We needed to parse more but EOF (or end of the string) was encountered. UnexpectedEof, /// A number failed to parse because it was too big to fit within the target /// type. NumberTooBig, /// An invalid unicode value was found in a `\u{...}` escape in a string, /// only valid unicode scalars can be escaped that way. InvalidUnicodeValue(u32), /// A lone underscore was found when parsing a number, since underscores /// should always be preceded and succeeded with a digit of some form. LoneUnderscore, /// A "confusing" unicode character is present in a comment or a string /// literal, such as a character that changes the direction text is /// typically displayed in editors. This could cause the human-read /// version to behave differently than the compiler-visible version, so /// these are simply rejected for now. ConfusingUnicode(char), /// An invalid utf-8 sequence was found in a quoted identifier, such as /// `$"\ff"`. InvalidUtf8Id(Utf8Error), /// An empty identifier was found, or a lone `$`. EmptyId, /// An empty identifier was found, or a lone `@`. EmptyAnnotation, } /// A sign token for an integer. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SignToken { /// Plus sign: "+", Plus, /// Minus sign: "-", Minus, } /// A fully parsed integer from a source string with a payload ready to parse /// into an integral type. #[derive(Debug, PartialEq)] pub struct Integer<'a> { sign: Option, val: Cow<'a, str>, hex: bool, } /// Possible parsed float values #[derive(Debug, PartialEq, Eq)] pub enum Float<'a> { /// A float `NaN` representation Nan { /// The specific bits to encode for this float, optionally val: Option>, /// Whether or not this is a negative `NaN` or not. negative: bool, }, /// An float infinite representation, Inf { #[allow(missing_docs)] negative: bool, }, /// A parsed and separated floating point value Val { /// Whether or not the `integral` and `decimal` are specified in hex hex: bool, /// The float parts before the `.` integral: Cow<'a, str>, /// The float parts after the `.` decimal: Option>, /// The exponent to multiple this `integral.decimal` portion of the /// float by. If `hex` is true this is `2^exponent` and otherwise it's /// `10^exponent` exponent: Option>, }, } // https://webassembly.github.io/spec/core/text/values.html#text-idchar macro_rules! idchars { () => { b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' | b'/' | b':' | b'<' | b'=' | b'>' | b'?' | b'@' | b'\\' | b'^' | b'_' | b'`' | b'|' | b'~' } } impl<'a> Lexer<'a> { /// Creates a new lexer which will lex the `input` source string. pub fn new(input: &str) -> Lexer<'_> { Lexer { input, allow_confusing_unicode: false, } } /// Returns the original source input that we're lexing. pub fn input(&self) -> &'a str { self.input } /// Configures whether "confusing" unicode characters are allowed while /// lexing. /// /// If allowed then no error will happen if these characters are found, but /// otherwise if disallowed a lex error will be produced when these /// characters are found. Confusing characters are denied by default. /// /// For now "confusing characters" are primarily related to the "trojan /// source" problem where it refers to characters which cause humans to read /// text differently than this lexer, such as characters that alter the /// left-to-right display of the source code. pub fn allow_confusing_unicode(&mut self, allow: bool) -> &mut Self { self.allow_confusing_unicode = allow; self } /// Lexes the next at the byte position `pos` in the input. /// /// Returns `Some` if a token is found or `None` if we're at EOF. /// /// The `pos` argument will be updated to point to the next token on a /// successful parse. /// /// # Errors /// /// Returns an error if the input is malformed. pub fn parse(&self, pos: &mut usize) -> Result, Error> { let offset = *pos; Ok(match self.parse_kind(pos)? { Some(kind) => Some(Token { kind, offset, len: (*pos - offset).try_into().unwrap(), }), None => None, }) } fn parse_kind(&self, pos: &mut usize) -> Result, Error> { let start = *pos; // This `match` generally parses the grammar specified at // // https://webassembly.github.io/spec/core/text/lexical.html#text-token let remaining = &self.input.as_bytes()[start..]; let byte = match remaining.first() { Some(b) => b, None => return Ok(None), }; match byte { // Open-parens check the next character to see if this is the start // of a block comment, otherwise it's just a bland left-paren // token. b'(' => match remaining.get(1) { Some(b';') => { let mut level = 1; // Note that we're doing a byte-level search here for the // close-delimiter of `;)`. The actual source text is utf-8 // encode in `remaining` but due to how utf-8 works we // can safely search for an ASCII byte since it'll never // otherwise appear in the middle of a codepoint and if we // find it then it's guaranteed to be the right byte. // // Mainly we're avoiding the overhead of decoding utf-8 // characters into a Rust `char` since it's otherwise // unnecessary work. let mut iter = remaining[2..].iter(); while let Some(ch) = iter.next() { match ch { b'(' => { if let Some(b';') = iter.as_slice().first() { level += 1; iter.next(); } } b';' => { if let Some(b')') = iter.as_slice().first() { level -= 1; iter.next(); if level == 0 { let len = remaining.len() - iter.as_slice().len(); let comment = &self.input[start..][..len]; *pos += len; self.check_confusing_comment(*pos, comment)?; return Ok(Some(TokenKind::BlockComment)); } } } _ => {} } } Err(self.error(start, LexError::DanglingBlockComment)) } _ => { *pos += 1; Ok(Some(TokenKind::LParen)) } }, b')' => { *pos += 1; Ok(Some(TokenKind::RParen)) } // https://webassembly.github.io/spec/core/text/lexical.html#white-space b' ' | b'\n' | b'\r' | b'\t' => { self.skip_ws(pos); Ok(Some(TokenKind::Whitespace)) } c @ (idchars!() | b'"') => { let (kind, src) = self.parse_reserved(pos)?; match kind { // If the reserved token was simply a single string then // that is converted to a standalone string token ReservedKind::String => return Ok(Some(TokenKind::String)), // If only idchars were consumed then this could be a // specific kind of standalone token we're interested in. ReservedKind::Idchars => { // https://webassembly.github.io/spec/core/text/values.html#integers if let Some(ret) = self.classify_number(src) { return Ok(Some(ret)); // https://webassembly.github.io/spec/core/text/values.html#text-id } else if *c == b'$' { return Ok(Some(TokenKind::Id)); // part of the WebAssembly/annotations proposal // (no online url yet) } else if *c == b'@' { return Ok(Some(TokenKind::Annotation)); // https://webassembly.github.io/spec/core/text/lexical.html#text-keyword } else if b'a' <= *c && *c <= b'z' { return Ok(Some(TokenKind::Keyword)); } } ReservedKind::IdString => return Ok(Some(TokenKind::Id)), ReservedKind::AnnotationString => return Ok(Some(TokenKind::Annotation)), // ... otherwise this was a conglomeration of idchars, // strings, or just idchars that don't match a prior rule, // meaning this falls through to the fallback `Reserved` // token. ReservedKind::Reserved => {} } Ok(Some(TokenKind::Reserved)) } // This could be a line comment, otherwise `;` is a reserved token. // The second byte is checked to see if it's a `;;` line comment // // Note that this character being considered as part of a // `reserved` token is part of the annotations proposal. b';' => match remaining.get(1) { Some(b';') => { let remaining = &self.input[*pos..]; let byte_pos = memchr::memchr2(b'\n', b'\r', remaining.as_bytes()) .unwrap_or(remaining.len()); *pos += byte_pos; let comment = &remaining[..byte_pos]; self.check_confusing_comment(*pos, comment)?; Ok(Some(TokenKind::LineComment)) } _ => { *pos += 1; Ok(Some(TokenKind::Reserved)) } }, // Other known reserved tokens other than `;` // // Note that these characters being considered as part of a // `reserved` token is part of the annotations proposal. b',' | b'[' | b']' | b'{' | b'}' => { *pos += 1; Ok(Some(TokenKind::Reserved)) } _ => { let ch = self.input[start..].chars().next().unwrap(); Err(self.error(*pos, LexError::Unexpected(ch))) } } } fn skip_ws(&self, pos: &mut usize) { // This table is a byte lookup table to determine whether a byte is a // whitespace byte. There are only 4 whitespace bytes for the `*.wat` // format right now which are ' ', '\t', '\r', and '\n'. These 4 bytes // have a '1' in the table below. // // Due to how utf-8 works (our input is guaranteed to be utf-8) it is // known that if these bytes are found they're guaranteed to be the // whitespace byte, so they can be safely skipped and we don't have to // do full utf-8 decoding. This means that the goal of this function is // to find the first non-whitespace byte in `remaining`. // // For now this lookup table seems to be the fastest, but projects like // https://github.com/lemire/despacer show other simd algorithms which // can possibly accelerate this even more. Note that `*.wat` files often // have a lot of whitespace so this function is typically quite hot when // parsing inputs. #[rustfmt::skip] const WS: [u8; 256] = [ // \t \n \r /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ' ' /* 0x20 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; let remaining = &self.input[*pos..]; let non_ws_pos = remaining .as_bytes() .iter() .position(|b| WS[*b as usize] != 1) .unwrap_or(remaining.len()); *pos += non_ws_pos; } /// Splits off a "reserved" token which is then further processed later on /// to figure out which kind of token it is `depending on `ReservedKind`. /// /// For more information on this method see the clarification at /// but the general gist is /// that this is parsing the grammar: /// /// ```text /// reserved := (idchar | string)+ /// ``` /// /// which means that it is eating any number of adjacent string/idchar /// tokens (e.g. `a"b"c`) and returning the classification of what was /// eaten. The classification assists in determining what the actual token /// here eaten looks like. fn parse_reserved(&self, pos: &mut usize) -> Result<(ReservedKind, &'a str), Error> { let mut idchars = 0u32; let mut strings = 0u32; let start = *pos; while let Some(byte) = self.input.as_bytes().get(*pos) { match byte { // Normal `idchars` production which appends to the reserved // token that's being produced. idchars!() => { idchars += 1; *pos += 1; } // https://webassembly.github.io/spec/core/text/values.html#text-string b'"' => { strings += 1; *pos += 1; let mut it = self.input[*pos..].chars(); let result = Lexer::parse_str(&mut it, self.allow_confusing_unicode); *pos = self.input.len() - it.as_str().len(); match result { Ok(_) => {} Err(e) => { let err_pos = match &e { LexError::UnexpectedEof => self.input.len(), _ => self.input[..*pos].char_indices().next_back().unwrap().0, }; return Err(self.error(err_pos, e)); } } } // Nothing else is considered part of a reserved token _ => break, } } let ret = &self.input[start..*pos]; Ok(match (idchars, strings) { (0, 0) => unreachable!(), (0, 1) => (ReservedKind::String, ret), (_, 0) => (ReservedKind::Idchars, ret), // Pattern match `@"..."` and `$"..."` for string-based // identifiers and annotations. (1, 1) if ret.starts_with("$") => (ReservedKind::IdString, ret), (1, 1) if ret.starts_with("@") => (ReservedKind::AnnotationString, ret), _ => (ReservedKind::Reserved, ret), }) } fn classify_number(&self, src: &str) -> Option { 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) }; let negative = sign == Some(SignToken::Minus); // Handle `inf` and `nan` which are special numbers here if num == "inf" { return Some(TokenKind::Float(FloatKind::Inf { negative })); } else if num == "nan" { return Some(TokenKind::Float(FloatKind::Nan { negative })); } else if let Some(stripped) = num.strip_prefix("nan:0x") { let mut it = stripped.as_bytes().iter(); let has_underscores = skip_underscores(&mut it, |x| char::from(x).is_ascii_hexdigit())?; if it.next().is_some() { return None; } return Some(TokenKind::Float(FloatKind::NanVal { negative, has_underscores, })); } // Figure out if we're a hex number or not let test_valid: fn(u8) -> bool; let (mut it, hex) = if let Some(stripped) = num.strip_prefix("0x") { test_valid = |x: u8| char::from(x).is_ascii_hexdigit(); (stripped.as_bytes().iter(), true) } else { test_valid = |x: u8| char::from(x).is_ascii_digit(); (num.as_bytes().iter(), false) }; // Evaluate the first part, moving out all underscores let mut has_underscores = skip_underscores(&mut it, test_valid)?; match it.clone().next() { // If we're followed by something this may be a float so keep going. Some(_) => {} // Otherwise this is a valid integer literal! None => { return Some(TokenKind::Integer(IntegerKind { has_underscores, sign, hex, })) } } // A number can optionally be after the decimal so only actually try to // parse one if it's there. if it.clone().next() == Some(&b'.') { it.next(); match it.clone().next() { Some(c) if test_valid(*c) => { if skip_underscores(&mut it, test_valid)? { has_underscores = true; } } Some(_) | None => {} } }; // Figure out if there's an exponential part here to make a float, and // if so parse it but defer its actual calculation until later. match (hex, it.next()) { (true, Some(b'p')) | (true, Some(b'P')) | (false, Some(b'e')) | (false, Some(b'E')) => { match it.clone().next() { Some(b'-') => { it.next(); } Some(b'+') => { it.next(); } _ => {} } if skip_underscores(&mut it, |x| char::from(x).is_ascii_digit())? { has_underscores = true; } } (_, None) => {} _ => return None, } // We should have eaten everything by now, if not then this is surely // not a float or integer literal. if it.next().is_some() { return None; } return Some(TokenKind::Float(FloatKind::Normal { has_underscores, hex, })); fn skip_underscores<'a>( it: &mut slice::Iter<'_, u8>, good: fn(u8) -> bool, ) -> Option { let mut last_underscore = false; let mut has_underscores = false; let first = *it.next()?; if !good(first) { return None; } while let Some(c) = it.clone().next() { if *c == b'_' && !last_underscore { has_underscores = true; it.next(); last_underscore = true; continue; } if !good(*c) { break; } last_underscore = false; it.next(); } if last_underscore { return None; } Some(has_underscores) } } /// Verifies that `comment`, which is about to be returned, has a "confusing /// unicode character" in it and should instead be transformed into an /// error. fn check_confusing_comment(&self, end: usize, comment: &str) -> Result<(), Error> { if self.allow_confusing_unicode { return Ok(()); } // In an effort to avoid utf-8 decoding the entire `comment` the search // here is a bit more optimized. This checks for the `0xe2` byte because // in the utf-8 encoding that's the leading encoding byte for all // "confusing characters". Each instance of 0xe2 is checked to see if it // starts a confusing character, and if so that's returned. // // Also note that 0xe2 will never be found in the middle of a codepoint, // it's always the start of a codepoint. This means that if our special // characters show up they're guaranteed to start with 0xe2 bytes. let bytes = comment.as_bytes(); for pos in memchr::Memchr::new(0xe2, bytes) { if let Some(c) = comment[pos..].chars().next() { if is_confusing_unicode(c) { // Note that `self.cur()` accounts for already having // parsed `comment`, so we move backwards to where // `comment` started and then add the index within // `comment`. let pos = end - comment.len() + pos; return Err(self.error(pos, LexError::ConfusingUnicode(c))); } } } Ok(()) } fn parse_str( it: &mut str::Chars<'a>, allow_confusing_unicode: bool, ) -> Result, LexError> { enum State { Start, String(Vec), } let orig = it.as_str(); let mut state = State::Start; loop { match it.next().ok_or(LexError::UnexpectedEof)? { '"' => break, '\\' => { match state { State::String(_) => {} State::Start => { let pos = orig.len() - it.as_str().len() - 1; state = State::String(orig[..pos].as_bytes().to_vec()); } } let buf = match &mut state { State::String(b) => b, State::Start => unreachable!(), }; match it.next().ok_or(LexError::UnexpectedEof)? { '"' => buf.push(b'"'), '\'' => buf.push(b'\''), 't' => buf.push(b'\t'), 'n' => buf.push(b'\n'), 'r' => buf.push(b'\r'), '\\' => buf.push(b'\\'), 'u' => { Lexer::must_eat_char(it, '{')?; let n = Lexer::hexnum(it)?; let c = char::from_u32(n).ok_or(LexError::InvalidUnicodeValue(n))?; buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()); Lexer::must_eat_char(it, '}')?; } c1 if c1.is_ascii_hexdigit() => { let c2 = Lexer::hexdigit(it)?; buf.push(to_hex(c1) * 16 + c2); } c => return Err(LexError::InvalidStringEscape(c)), } } c if (c as u32) < 0x20 || c as u32 == 0x7f => { return Err(LexError::InvalidStringElement(c)) } c if !allow_confusing_unicode && is_confusing_unicode(c) => { return Err(LexError::ConfusingUnicode(c)) } c => match &mut state { State::Start => {} State::String(v) => { v.extend(c.encode_utf8(&mut [0; 4]).as_bytes()); } }, } } match state { State::Start => Ok(orig[..orig.len() - it.as_str().len() - 1].as_bytes().into()), State::String(s) => Ok(s.into()), } } /// Parses an id-or-string-based name from `it`. /// /// Note that `it` should already have been lexed and this is just /// extracting the value. If the token lexed was `@a` then this should point /// to `a`. /// /// This will automatically detect quoted syntax such as `@"..."` and the /// byte string will be parsed and validated as utf-8. /// /// # Errors /// /// Returns an error if a quoted byte string is found and contains invalid /// utf-8. fn parse_name(it: &mut str::Chars<'a>) -> Result, LexError> { if it.clone().next() == Some('"') { it.next(); match Lexer::parse_str(it, true)? { Cow::Borrowed(bytes) => match std::str::from_utf8(bytes) { Ok(s) => Ok(Cow::Borrowed(s)), Err(e) => Err(LexError::InvalidUtf8Id(e)), }, Cow::Owned(bytes) => match String::from_utf8(bytes) { Ok(s) => Ok(Cow::Owned(s)), Err(e) => Err(LexError::InvalidUtf8Id(e.utf8_error())), }, } } else { Ok(Cow::Borrowed(it.as_str())) } } fn hexnum(it: &mut str::Chars<'_>) -> Result { let n = Lexer::hexdigit(it)?; let mut last_underscore = false; let mut n = n as u32; while let Some(c) = it.clone().next() { if c == '_' { it.next(); last_underscore = true; continue; } if !c.is_ascii_hexdigit() { break; } last_underscore = false; it.next(); n = n .checked_mul(16) .and_then(|n| n.checked_add(to_hex(c) as u32)) .ok_or(LexError::NumberTooBig)?; } if last_underscore { return Err(LexError::LoneUnderscore); } Ok(n) } /// Reads a hexidecimal digit from the input stream, returning where it's /// defined and the hex value. Returns an error on EOF or an invalid hex /// digit. fn hexdigit(it: &mut str::Chars<'_>) -> Result { let ch = Lexer::must_char(it)?; if ch.is_ascii_hexdigit() { Ok(to_hex(ch)) } else { Err(LexError::InvalidHexDigit(ch)) } } /// Reads the next character from the input string and where it's located, /// returning an error if the input stream is empty. fn must_char(it: &mut str::Chars<'_>) -> Result { it.next().ok_or(LexError::UnexpectedEof) } /// Expects that a specific character must be read next fn must_eat_char(it: &mut str::Chars<'_>, wanted: char) -> Result<(), LexError> { let found = Lexer::must_char(it)?; if wanted == found { Ok(()) } else { Err(LexError::Expected { wanted, found }) } } /// Creates an error at `pos` with the specified `kind` fn error(&self, pos: usize, kind: LexError) -> Error { Error::lex(Span { offset: pos }, self.input, kind) } /// Returns an iterator over all tokens in the original source string /// starting at the `pos` specified. pub fn iter(&self, mut pos: usize) -> impl Iterator> + '_ { std::iter::from_fn(move || self.parse(&mut pos).transpose()) } /// Returns whether an annotation is present at `pos`. If it is present then /// `Ok(Some(token))` is returned corresponding to the token, otherwise /// `Ok(None)` is returned. If the next token cannot be parsed then an error /// is returned. pub fn annotation(&self, mut pos: usize) -> Result, Error> { let bytes = self.input.as_bytes(); // Quickly reject anything that for sure isn't an annotation since this // method is used every time an lparen is parsed. if bytes.get(pos) != Some(&b'@') { return Ok(None); } match self.parse(&mut pos)? { Some(token) => match token.kind { TokenKind::Annotation => Ok(Some(token)), _ => Ok(None), }, None => Ok(None), } } } impl Token { /// Returns the original source text for this token. pub fn src<'a>(&self, s: &'a str) -> &'a str { &s[self.offset..][..self.len.try_into().unwrap()] } /// Returns the identifier, without the leading `$` symbol, that this token /// represents. /// /// Note that this method returns the contents of the identifier. With a /// string-based identifier this means that escapes have been resolved to /// their string-based equivalent. /// /// Should only be used with `TokenKind::Id`. /// /// # Errors /// /// Returns an error if this is a string-based identifier (e.g. `$"..."`) /// which is invalid utf-8. pub fn id<'a>(&self, s: &'a str) -> Result, Error> { let mut ch = self.src(s).chars(); let dollar = ch.next(); debug_assert_eq!(dollar, Some('$')); let id = Lexer::parse_name(&mut ch).map_err(|e| self.error(s, e))?; if id.is_empty() { return Err(self.error(s, LexError::EmptyId)); } Ok(id) } /// Returns the annotation, without the leading `@` symbol, that this token /// represents. /// /// Note that this method returns the contents of the identifier. With a /// string-based identifier this means that escapes have been resolved to /// their string-based equivalent. /// /// Should only be used with `TokenKind::Annotation`. /// /// # Errors /// /// Returns an error if this is a string-based identifier (e.g. `$"..."`) /// which is invalid utf-8. pub fn annotation<'a>(&self, s: &'a str) -> Result, Error> { let mut ch = self.src(s).chars(); let at = ch.next(); debug_assert_eq!(at, Some('@')); let id = Lexer::parse_name(&mut ch).map_err(|e| self.error(s, e))?; if id.is_empty() { return Err(self.error(s, LexError::EmptyAnnotation)); } Ok(id) } /// Returns the keyword this token represents. /// /// Should only be used with [`TokenKind::Keyword`]. pub fn keyword<'a>(&self, s: &'a str) -> &'a str { self.src(s) } /// Returns the reserved string this token represents. /// /// Should only be used with [`TokenKind::Reserved`]. pub fn reserved<'a>(&self, s: &'a str) -> &'a str { self.src(s) } /// Returns the parsed string that this token represents. /// /// This returns either a raw byte slice into the source if that's possible /// or an owned representation to handle escaped characters and such. /// /// Should only be used with [`TokenKind::String`]. pub fn string<'a>(&self, s: &'a str) -> Cow<'a, [u8]> { let mut ch = self.src(s).chars(); ch.next().unwrap(); Lexer::parse_str(&mut ch, true).unwrap() } /// Returns the decomposed float token that this represents. /// /// This will slice up the float token into its component parts and return a /// description of the float token in the source. /// /// Should only be used with [`TokenKind::Float`]. pub fn float<'a>(&self, s: &'a str, kind: FloatKind) -> Float<'a> { match kind { FloatKind::Inf { negative } => Float::Inf { negative }, FloatKind::Nan { negative } => Float::Nan { val: None, negative, }, FloatKind::NanVal { negative, has_underscores, } => { let src = self.src(s); let src = if src.starts_with("n") { src } else { &src[1..] }; let mut val = Cow::Borrowed(src.strip_prefix("nan:0x").unwrap()); if has_underscores { *val.to_mut() = val.replace("_", ""); } Float::Nan { val: Some(val), negative, } } FloatKind::Normal { has_underscores, hex, } => { let src = self.src(s); let (integral, decimal, exponent) = match src.find('.') { Some(i) => { let integral = &src[..i]; let rest = &src[i + 1..]; let exponent = if hex { rest.find('p').or_else(|| rest.find('P')) } else { rest.find('e').or_else(|| rest.find('E')) }; match exponent { Some(i) => (integral, Some(&rest[..i]), Some(&rest[i + 1..])), None => (integral, Some(rest), None), } } None => { let exponent = if hex { src.find('p').or_else(|| src.find('P')) } else { src.find('e').or_else(|| src.find('E')) }; match exponent { Some(i) => (&src[..i], None, Some(&src[i + 1..])), None => (src, None, None), } } }; let mut integral = Cow::Borrowed(integral.strip_prefix('+').unwrap_or(integral)); let mut decimal = decimal.and_then(|s| { if s.is_empty() { None } else { Some(Cow::Borrowed(s)) } }); let mut exponent = exponent.map(|s| Cow::Borrowed(s.strip_prefix('+').unwrap_or(s))); if has_underscores { *integral.to_mut() = integral.replace("_", ""); if let Some(decimal) = &mut decimal { *decimal.to_mut() = decimal.replace("_", ""); } if let Some(exponent) = &mut exponent { *exponent.to_mut() = exponent.replace("_", ""); } } if hex { *integral.to_mut() = integral.replace("0x", ""); } Float::Val { hex, integral, decimal, exponent, } } } } /// Returns the decomposed integer token that this represents. /// /// This will slice up the integer token into its component parts and /// return a description of the integer token in the source. /// /// Should only be used with [`TokenKind::Integer`]. pub fn integer<'a>(&self, s: &'a str, kind: IntegerKind) -> Integer<'a> { let src = self.src(s); let val = match kind.sign { Some(SignToken::Plus) => src.strip_prefix('+').unwrap(), Some(SignToken::Minus) => src, None => src, }; let mut val = Cow::Borrowed(val); if kind.has_underscores { *val.to_mut() = val.replace("_", ""); } if kind.hex { *val.to_mut() = val.replace("0x", ""); } Integer { sign: kind.sign, hex: kind.hex, val, } } fn error(&self, src: &str, err: LexError) -> Error { Error::lex( Span { offset: self.offset, }, src, err, ) } } impl<'a> Integer<'a> { /// Returns the sign token for this integer. pub fn sign(&self) -> Option { self.sign } /// Returns the value string that can be parsed for this integer, as well /// as the base that it should be parsed in pub fn val(&self) -> (&str, u32) { (&self.val, if self.hex { 16 } else { 10 }) } } fn to_hex(c: char) -> u8 { match c { 'a'..='f' => c as u8 - b'a' + 10, 'A'..='F' => c as u8 - b'A' + 10, _ => c as u8 - b'0', } } impl fmt::Display for LexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use LexError::*; match self { DanglingBlockComment => f.write_str("unterminated block comment")?, Unexpected(c) => write!(f, "unexpected character '{}'", escape_char(*c))?, InvalidStringElement(c) => { write!(f, "invalid character in string '{}'", escape_char(*c))? } InvalidStringEscape(c) => write!(f, "invalid string escape '{}'", escape_char(*c))?, InvalidHexDigit(c) => write!(f, "invalid hex digit '{}'", escape_char(*c))?, InvalidDigit(c) => write!(f, "invalid decimal digit '{}'", escape_char(*c))?, Expected { wanted, found } => write!( f, "expected '{}' but found '{}'", escape_char(*wanted), escape_char(*found) )?, UnexpectedEof => write!(f, "unexpected end-of-file")?, NumberTooBig => f.write_str("number is too big to parse")?, InvalidUnicodeValue(c) => write!(f, "invalid unicode scalar value 0x{:x}", c)?, LoneUnderscore => write!(f, "bare underscore in numeric literal")?, ConfusingUnicode(c) => write!(f, "likely-confusing unicode character found {:?}", c)?, InvalidUtf8Id(_) => write!(f, "malformed UTF-8 encoding of string-based id")?, EmptyId => write!(f, "empty identifier")?, EmptyAnnotation => write!(f, "empty annotation id")?, } Ok(()) } } fn escape_char(c: char) -> String { match c { '\t' => String::from("\\t"), '\r' => String::from("\\r"), '\n' => String::from("\\n"), '\\' => String::from("\\\\"), '\'' => String::from("\\\'"), '\"' => String::from("\""), '\x20'..='\x7e' => String::from(c), _ => c.escape_unicode().to_string(), } } /// This is an attempt to protect agains the "trojan source" [1] problem where /// unicode characters can cause editors to render source code differently /// for humans than the compiler itself sees. /// /// To mitigate this issue, and because it's relatively rare in practice, /// this simply rejects characters of that form. /// /// [1]: https://www.trojansource.codes/ fn is_confusing_unicode(ch: char) -> bool { matches!( ch, '\u{202a}' | '\u{202b}' | '\u{202d}' | '\u{202e}' | '\u{2066}' | '\u{2067}' | '\u{2068}' | '\u{206c}' | '\u{2069}' ) } #[cfg(test)] mod tests { use super::*; #[test] fn ws_smoke() { fn get_whitespace(input: &str) -> &str { let token = get_token(input); match token.kind { TokenKind::Whitespace => token.src(input), other => panic!("unexpected {:?}", other), } } assert_eq!(get_whitespace(" "), " "); assert_eq!(get_whitespace(" "), " "); assert_eq!(get_whitespace(" \n "), " \n "); assert_eq!(get_whitespace(" x"), " "); assert_eq!(get_whitespace(" ;"), " "); } #[test] fn line_comment_smoke() { fn get_line_comment(input: &str) -> &str { let token = get_token(input); match token.kind { TokenKind::LineComment => token.src(input), other => panic!("unexpected {:?}", other), } } assert_eq!(get_line_comment(";;"), ";;"); assert_eq!(get_line_comment(";; xyz"), ";; xyz"); assert_eq!(get_line_comment(";; xyz\nabc"), ";; xyz"); assert_eq!(get_line_comment(";;\nabc"), ";;"); assert_eq!(get_line_comment(";; \nabc"), ";; "); assert_eq!(get_line_comment(";; \rabc"), ";; "); assert_eq!(get_line_comment(";; \r\nabc"), ";; "); } #[test] fn block_comment_smoke() { fn get_block_comment(input: &str) -> &str { let token = get_token(input); match token.kind { TokenKind::BlockComment => token.src(input), other => panic!("unexpected {:?}", other), } } assert_eq!(get_block_comment("(;;)"), "(;;)"); assert_eq!(get_block_comment("(; ;)"), "(; ;)"); assert_eq!(get_block_comment("(; (;;) ;)"), "(; (;;) ;)"); } fn get_token(input: &str) -> Token { Lexer::new(input) .parse(&mut 0) .expect("no first token") .expect("no token") } #[test] fn lparen() { assert_eq!(get_token("((").kind, TokenKind::LParen); } #[test] fn rparen() { assert_eq!(get_token(")(").kind, TokenKind::RParen); } #[test] fn strings() { fn get_string(input: &str) -> Vec { let token = get_token(input); match token.kind { TokenKind::String => token.string(input).to_vec(), other => panic!("not keyword {:?}", other), } } assert_eq!(&*get_string("\"\""), b""); assert_eq!(&*get_string("\"a\""), b"a"); assert_eq!(&*get_string("\"a b c d\""), b"a b c d"); assert_eq!(&*get_string("\"\\\"\""), b"\""); assert_eq!(&*get_string("\"\\'\""), b"'"); assert_eq!(&*get_string("\"\\n\""), b"\n"); assert_eq!(&*get_string("\"\\t\""), b"\t"); assert_eq!(&*get_string("\"\\r\""), b"\r"); assert_eq!(&*get_string("\"\\\\\""), b"\\"); assert_eq!(&*get_string("\"\\01\""), &[1]); assert_eq!(&*get_string("\"\\u{1}\""), &[1]); assert_eq!( &*get_string("\"\\u{0f3}\""), '\u{0f3}'.encode_utf8(&mut [0; 4]).as_bytes() ); assert_eq!( &*get_string("\"\\u{0_f_3}\""), '\u{0f3}'.encode_utf8(&mut [0; 4]).as_bytes() ); for i in 0..=255i32 { let s = format!("\"\\{:02x}\"", i); assert_eq!(&*get_string(&s), &[i as u8]); } } #[test] fn id() { fn get_id(input: &str) -> String { let token = get_token(input); match token.kind { TokenKind::Id => token.id(input).unwrap().to_string(), other => panic!("not id {:?}", other), } } assert_eq!(get_id("$x"), "x"); assert_eq!(get_id("$xyz"), "xyz"); assert_eq!(get_id("$x_z"), "x_z"); assert_eq!(get_id("$0^"), "0^"); assert_eq!(get_id("$0^;;"), "0^"); assert_eq!(get_id("$0^ ;;"), "0^"); assert_eq!(get_id("$\"x\" ;;"), "x"); } #[test] fn annotation() { fn get_annotation(input: &str) -> String { let token = get_token(input); match token.kind { TokenKind::Annotation => token.annotation(input).unwrap().to_string(), other => panic!("not annotation {:?}", other), } } assert_eq!(get_annotation("@foo"), "foo"); assert_eq!(get_annotation("@foo "), "foo"); assert_eq!(get_annotation("@f "), "f"); assert_eq!(get_annotation("@\"x\" "), "x"); assert_eq!(get_annotation("@0 "), "0"); } #[test] fn keyword() { fn get_keyword(input: &str) -> &str { let token = get_token(input); match token.kind { TokenKind::Keyword => token.keyword(input), other => panic!("not keyword {:?}", other), } } assert_eq!(get_keyword("x"), "x"); assert_eq!(get_keyword("xyz"), "xyz"); assert_eq!(get_keyword("x_z"), "x_z"); assert_eq!(get_keyword("x_z "), "x_z"); assert_eq!(get_keyword("x_z "), "x_z"); } #[test] fn reserved() { fn get_reserved(input: &str) -> &str { let token = get_token(input); match token.kind { TokenKind::Reserved => token.reserved(input), other => panic!("not reserved {:?}", other), } } assert_eq!(get_reserved("^_x "), "^_x"); } #[test] fn integer() { fn get_integer(input: &str) -> String { let token = get_token(input); match token.kind { TokenKind::Integer(i) => token.integer(input, i).val.to_string(), other => panic!("not integer {:?}", other), } } assert_eq!(get_integer("1"), "1"); assert_eq!(get_integer("0"), "0"); assert_eq!(get_integer("-1"), "-1"); assert_eq!(get_integer("+1"), "1"); assert_eq!(get_integer("+1_000"), "1000"); assert_eq!(get_integer("+1_0_0_0"), "1000"); assert_eq!(get_integer("+0x10"), "10"); assert_eq!(get_integer("-0x10"), "-10"); assert_eq!(get_integer("0x10"), "10"); } #[test] fn float() { fn get_float(input: &str) -> Float<'_> { let token = get_token(input); match token.kind { TokenKind::Float(f) => token.float(input, f), other => panic!("not float {:?}", other), } } assert_eq!( get_float("nan"), Float::Nan { val: None, negative: false }, ); assert_eq!( get_float("-nan"), Float::Nan { val: None, negative: true, }, ); assert_eq!( get_float("+nan"), Float::Nan { val: None, negative: false, }, ); assert_eq!( get_float("+nan:0x1"), Float::Nan { val: Some("1".into()), negative: false, }, ); assert_eq!( get_float("nan:0x7f_ffff"), Float::Nan { val: Some("7fffff".into()), negative: false, }, ); assert_eq!(get_float("inf"), Float::Inf { negative: false }); assert_eq!(get_float("-inf"), Float::Inf { negative: true }); assert_eq!(get_float("+inf"), Float::Inf { negative: false }); assert_eq!( get_float("1.2"), Float::Val { integral: "1".into(), decimal: Some("2".into()), exponent: None, hex: false, }, ); assert_eq!( get_float("1.2e3"), Float::Val { integral: "1".into(), decimal: Some("2".into()), exponent: Some("3".into()), hex: false, }, ); assert_eq!( get_float("-1_2.1_1E+0_1"), Float::Val { integral: "-12".into(), decimal: Some("11".into()), exponent: Some("01".into()), hex: false, }, ); assert_eq!( get_float("+1_2.1_1E-0_1"), Float::Val { integral: "12".into(), decimal: Some("11".into()), exponent: Some("-01".into()), hex: false, }, ); assert_eq!( get_float("0x1_2.3_4p5_6"), Float::Val { integral: "12".into(), decimal: Some("34".into()), exponent: Some("56".into()), hex: true, }, ); assert_eq!( get_float("+0x1_2.3_4P-5_6"), Float::Val { integral: "12".into(), decimal: Some("34".into()), exponent: Some("-56".into()), hex: true, }, ); assert_eq!( get_float("1."), Float::Val { integral: "1".into(), decimal: None, exponent: None, hex: false, }, ); assert_eq!( get_float("0x1p-24"), Float::Val { integral: "1".into(), decimal: None, exponent: Some("-24".into()), hex: true, }, ); } } wast-217.0.0/src/lib.rs000064400000000000000000000436701046102023000126660ustar 00000000000000//! A crate for low-level parsing of the WebAssembly text formats: WAT and WAST. //! //! This crate is intended to be a low-level detail of the `wat` crate, //! providing a low-level parsing API for parsing WebAssembly text format //! structures. The API provided by this crate is very similar to //! [`syn`](https://docs.rs/syn) and provides the ability to write customized //! parsers which may be an extension to the core WebAssembly text format. For //! more documentation see the [`parser`] module. //! //! # High-level Overview //! //! This crate provides a few major pieces of functionality //! //! * [`lexer`] - this is a raw lexer for the wasm text format. This is not //! customizable, but if you'd like to iterate over raw tokens this is the //! module for you. You likely won't use this much. //! //! * [`parser`] - this is the workhorse of this crate. The [`parser`] module //! provides the [`Parse`][] trait primarily and utilities //! around working with a [`Parser`](`parser::Parser`) to parse streams of //! tokens. //! //! * [`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 //! //! This crate provides support for many in-progress WebAssembly features such //! as reference types, multi-value, etc. Be sure to check out the documentation //! of the [`wast` crate](https://docs.rs/wast) for policy information on crate //! stability vs WebAssembly Features. The tl;dr; version is that this crate //! will issue semver-non-breaking releases which will break the parsing of the //! text format. This crate, unlike `wast`, is expected to have numerous Rust //! public API changes, all of which will be accompanied with a semver-breaking //! release. //! //! # Compile-time Cargo features //! //! This crate has a `wasm-module` feature which is turned on by default which //! includes all necessary support to parse full WebAssembly modules. If you //! don't need this (for example you're parsing your own s-expression format) //! then this feature can be disabled. //! //! This crate also has an off-by-default `dwarf` feature which enables using //! [`core::EncodeOptions::dwarf`] to embed DWARF debugging information in generated //! binaries. //! //! [`Parse`]: parser::Parse //! [`LexError`]: lexer::LexError #![deny(missing_docs, rustdoc::broken_intra_doc_links)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] /// 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 { /// parser.parse::()?; /// 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(#[allow(dead_code)] pub $crate::token::Span); impl<'a> $crate::parser::Parse<'a> for $name { fn parse(parser: $crate::parser::Parser<'a>) -> $crate::parser::Result { 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<'_>) -> $crate::parser::Result { Ok(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 { /// parser.parse::()?; /// 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 { 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<'_>) -> Result { if let Some((rsv, _rest)) = cursor.reserved()? { Ok(rsv == $rsv) } else { Ok(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 { /// // 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::()?; /// 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>, /// } /// /// impl<'a> Parse<'a> for Module<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // .. 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 { Ok(Producer(parser.parse()?)) } /// # } /// # mod annotation { wast::annotation!(producer); } /// /// impl<'a> Parse<'a> for ModuleField<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // and here `peek` works and our delegated parsing works because the /// // annotation has been registered. /// if parser.peek::()? { /// 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 { 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<'_>) -> $crate::parser::Result { Ok(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; #[cfg(feature = "wasm-module")] mod encode; mod error; #[cfg(feature = "wasm-module")] mod gensym; #[cfg(feature = "wasm-module")] mod names; pub use self::error::*; #[cfg(feature = "wasm-module")] macro_rules! id { ($($t:tt)*) => ($($t)*) } #[cfg(feature = "wasm-module")] id! { mod wast; mod wat; pub use self::wast::*; pub use self::wat::*; // Support for core wasm parsing pub mod core; // Support for component model parsing pub mod component; } /// Common keyword used to parse WebAssembly text files. pub mod kw { custom_keyword!(after); custom_keyword!(alias); custom_keyword!(any); custom_keyword!(anyref); custom_keyword!(arg); custom_keyword!(array); custom_keyword!(arrayref); 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!(borrow); custom_keyword!(catch); custom_keyword!(catch_ref); custom_keyword!(catch_all); custom_keyword!(catch_all_ref); custom_keyword!(code); custom_keyword!(component); custom_keyword!(data); custom_keyword!(declare); custom_keyword!(delegate); custom_keyword!(r#do = "do"); custom_keyword!(dtor); custom_keyword!(elem); custom_keyword!(end); custom_keyword!(tag); custom_keyword!(exn); custom_keyword!(exnref); custom_keyword!(export); custom_keyword!(r#extern = "extern"); custom_keyword!(externref); custom_keyword!(eq); custom_keyword!(eqref); custom_keyword!(f32); custom_keyword!(f32x4); custom_keyword!(f64); custom_keyword!(f64x2); custom_keyword!(field); custom_keyword!(first); custom_keyword!(func); custom_keyword!(funcref); custom_keyword!(get); custom_keyword!(global); custom_keyword!(i16); custom_keyword!(i16x8); custom_keyword!(i31); custom_keyword!(i31ref); custom_keyword!(i32); custom_keyword!(i32x4); custom_keyword!(i64); custom_keyword!(i64x2); custom_keyword!(i8); custom_keyword!(i8x16); custom_keyword!(import); custom_keyword!(instance); custom_keyword!(instantiate); custom_keyword!(interface); 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!(nofunc); custom_keyword!(noextern); custom_keyword!(noexn); custom_keyword!(none); custom_keyword!(null); custom_keyword!(nullfuncref); custom_keyword!(nullexternref); custom_keyword!(nullexnref); custom_keyword!(nullref); custom_keyword!(offset); custom_keyword!(outer); custom_keyword!(own); custom_keyword!(pagesize); 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!(rec); custom_keyword!(acq_rel); custom_keyword!(rep); custom_keyword!(resource); custom_keyword!(resource_new = "resource.new"); custom_keyword!(resource_drop = "resource.drop"); custom_keyword!(resource_rep = "resource.rep"); custom_keyword!(result); custom_keyword!(seq_cst); custom_keyword!(shared); custom_keyword!(start); custom_keyword!(sub); custom_keyword!(r#final = "final"); 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!(refines); custom_keyword!(record); custom_keyword!(string); custom_keyword!(bool_ = "bool"); custom_keyword!(float32); custom_keyword!(float64); custom_keyword!(variant); custom_keyword!(flags); custom_keyword!(option); custom_keyword!(tuple); custom_keyword!(list); custom_keyword!(error); custom_keyword!(canon); custom_keyword!(lift); custom_keyword!(lower); custom_keyword!(enum_ = "enum"); custom_keyword!(string_utf8 = "string-encoding=utf8"); custom_keyword!(string_utf16 = "string-encoding=utf16"); custom_keyword!(string_latin1_utf16 = "string-encoding=latin1+utf16"); custom_keyword!(r#struct = "struct"); custom_keyword!(structref); custom_keyword!(realloc); custom_keyword!(post_return = "post-return"); custom_keyword!(with); custom_keyword!(core); custom_keyword!(true_ = "true"); custom_keyword!(false_ = "false"); custom_keyword!(language); custom_keyword!(sdk); custom_keyword!(processed_by = "processed-by"); custom_keyword!(mem_info = "mem-info"); custom_keyword!(needed); custom_keyword!(export_info = "export-info"); custom_keyword!(import_info = "import-info"); custom_keyword!(thread); custom_keyword!(wait); custom_keyword!(definition); } /// Common annotations used to parse WebAssembly text files. pub mod annotation { annotation!(custom); annotation!(name); annotation!(producers); annotation!(dylink_0 = "dylink.0"); annotation!(metadata_code_branch_hint = "metadata.code.branch_hint"); } wast-217.0.0/src/names.rs000064400000000000000000000034611046102023000132150ustar 00000000000000use crate::token::{Id, Index}; use crate::Error; use std::collections::HashMap; #[derive(Default)] pub struct Namespace<'a> { names: HashMap, u32>, count: u32, } impl<'a> Namespace<'a> { pub fn register(&mut self, name: Option>, desc: &str) -> Result { let index = self.alloc(); if let Some(name) = name { if let Some(_prev) = self.names.insert(name, index) { 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; 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: duplicate {desc} named `{}`", name.name() ), )); } Ok(()) } pub fn resolve(&self, idx: &mut Index<'a>, desc: &str) -> Result { 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!("unknown {ns}: failed to find name `${}`", id.name()), ) } wast-217.0.0/src/parser.rs000064400000000000000000001456041046102023000134140ustar 00000000000000//! Traits for parsing the WebAssembly Text format //! //! This module contains the traits, abstractions, and utilities needed to //! define custom parsers for WebAssembly text format items. This module exposes //! a recursive descent parsing strategy and centers around the [`Parse`] trait //! for defining new fragments of WebAssembly text syntax. //! //! The top-level [`parse`] function can be used to fully parse AST fragments: //! //! ``` //! use wast::Wat; //! use wast::parser::{self, ParseBuffer}; //! //! # fn foo() -> Result<(), wast::Error> { //! let wat = "(module (func))"; //! let buf = ParseBuffer::new(wat)?; //! let module = parser::parse::(&buf)?; //! # Ok(()) //! # } //! ``` //! //! and you can also define your own new syntax with the [`Parse`] trait: //! //! ``` //! 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 //! // imports must come before all the functions //! struct OnlyImportsAndFunctions<'a> { //! imports: Vec>, //! functions: Vec>, //! } //! //! impl<'a> Parse<'a> for OnlyImportsAndFunctions<'a> { //! fn parse(parser: Parser<'a>) -> Result { //! // While the second token is `import` (the first is `(`, so we care //! // about the second) we parse an `ast::ModuleImport` inside of //! // parentheses. The `parens` function here ensures that what we //! // parse inside of it is surrounded by `(` and `)`. //! let mut imports = Vec::new(); //! while parser.peek2::()? { //! let import = parser.parens(|p| p.parse())?; //! imports.push(import); //! } //! //! // Afterwards we assume everything else is a function. Note that //! // `parse` here is a generic function and type inference figures out //! // that we're parsing functions here and imports above. //! let mut functions = Vec::new(); //! while !parser.is_empty() { //! let func = parser.parens(|p| p.parse())?; //! functions.push(func); //! } //! //! Ok(OnlyImportsAndFunctions { imports, functions }) //! } //! } //! ``` //! //! This module is heavily inspired by [`syn`](https://docs.rs/syn) so you can //! likely also draw inspiration from the excellent examples in the `syn` crate. use crate::lexer::{Float, Integer, Lexer, Token, TokenKind}; use crate::token::Span; use crate::Error; use bumpalo::Bump; use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fmt; use std::usize; /// The maximum recursive depth of parens to parse. /// /// This is sort of a fundamental limitation of the way this crate is /// designed. Everything is done through recursive descent parsing which /// means, well, that we're recursively going down the stack as we parse /// nested data structures. While we can handle this for wasm expressions /// since that's a pretty local decision, handling this for nested /// modules/components which be far trickier. For now we just say that when /// the parser goes too deep we return an error saying there's too many /// nested items. It would be great to not return an error here, though! #[cfg(feature = "wasm-module")] pub(crate) const MAX_PARENS_DEPTH: usize = 100; /// A top-level convenience parsing function that parses a `T` from `buf` and /// requires that all tokens in `buf` are consume. /// /// This generic parsing function can be used to parse any `T` implementing the /// [`Parse`] trait. It is not used from [`Parse`] trait implementations. /// /// # Examples /// /// ``` /// use wast::Wat; /// use wast::parser::{self, ParseBuffer}; /// /// # fn foo() -> Result<(), wast::Error> { /// let wat = "(module (func))"; /// let buf = ParseBuffer::new(wat)?; /// let module = parser::parse::(&buf)?; /// # Ok(()) /// # } /// ``` /// /// or parsing simply a fragment /// /// ``` /// use wast::parser::{self, ParseBuffer}; /// /// # fn foo() -> Result<(), wast::Error> { /// let wat = "12"; /// let buf = ParseBuffer::new(wat)?; /// let val = parser::parse::(&buf)?; /// assert_eq!(val, 12); /// # Ok(()) /// # } /// ``` pub fn parse<'a, T: Parse<'a>>(buf: &'a ParseBuffer<'a>) -> Result { let parser = buf.parser(); let result = parser.parse()?; if parser.cursor().token()?.is_none() { Ok(result) } else { Err(parser.error("extra tokens remaining after parse")) } } /// A trait for parsing a fragment of syntax in a recursive descent fashion. /// /// 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 /// [`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. /// * The intermediate [`Parser::parse`] function to parse an item out of an /// input stream and then parse remaining items. /// /// Implementation of [`Parse`] take a [`Parser`] as input and will mutate the /// parser as they parse syntax. Once a token is consume it cannot be /// "un-consumed". Utilities such as [`Parser::peek`] and [`Parser::lookahead1`] /// can be used to determine what to parse next. /// /// ## When to parse `(` and `)`? /// /// Conventionally types are not responsible for parsing their own `(` and `)` /// tokens which surround the type. For example WebAssembly imports look like: /// /// ```text /// (import "foo" "bar" (func (type 0))) /// ``` /// /// but the [`Import`](crate::core::Import) type parser looks like: /// /// ``` /// # use wast::kw; /// # use wast::parser::{Parser, Parse, Result}; /// # struct Import<'a>(&'a str); /// impl<'a> Parse<'a> for Import<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// parser.parse::()?; /// // ... /// # panic!() /// } /// } /// ``` /// /// It is assumed here that the `(` and `)` tokens which surround an `import` /// statement in the WebAssembly text format are parsed by the parent item /// parsing `Import`. /// /// Note that this is just a convention, so it's not necessarily required for /// all types. It's recommended that your types stick to this convention where /// possible to avoid nested calls to [`Parser::parens`] or accidentally trying /// to parse too many parenthesis. /// /// # Examples /// /// Let's say you want to define your own WebAssembly text format which only /// contains imports and functions. You also require all imports to be listed /// before all functions. An example [`Parse`] implementation might look like: /// /// ``` /// 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 /// // imports must come before all the functions /// struct OnlyImportsAndFunctions<'a> { /// imports: Vec>, /// functions: Vec>, /// } /// /// impl<'a> Parse<'a> for OnlyImportsAndFunctions<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // While the second token is `import` (the first is `(`, so we care /// // about the second) we parse an `ast::ModuleImport` inside of /// // parentheses. The `parens` function here ensures that what we /// // parse inside of it is surrounded by `(` and `)`. /// let mut imports = Vec::new(); /// while parser.peek2::()? { /// let import = parser.parens(|p| p.parse())?; /// imports.push(import); /// } /// /// // Afterwards we assume everything else is a function. Note that /// // `parse` here is a generic function and type inference figures out /// // that we're parsing functions here and imports above. /// let mut functions = Vec::new(); /// while !parser.is_empty() { /// let func = parser.parens(|p| p.parse())?; /// functions.push(func); /// } /// /// Ok(OnlyImportsAndFunctions { imports, functions }) /// } /// } /// ``` pub trait Parse<'a>: Sized { /// Attempts to parse `Self` from `parser`, returning an error if it could /// not be parsed. /// /// This method will mutate the state of `parser` after attempting to parse /// an instance of `Self`. If an error happens then it is likely fatal and /// there is no guarantee of how many tokens have been consumed from /// `parser`. /// /// As recommended in the documentation of [`Parse`], implementations of /// this function should not start out by parsing `(` and `)` tokens, but /// rather parents calling recursive parsers should parse the `(` and `)` /// tokens for their child item that's being parsed. /// /// # Errors /// /// This function will return an error if `Self` could not be parsed. Note /// that creating an [`Error`] is not exactly a cheap operation, so /// [`Error`] is typically fatal and propagated all the way back to the top /// parse call site. fn parse(parser: Parser<'a>) -> Result; } impl<'a, T> Parse<'a> for Box where T: Parse<'a>, { fn parse(parser: Parser<'a>) -> Result { Ok(Box::new(parser.parse()?)) } } /// A trait for types which be used to "peek" to see if they're the next token /// in an input stream of [`Parser`]. /// /// Often when implementing [`Parse`] you'll need to query what the next token /// in the stream is to figure out what to parse next. This [`Peek`] trait /// defines the set of types that can be tested whether they're the next token /// in the input stream. /// /// Implementations of [`Peek`] should only be present on types that consume /// exactly one token (not zero, not more, exactly one). Types implementing /// [`Peek`] should also typically implement [`Parse`] should also typically /// implement [`Parse`]. /// /// See the documentation of [`Parser::peek`] for example usage. pub trait Peek { /// Tests to see whether this token is the first token within the [`Cursor`] /// specified. /// /// Returns `true` if [`Parse`] for this type is highly likely to succeed /// failing no other error conditions happening (like an integer literal /// being too big). fn peek(cursor: Cursor<'_>) -> Result; /// The same as `peek`, except it checks the token immediately following /// the current token. fn peek2(mut cursor: Cursor<'_>) -> Result { match cursor.token()? { Some(token) => cursor.advance_past(&token), None => return Ok(false), } Self::peek(cursor) } /// Returns a human-readable name of this token to display when generating /// errors about this token missing. fn display() -> &'static str; } /// A convenience type definition for `Result` where the error is hardwired to /// [`Error`]. pub type Result = std::result::Result; /// A low-level buffer of tokens which represents a completely lexed file. /// /// A `ParseBuffer` will immediately lex an entire file and then store all /// tokens internally. A `ParseBuffer` only used to pass to the top-level /// [`parse`] function. pub struct ParseBuffer<'a> { lexer: Lexer<'a>, cur: Cell, known_annotations: RefCell>, track_instr_spans: bool, depth: Cell, strings: Bump, } /// The current position within a `Lexer` that we're at. This simultaneously /// stores the byte position that the lexer was last positioned at as well as /// the next significant token. /// /// Note that "significant" here does not mean that `token` is the next token /// to be lexed at `offset`. Instead it's the next non-whitespace, /// non-annotation, non-coment token. This simple cache-of-sorts avoids /// re-parsing tokens the majority of the time, or at least that's the /// intention. /// /// If `token` is set to `None` then it means that either it hasn't been /// calculated at or the lexer is at EOF. Basically it means go talk to the /// lexer. #[derive(Copy, Clone)] struct Position { offset: usize, token: Option, } /// An in-progress parser for the tokens of a WebAssembly text file. /// /// A `Parser` is argument to the [`Parse`] trait and is now the input stream is /// interacted with to parse new items. Cloning [`Parser`] or copying a parser /// refers to the same stream of tokens to parse, you cannot clone a [`Parser`] /// and clone two items. /// /// For more information about a [`Parser`] see its methods. #[derive(Copy, Clone)] pub struct Parser<'a> { buf: &'a ParseBuffer<'a>, } /// A helpful structure to perform a lookahead of one token to determine what to /// parse. /// /// For more information see the [`Parser::lookahead1`] method. pub struct Lookahead1<'a> { parser: Parser<'a>, attempts: Vec<&'static str>, } /// An immutable cursor into a list of tokens. /// /// This cursor cannot be mutated but can be used to parse more tokens in a list /// of tokens. Cursors are created from the [`Parser::step`] method. This is a /// very low-level parsing structure and you likely won't use it much. #[derive(Copy, Clone)] pub struct Cursor<'a> { parser: Parser<'a>, pos: Position, } impl ParseBuffer<'_> { /// Creates a new [`ParseBuffer`] by lexing the given `input` completely. /// /// # Errors /// /// Returns an error if `input` fails to lex. pub fn new(input: &str) -> Result> { ParseBuffer::new_with_lexer(Lexer::new(input)) } /// Creates a new [`ParseBuffer`] by lexing the given `input` completely. /// /// # Errors /// /// Returns an error if `input` fails to lex. pub fn new_with_lexer(lexer: Lexer<'_>) -> Result> { Ok(ParseBuffer { lexer, depth: Cell::new(0), cur: Cell::new(Position { offset: 0, token: None, }), known_annotations: Default::default(), strings: Default::default(), track_instr_spans: false, }) } /// Indicates whether the [`Expression::instr_spans`] field will be filled /// in. /// /// This is useful when enabling DWARF debugging information via /// [`EncodeOptions::dwarf`], for example. /// /// [`Expression::instr_spans`]: crate::core::Expression::instr_spans /// [`EncodeOptions::dwarf`]: crate::core::EncodeOptions::dwarf pub fn track_instr_spans(&mut self, track: bool) -> &mut Self { self.track_instr_spans = track; self } fn parser(&self) -> Parser<'_> { Parser { buf: self } } /// Stores an owned allocation in this `Parser` to attach the lifetime of /// the vector to `self`. /// /// This will return a reference to `s`, but one that's safely rooted in the /// `Parser`. fn push_str(&self, s: Vec) -> &[u8] { self.strings.alloc_slice_copy(&s) } /// Lexes the next "significant" token from the `pos` specified. /// /// This will skip irrelevant tokens such as whitespace, comments, and /// unknown annotations. fn advance_token(&self, mut pos: usize) -> Result> { let token = loop { let token = match self.lexer.parse(&mut pos)? { Some(token) => token, None => return Ok(None), }; match token.kind { // Always skip whitespace and comments. TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment => { continue } // If an lparen is seen then this may be skipped if it's an // annotation of the form `(@foo ...)`. In this situation // everything up to and including the closing rparen is skipped. // // Note that the annotation is only skipped if it's an unknown // annotation as known annotations are specifically registered // as "someone's gonna parse this". TokenKind::LParen => { if let Some(annotation) = self.lexer.annotation(pos)? { let text = annotation.annotation(self.lexer.input())?; match self.known_annotations.borrow().get(&text[..]) { Some(0) | None => { self.skip_annotation(&mut pos)?; continue; } Some(_) => {} } } break token; } _ => break token, } }; Ok(Some(token)) } fn skip_annotation(&self, pos: &mut usize) -> Result<()> { let mut depth = 1; let span = Span { offset: *pos }; loop { let token = match self.lexer.parse(pos)? { Some(token) => token, None => { break Err(Error::new(span, "unclosed annotation".to_string())); } }; match token.kind { TokenKind::LParen => depth += 1, TokenKind::RParen => { depth -= 1; if depth == 0 { break Ok(()); } } _ => {} } } } } impl<'a> Parser<'a> { /// Returns whether there are no more `Token` tokens to parse from this /// [`Parser`]. /// /// This indicates that either we've reached the end of the input, or we're /// a sub-[`Parser`] inside of a parenthesized expression and we've hit the /// `)` token. /// /// Note that if `false` is returned there *may* be more comments. Comments /// and whitespace are not considered for whether this parser is empty. pub fn is_empty(self) -> bool { match self.cursor().token() { Ok(Some(token)) => matches!(token.kind, TokenKind::RParen), Ok(None) => true, Err(_) => false, } } #[cfg(feature = "wasm-module")] pub(crate) fn has_meaningful_tokens(self) -> bool { self.buf.lexer.iter(0).any(|t| match t { Ok(token) => !matches!( token.kind, TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment ), Err(_) => true, }) } /// Parses a `T` from this [`Parser`]. /// /// This method has a trivial definition (it simply calls /// [`T::parse`](Parse::parse)) but is here for syntactic purposes. This is /// what you'll call 99% of the time in a [`Parse`] implementation in order /// to parse sub-items. /// /// Typically you always want to use `?` with the result of this method, you /// should not handle errors and decide what else to parse. To handle /// branches in parsing, use [`Parser::peek`]. /// /// # Examples /// /// A good example of using `parse` is to see how the [`TableType`] type is /// parsed in this crate. A [`TableType`] is defined in the official /// specification as [`tabletype`][spec] and is defined as: /// /// [spec]: https://webassembly.github.io/spec/core/text/types.html#table-types /// /// ```text /// tabletype ::= lim:limits et:reftype /// ``` /// /// so to parse a [`TableType`] we recursively need to parse a [`Limits`] /// and a [`RefType`] /// /// ``` /// # use wast::core::*; /// # use wast::parser::*; /// struct TableType<'a> { /// limits: Limits, /// elem: RefType<'a>, /// } /// /// impl<'a> Parse<'a> for TableType<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // parse the `lim` then `et` in sequence /// Ok(TableType { /// limits: parser.parse()?, /// elem: parser.parse()?, /// }) /// } /// } /// ``` /// /// [`Limits`]: crate::core::Limits /// [`TableType`]: crate::core::TableType /// [`RefType`]: crate::core::RefType pub fn parse>(self) -> Result { T::parse(self) } /// Performs a cheap test to see whether the current token in this stream is /// `T`. /// /// This method can be used to efficiently determine what next to parse. The /// [`Peek`] trait is defined for types which can be used to test if they're /// the next item in the input stream. /// /// Nothing is actually parsed in this method, nor does this mutate the /// state of this [`Parser`]. Instead, this simply performs a check. /// /// This method is frequently combined with the [`Parser::lookahead1`] /// method to automatically produce nice error messages if some tokens /// aren't found. /// /// # Examples /// /// For an example of using the `peek` method let's take a look at parsing /// the [`Limits`] type. This is [defined in the official spec][spec] as: /// /// ```text /// limits ::= n:u32 /// | n:u32 m:u32 /// ``` /// /// which means that it's either one `u32` token or two, so we need to know /// whether to consume two tokens or one: /// /// ``` /// # use wast::parser::*; /// struct Limits { /// min: u32, /// max: Option, /// } /// /// impl<'a> Parse<'a> for Limits { /// fn parse(parser: Parser<'a>) -> Result { /// // Always parse the first number... /// let min = parser.parse()?; /// /// // ... and then test if there's a second number before parsing /// let max = if parser.peek::()? { /// Some(parser.parse()?) /// } else { /// None /// }; /// /// Ok(Limits { min, max }) /// } /// } /// ``` /// /// [spec]: https://webassembly.github.io/spec/core/text/types.html#limits /// [`Limits`]: crate::core::Limits pub fn peek(self) -> Result { T::peek(self.cursor()) } /// Same as the [`Parser::peek`] method, except checks the next token, not /// the current token. pub fn peek2(self) -> Result { T::peek2(self.cursor()) } /// Same as the [`Parser::peek2`] method, except checks the next next token, /// not the next token. pub fn peek3(self) -> Result { let mut cursor = self.cursor(); match cursor.token()? { Some(token) => cursor.advance_past(&token), None => return Ok(false), } match cursor.token()? { Some(token) => cursor.advance_past(&token), None => return Ok(false), } T::peek(cursor) } /// A helper structure to perform a sequence of `peek` operations and if /// they all fail produce a nice error message. /// /// This method purely exists for conveniently producing error messages and /// provides no functionality that [`Parser::peek`] doesn't already give. /// The [`Lookahead1`] structure has one main method [`Lookahead1::peek`], /// which is the same method as [`Parser::peek`]. The difference is that the /// [`Lookahead1::error`] method needs no arguments. /// /// # Examples /// /// Let's look at the parsing of [`Index`]. This type is either a `u32` or /// an [`Id`] and is used in name resolution primarily. The [official /// grammar for an index][spec] is: /// /// ```text /// idx ::= x:u32 /// | v:id /// ``` /// /// Which is to say that an index is either a `u32` or an [`Id`]. When /// parsing an [`Index`] we can do: /// /// ``` /// # use wast::token::*; /// # use wast::parser::*; /// enum Index<'a> { /// Num(u32), /// Id(Id<'a>), /// } /// /// impl<'a> Parse<'a> for Index<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// let mut l = parser.lookahead1(); /// if l.peek::()? { /// Ok(Index::Id(parser.parse()?)) /// } else if l.peek::()? { /// Ok(Index::Num(parser.parse()?)) /// } else { /// // produces error message of `expected identifier or u32` /// Err(l.error()) /// } /// } /// } /// ``` /// /// [spec]: https://webassembly.github.io/spec/core/text/modules.html#indices /// [`Index`]: crate::token::Index /// [`Id`]: crate::token::Id pub fn lookahead1(self) -> Lookahead1<'a> { Lookahead1 { attempts: Vec::new(), parser: self, } } /// Parse an item surrounded by parentheses. /// /// WebAssembly's text format is all based on s-expressions, so naturally /// you're going to want to parse a lot of parenthesized things! As noted in /// the documentation of [`Parse`] you typically don't parse your own /// surrounding `(` and `)` tokens, but the parser above you parsed them for /// you. This is method method the parser above you uses. /// /// This method will parse a `(` token, and then call `f` on a sub-parser /// which when finished asserts that a `)` token is the next token. This /// requires that `f` consumes all tokens leading up to the paired `)`. /// /// Usage will often simply be `parser.parens(|p| p.parse())?` to /// automatically parse a type within parentheses, but you can, as always, /// go crazy and do whatever you'd like too. /// /// # Examples /// /// A good example of this is to see how a `Module` is parsed. This isn't /// the exact definition, but it's close enough! /// /// ``` /// # use wast::kw; /// # use wast::core::*; /// # use wast::parser::*; /// struct Module<'a> { /// fields: Vec>, /// } /// /// impl<'a> Parse<'a> for Module<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // Modules start out with a `module` keyword /// parser.parse::()?; /// /// // And then everything else is `(field ...)`, so while we've got /// // items left we continuously parse parenthesized items. /// let mut fields = Vec::new(); /// while !parser.is_empty() { /// fields.push(parser.parens(|p| p.parse())?); /// } /// Ok(Module { fields }) /// } /// } /// ``` pub fn parens(self, f: impl FnOnce(Parser<'a>) -> Result) -> Result { self.buf.depth.set(self.buf.depth.get() + 1); let before = self.buf.cur.get(); let res = self.step(|cursor| { let mut cursor = match cursor.lparen()? { Some(rest) => rest, None => return Err(cursor.error("expected `(`")), }; cursor.parser.buf.cur.set(cursor.pos); let result = f(cursor.parser)?; // Reset our cursor's state to whatever the current state of the // parser is. cursor.pos = cursor.parser.buf.cur.get(); match cursor.rparen()? { Some(rest) => Ok((result, rest)), None => Err(cursor.error("expected `)`")), } }); self.buf.depth.set(self.buf.depth.get() - 1); if res.is_err() { self.buf.cur.set(before); } res } /// Return the depth of nested parens we've parsed so far. /// /// This is a low-level method that is only useful for implementing /// recursion limits in custom parsers. pub fn parens_depth(&self) -> usize { self.buf.depth.get() } /// Checks that the parser parens depth hasn't exceeded the maximum depth. #[cfg(feature = "wasm-module")] pub(crate) fn depth_check(&self) -> Result<()> { if self.parens_depth() > MAX_PARENS_DEPTH { Err(self.error("item nesting too deep")) } else { Ok(()) } } fn cursor(self) -> Cursor<'a> { Cursor { parser: self, pos: self.buf.cur.get(), } } /// A low-level parsing method you probably won't use. /// /// This is used to implement parsing of the most primitive types in the /// [`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(self, f: F) -> Result where F: FnOnce(Cursor<'a>) -> Result<(T, Cursor<'a>)>, { let (result, cursor) = f(self.cursor())?; self.buf.cur.set(cursor.pos); Ok(result) } /// Creates an error whose line/column information is pointing at the /// current token. /// /// This is used to produce human-readable error messages which point to the /// right location in the input stream, and the `msg` here is arbitrary text /// used to associate with the error and indicate why it was generated. pub fn error(self, msg: impl fmt::Display) -> Error { self.error_at(self.cursor().cur_span(), msg) } /// Creates an error whose line/column information is pointing at the /// given span. pub fn error_at(self, span: Span, msg: impl fmt::Display) -> Error { Error::parse(span, self.buf.lexer.input(), msg.to_string()) } /// Returns the span of the current token pub fn cur_span(&self) -> Span { self.cursor().cur_span() } /// Returns the span of the previous token pub fn prev_span(&self) -> Span { self.cursor() .prev_span() .unwrap_or_else(|| Span::from_offset(0)) } /// Registers a new known annotation with this parser to allow parsing /// annotations with this name. /// /// [WebAssembly annotations][annotation] are a proposal for the text format /// which allows decorating the text format with custom structured /// information. By default all annotations are ignored when parsing, but /// the whole purpose of them is to sometimes parse them! /// /// To support parsing text annotations this method is used to allow /// annotations and their tokens to *not* be skipped. Once an annotation is /// registered with this method, then while the return value has not been /// dropped (e.g. the scope of where this function is called) annotations /// with the name `annotation` will be parse of the token stream and not /// implicitly skipped. /// /// # Skipping annotations /// /// The behavior of skipping unknown/unregistered annotations can be /// somewhat subtle and surprising, so if you're interested in parsing /// annotations it's important to point out the importance of this method /// and where to call it. /// /// Generally when parsing tokens you'll be bottoming out in various /// `Cursor` methods. These are all documented as advancing the stream as /// much as possible to the next token, skipping "irrelevant stuff" like /// comments, whitespace, etc. The `Cursor` methods will also skip unknown /// annotations. This means that if you parse *any* token, it will skip over /// any number of annotations that are unknown at all times. /// /// To parse an annotation you must, before parsing any token of the /// annotation, register the annotation via this method. This includes the /// beginning `(` token, which is otherwise skipped if the annotation isn't /// marked as registered. Typically parser parse the *contents* of an /// s-expression, so this means that the outer parser of an s-expression /// must register the custom annotation name, rather than the inner parser. /// /// # Return /// /// This function returns an RAII guard which, when dropped, will unregister /// the `annotation` given. Parsing `annotation` is only supported while the /// returned value is still alive, and once dropped the parser will go back /// to skipping annotations with the name `annotation`. /// /// # Example /// /// Let's see an example of how the `@name` annotation is parsed for modules /// to get an idea of how this works: /// /// ``` /// # use wast::kw; /// # use wast::token::NameAnnotation; /// # use wast::parser::*; /// struct Module<'a> { /// name: Option>, /// } /// /// impl<'a> Parse<'a> for Module<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // Modules start out with a `module` keyword /// parser.parse::()?; /// /// // Next may be `(@name "foo")`. Typically this annotation would /// // skipped, but we don't want it skipped, so we register it. /// // Note that the parse implementation of /// // `Option` is the one that consumes the /// // parentheses here. /// let _r = parser.register_annotation("name"); /// let name = parser.parse()?; /// /// // ... and normally you'd otherwise parse module fields here ... /// /// Ok(Module { name }) /// } /// } /// ``` /// /// Another example is how we parse the `@custom` annotation. Note that this /// is parsed as part of `ModuleField`, so note how the annotation is /// registered *before* we parse the parentheses of the annotation. /// /// ``` /// # use wast::{kw, annotation}; /// # use wast::core::Custom; /// # use wast::parser::*; /// struct Module<'a> { /// fields: Vec>, /// } /// /// impl<'a> Parse<'a> for Module<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // Modules start out with a `module` keyword /// parser.parse::()?; /// /// // register the `@custom` annotation *first* before we start /// // parsing fields, because each field is contained in /// // parentheses and to parse the parentheses of an annotation we /// // have to known to not skip it. /// let _r = parser.register_annotation("custom"); /// /// let mut fields = Vec::new(); /// while !parser.is_empty() { /// fields.push(parser.parens(|p| p.parse())?); /// } /// Ok(Module { fields }) /// } /// } /// /// enum ModuleField<'a> { /// Custom(Custom<'a>), /// // ... /// } /// /// impl<'a> Parse<'a> for ModuleField<'a> { /// fn parse(parser: Parser<'a>) -> Result { /// // Note that because we have previously registered the `@custom` /// // annotation with the parser we known that `peek` methods like /// // this, working on the annotation token, are enabled to ever /// // return `true`. /// if parser.peek::()? { /// return Ok(ModuleField::Custom(parser.parse()?)); /// } /// /// // .. typically we'd parse other module fields here... /// /// Err(parser.error("unknown module field")) /// } /// } /// ``` /// /// [annotation]: https://github.com/WebAssembly/annotations pub fn register_annotation<'b>(self, annotation: &'b str) -> impl Drop + 'b where 'a: 'b, { let mut annotations = self.buf.known_annotations.borrow_mut(); if !annotations.contains_key(annotation) { annotations.insert(annotation.to_string(), 0); } *annotations.get_mut(annotation).unwrap() += 1; return RemoveOnDrop(self, annotation); struct RemoveOnDrop<'a>(Parser<'a>, &'a str); impl Drop for RemoveOnDrop<'_> { fn drop(&mut self) { let mut annotations = self.0.buf.known_annotations.borrow_mut(); let slot = annotations.get_mut(self.1).unwrap(); *slot -= 1; } } } #[cfg(feature = "wasm-module")] pub(crate) fn track_instr_spans(&self) -> bool { self.buf.track_instr_spans } #[cfg(feature = "wasm-module")] pub(crate) fn with_standard_annotations_registered( self, f: impl FnOnce(Self) -> Result, ) -> Result { let _r = self.register_annotation("custom"); let _r = self.register_annotation("producers"); let _r = self.register_annotation("name"); let _r = self.register_annotation("dylink.0"); let _r = self.register_annotation("metadata.code.branch_hint"); f(self) } } impl<'a> Cursor<'a> { /// Returns the span of the next `Token` token. /// /// Does not take into account whitespace or comments. pub fn cur_span(&self) -> Span { let offset = match self.token() { Ok(Some(t)) => t.offset, Ok(None) => self.parser.buf.lexer.input().len(), Err(_) => self.pos.offset, }; Span { offset } } /// Returns the span of the previous `Token` token. /// /// Does not take into account whitespace or comments. pub(crate) fn prev_span(&self) -> Option { // TODO Some(Span { offset: self.pos.offset, }) // let (token, _) = self.parser.buf.tokens.get(self.cur.checked_sub(1)?)?; // Some(Span { // offset: token.offset, // }) } /// Same as [`Parser::error`], but works with the current token in this /// [`Cursor`] instead. pub fn error(&self, msg: impl fmt::Display) -> Error { self.parser.error_at(self.cur_span(), msg) } /// Tests whether the next token is an lparen pub fn peek_lparen(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::LParen, .. }) )) } /// Tests whether the next token is an rparen pub fn peek_rparen(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::RParen, .. }) )) } /// Tests whether the next token is an id pub fn peek_id(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::Id, .. }) )) } /// Tests whether the next token is reserved pub fn peek_reserved(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::Reserved, .. }) )) } /// Tests whether the next token is a keyword pub fn peek_keyword(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::Keyword, .. }) )) } /// Tests whether the next token is an integer pub fn peek_integer(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::Integer(_), .. }) )) } /// Tests whether the next token is a float pub fn peek_float(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::Float(_), .. }) )) } /// Tests whether the next token is a string pub fn peek_string(self) -> Result { Ok(matches!( self.token()?, Some(Token { kind: TokenKind::String, .. }) )) } /// Attempts to advance this cursor if the current token is a `(`. /// /// If the current token is `(`, returns a new [`Cursor`] pointing at the /// rest of the tokens in the stream. Otherwise returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn lparen(mut self) -> Result> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::LParen => {} _ => return Ok(None), } self.advance_past(&token); Ok(Some(self)) } /// Attempts to advance this cursor if the current token is a `)`. /// /// If the current token is `)`, returns a new [`Cursor`] pointing at the /// rest of the tokens in the stream. Otherwise returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn rparen(mut self) -> Result> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::RParen => {} _ => return Ok(None), } self.advance_past(&token); Ok(Some(self)) } /// Attempts to advance this cursor if the current token is a /// [`Token::Id`](crate::lexer::Token) /// /// If the current token is `Id`, returns the identifier minus the leading /// `$` character as well as a new [`Cursor`] pointing at the rest of the /// tokens in the stream. Otherwise returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn id(mut self) -> Result> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::Id => {} _ => return Ok(None), } self.advance_past(&token); let id = match token.id(self.parser.buf.lexer.input())? { Cow::Borrowed(id) => id, // Our `self.parser.buf` only retains `Vec` so briefly convert // this owned string to `Vec` and then convert it back to `&str` // out the other end. Cow::Owned(s) => std::str::from_utf8(self.parser.buf.push_str(s.into_bytes())).unwrap(), }; Ok(Some((id, self))) } /// Attempts to advance this cursor if the current token is a /// [`Token::Keyword`](crate::lexer::Token) /// /// If the current token is `Keyword`, returns the keyword as well as a new /// [`Cursor`] pointing at the rest of the tokens in the stream. Otherwise /// returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn keyword(mut self) -> Result> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::Keyword => {} _ => return Ok(None), } self.advance_past(&token); Ok(Some((token.keyword(self.parser.buf.lexer.input()), self))) } /// Attempts to advance this cursor if the current token is a /// [`Token::Annotation`](crate::lexer::Token) /// /// If the current token is `Annotation`, returns the annotation token as well /// as a new [`Cursor`] pointing at the rest of the tokens in the stream. /// Otherwise returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn annotation(mut self) -> Result> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::Annotation => {} _ => return Ok(None), } self.advance_past(&token); let annotation = match token.annotation(self.parser.buf.lexer.input())? { Cow::Borrowed(id) => id, // Our `self.parser.buf` only retains `Vec` so briefly convert // this owned string to `Vec` and then convert it back to `&str` // out the other end. Cow::Owned(s) => std::str::from_utf8(self.parser.buf.push_str(s.into_bytes())).unwrap(), }; Ok(Some((annotation, self))) } /// Attempts to advance this cursor if the current token is a /// [`Token::Reserved`](crate::lexer::Token) /// /// If the current token is `Reserved`, returns the reserved token as well /// as a new [`Cursor`] pointing at the rest of the tokens in the stream. /// Otherwise returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn reserved(mut self) -> Result> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::Reserved => {} _ => return Ok(None), } self.advance_past(&token); Ok(Some((token.reserved(self.parser.buf.lexer.input()), self))) } /// Attempts to advance this cursor if the current token is a /// [`Token::Integer`](crate::lexer::Token) /// /// If the current token is `Integer`, returns the integer as well as a new /// [`Cursor`] pointing at the rest of the tokens in the stream. Otherwise /// returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn integer(mut self) -> Result, Self)>> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; let i = match token.kind { TokenKind::Integer(i) => i, _ => return Ok(None), }; self.advance_past(&token); Ok(Some(( token.integer(self.parser.buf.lexer.input(), i), self, ))) } /// Attempts to advance this cursor if the current token is a /// [`Token::Float`](crate::lexer::Token) /// /// If the current token is `Float`, returns the float as well as a new /// [`Cursor`] pointing at the rest of the tokens in the stream. Otherwise /// returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn float(mut self) -> Result, Self)>> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; let f = match token.kind { TokenKind::Float(f) => f, _ => return Ok(None), }; self.advance_past(&token); Ok(Some((token.float(self.parser.buf.lexer.input(), f), self))) } /// Attempts to advance this cursor if the current token is a /// [`Token::String`](crate::lexer::Token) /// /// If the current token is `String`, returns the byte value of the string /// as well as a new [`Cursor`] pointing at the rest of the tokens in the /// stream. Otherwise returns `None`. /// /// This function will automatically skip over any comments, whitespace, or /// unknown annotations. pub fn string(mut self) -> Result> { let token = match self.token()? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::String => {} _ => return Ok(None), } let string = match token.string(self.parser.buf.lexer.input()) { Cow::Borrowed(s) => s, Cow::Owned(s) => self.parser.buf.push_str(s), }; self.advance_past(&token); Ok(Some((string, self))) } /// Attempts to advance this cursor if the current token is a /// [`Token::LineComment`](crate::lexer::Token) or a /// [`Token::BlockComment`](crate::lexer::Token) /// /// This function will only skip whitespace, no other tokens. pub fn comment(mut self) -> Result> { let start = self.pos.offset; self.pos.token = None; let comment = loop { let token = match self.parser.buf.lexer.parse(&mut self.pos.offset)? { Some(token) => token, None => return Ok(None), }; match token.kind { TokenKind::LineComment | TokenKind::BlockComment => { break token.src(self.parser.buf.lexer.input()); } TokenKind::Whitespace => {} _ => { self.pos.offset = start; return Ok(None); } } }; Ok(Some((comment, self))) } fn token(&self) -> Result> { match self.pos.token { Some(token) => Ok(Some(token)), None => self.parser.buf.advance_token(self.pos.offset), } } fn advance_past(&mut self, token: &Token) { self.pos.offset = token.offset + (token.len as usize); self.pos.token = self .parser .buf .advance_token(self.pos.offset) .unwrap_or(None); } } impl Lookahead1<'_> { /// Attempts to see if `T` is the next token in the [`Parser`] this /// [`Lookahead1`] references. /// /// For more information see [`Parser::lookahead1`] and [`Parser::peek`] pub fn peek(&mut self) -> Result { Ok(if self.parser.peek::()? { true } else { self.attempts.push(T::display()); false }) } /// Generates an error message saying that one of the tokens passed to /// [`Lookahead1::peek`] method was expected. /// /// Before calling this method you should call [`Lookahead1::peek`] for all /// possible tokens you'd like to parse. pub fn error(self) -> Error { match self.attempts.len() { 0 => { if self.parser.is_empty() { self.parser.error("unexpected end of input") } else { self.parser.error("unexpected token") } } 1 => { let message = format!("unexpected token, expected {}", self.attempts[0]); self.parser.error(&message) } 2 => { let message = format!( "unexpected token, expected {} or {}", self.attempts[0], self.attempts[1] ); self.parser.error(&message) } _ => { let join = self.attempts.join(", "); let message = format!("unexpected token, expected one of: {}", join); self.parser.error(&message) } } } } impl<'a, T: Peek + Parse<'a>> Parse<'a> for Option { fn parse(parser: Parser<'a>) -> Result> { if parser.peek::()? { Ok(Some(parser.parse()?)) } else { Ok(None) } } } wast-217.0.0/src/token.rs000064400000000000000000000556421046102023000132420ustar 00000000000000//! 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::Float; use crate::parser::{Cursor, Parse, Parser, Peek, Result}; use std::fmt; use std::hash::{Hash, Hasher}; use std::str; /// A position in the original source stream, used to render errors. #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct Span { pub(crate) offset: usize, } impl Span { /// Construct a `Span` from a byte offset in the source file. pub fn from_offset(offset: usize) -> Self { Span { offset } } /// Returns the line/column information of this span within `text`. /// Line and column numbers are 0-indexed. User presentation is typically /// 1-indexed, but 0-indexing is appropriate for internal use with /// iterators and slices. pub fn linecol_in(&self, text: &str) -> (usize, usize) { let mut cur = 0; // Use split_terminator instead of lines so that if there is a `\r`, // it is included in the offset calculation. The `+1` values below // account for the `\n`. for (i, line) in text.split_terminator('\n').enumerate() { if cur + line.len() + 1 > self.offset { return (i, self.offset - cur); } cur += line.len() + 1; } (text.lines().count(), 0) } /// Returns the byte offset of this span. pub fn offset(&self) -> usize { self.offset } } /// An identifier in a WebAssembly module, prefixed by `$` in the textual /// format. /// /// An identifier is used to symbolically refer to items in a a wasm module, /// typically via the [`Index`] type. #[derive(Copy, Clone)] pub struct Id<'a> { name: &'a str, gen: u32, span: Span, } impl<'a> Id<'a> { /// Construct a new identifier from given string. /// /// Note that `name` can be any arbitrary string according to the /// WebAssembly/annotations proposal. pub fn new(name: &'a str, span: Span) -> Id<'a> { Id { name, gen: 0, span } } #[cfg(feature = "wasm-module")] pub(crate) fn gensym(span: Span, gen: u32) -> Id<'a> { Id { name: "gensym", gen, span, } } /// Returns the underlying name of this identifier. /// /// The name returned does not contain the leading `$`. pub fn name(&self) -> &'a str { self.name } /// Returns span of this identifier in the original source pub fn span(&self) -> Span { self.span } #[cfg(feature = "wasm-module")] pub(crate) fn is_gensym(&self) -> bool { self.gen != 0 } } impl<'a> Hash for Id<'a> { fn hash(&self, hasher: &mut H) { self.name.hash(hasher); self.gen.hash(hasher); } } impl<'a> PartialEq for Id<'a> { fn eq(&self, other: &Id<'a>) -> bool { self.name == other.name && self.gen == other.gen } } impl<'a> Eq for Id<'a> {} impl<'a> Parse<'a> for Id<'a> { fn parse(parser: Parser<'a>) -> Result { parser.step(|c| { if let Some((name, rest)) = c.id()? { return Ok(( Id { name, gen: 0, span: c.cur_span(), }, rest, )); } Err(c.error("expected an identifier")) }) } } impl fmt::Debug for Id<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.gen != 0 { f.debug_struct("Id").field("gen", &self.gen).finish() } else { self.name.fmt(f) } } } impl Peek for Id<'_> { fn peek(cursor: Cursor<'_>) -> Result { cursor.peek_id() } fn display() -> &'static str { "an identifier" } } /// A reference to another item in a wasm module. /// /// This type is used for items referring to other items (such as `call $foo` /// referencing function `$foo`). References can be either an index (u32) or an /// [`Id`] in the textual format. /// /// The emission phase of a module will ensure that `Index::Id` is never used /// and switch them all to `Index::Num`. #[derive(Copy, Clone, Debug)] pub enum Index<'a> { /// A numerical index that this references. The index space this is /// referencing is implicit based on where this [`Index`] is stored. Num(u32, Span), /// A human-readable identifier this references. Like `Num`, the namespace /// this references is based on where this is stored. Id(Id<'a>), } impl Index<'_> { /// Returns the source location where this `Index` was defined. pub fn span(&self) -> Span { match self { Index::Num(_, span) => *span, Index::Id(id) => id.span(), } } #[cfg(feature = "wasm-module")] pub(crate) fn is_resolved(&self) -> bool { matches!(self, Index::Num(..)) } } impl<'a> Parse<'a> for Index<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::()? { Ok(Index::Id(parser.parse()?)) } else if parser.peek::()? { let (val, span) = parser.parse()?; Ok(Index::Num(val, span)) } else { Err(parser.error(format!( "unexpected token, expected an index or an identifier" ))) } } } impl Peek for Index<'_> { fn peek(cursor: Cursor<'_>) -> Result { Ok(u32::peek(cursor)? || Id::peek(cursor)?) } fn display() -> &'static str { "an index" } } impl<'a> From> for Index<'a> { fn from(id: Id<'a>) -> Index<'a> { Index::Id(id) } } impl PartialEq for Index<'_> { fn eq(&self, other: &Index<'_>) -> bool { match (self, other) { (Index::Num(a, _), Index::Num(b, _)) => a == b, (Index::Id(a), Index::Id(b)) => a == b, _ => false, } } } impl Eq for Index<'_> {} impl Hash for Index<'_> { fn hash(&self, hasher: &mut H) { match self { Index::Num(a, _) => { 0u8.hash(hasher); a.hash(hasher); } Index::Id(a) => { 1u8.hash(hasher); a.hash(hasher); } } } } /// Parses `(func $foo)` #[derive(Clone, Debug)] #[allow(missing_docs)] 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 { parser.parens(|parser| { let kind = parser.parse::()?; let idx = parser.parse()?; Ok(ItemRef { kind, idx }) }) } } impl<'a, K: Peek> Peek for ItemRef<'a, K> { fn peek(cursor: Cursor<'_>) -> Result { match cursor.lparen()? { Some(remaining) => K::peek(remaining), None => Ok(false), } } fn display() -> &'static str { "an item reference" } } /// An `@name` annotation in source, currently of the form `@name "foo"` #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct NameAnnotation<'a> { /// The name specified for the item pub name: &'a str, } impl<'a> Parse<'a> for NameAnnotation<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; let name = parser.parse()?; Ok(NameAnnotation { name }) } } impl<'a> Parse<'a> for Option> { fn parse(parser: Parser<'a>) -> Result { Ok(if parser.peek2::()? { Some(parser.parens(|p| p.parse())?) } else { None }) } } macro_rules! integers { ($($i:ident($u:ident))*) => ($( impl<'a> Parse<'a> for $i { fn parse(parser: Parser<'a>) -> Result { Ok(parser.parse::<($i, Span)>()?.0) } } impl<'a> Parse<'a> for ($i, Span) { fn parse(parser: Parser<'a>) -> Result { parser.step(|c| { if let Some((i, rest)) = c.integer()? { let (s, base) = i.val(); let val = $i::from_str_radix(s, base) .or_else(|_| { $u::from_str_radix(s, base).map(|i| i as $i) }); return match val { Ok(n) => Ok(((n, c.cur_span()), rest)), Err(_) => Err(c.error(concat!( "invalid ", stringify!($i), " number: constant out of range", ))), }; } Err(c.error(concat!("expected a ", stringify!($i)))) }) } } impl Peek for $i { fn peek(cursor: Cursor<'_>) -> Result { cursor.peek_integer() } fn display() -> &'static str { stringify!($i) } } )*) } integers! { u8(u8) u16(u16) u32(u32) u64(u64) i8(u8) i16(u16) i32(u32) i64(u64) } impl<'a> Parse<'a> for &'a [u8] { fn parse(parser: Parser<'a>) -> Result { parser.step(|c| { if let Some((i, rest)) = c.string()? { return Ok((i, rest)); } Err(c.error("expected a string")) }) } } impl Peek for &'_ [u8] { fn peek(cursor: Cursor<'_>) -> Result { cursor.peek_string() } fn display() -> &'static str { "string" } } impl<'a> Parse<'a> for &'a str { fn parse(parser: Parser<'a>) -> Result { str::from_utf8(parser.parse()?) .map_err(|_| parser.error_at(parser.prev_span(), "malformed UTF-8 encoding")) } } impl Parse<'_> for String { fn parse(parser: Parser<'_>) -> Result { Ok(<&str>::parse(parser)?.to_string()) } } impl Peek for &'_ str { fn peek(cursor: Cursor<'_>) -> Result { <&[u8]>::peek(cursor) } fn display() -> &'static str { <&[u8]>::display() } } macro_rules! float { ($($name:ident => { bits: $int:ident, float: $float:ident, exponent_bits: $exp_bits:tt, name: $parse:ident, })*) => ($( /// A parsed floating-point type #[derive(Debug, Copy, Clone)] pub struct $name { /// The raw bits that this floating point number represents. pub bits: $int, } impl<'a> Parse<'a> for $name { fn parse(parser: Parser<'a>) -> Result { parser.step(|c| { let (val, rest) = if let Some((f, rest)) = c.float()? { ($parse(&f), rest) } else if let Some((i, rest)) = c.integer()? { let (s, base) = i.val(); ( $parse(&Float::Val { hex: base == 16, integral: s.into(), decimal: None, exponent: None, }), rest, ) } else { return Err(c.error("expected a float")); }; match val { Some(bits) => Ok(($name { bits }, rest)), None => Err(c.error("invalid float value: constant out of range")), } }) } } fn $parse(val: &Float<'_>) -> Option<$int> { // Compute a few well-known constants about the float representation // given the parameters to the macro here. let width = std::mem::size_of::<$int>() * 8; let neg_offset = width - 1; let exp_offset = neg_offset - $exp_bits; let signif_bits = width - 1 - $exp_bits; let signif_mask = (1 << exp_offset) - 1; let bias = (1 << ($exp_bits - 1)) - 1; let (hex, integral, decimal, exponent_str) = match val { // Infinity is when the exponent bits are all set and // the significand is zero. Float::Inf { negative } => { let exp_bits = (1 << $exp_bits) - 1; let neg_bit = *negative as $int; return Some( (neg_bit << neg_offset) | (exp_bits << exp_offset) ); } // NaN is when the exponent bits are all set and // the significand is nonzero. The default of NaN is // when only the highest bit of the significand is set. Float::Nan { negative, val } => { let exp_bits = (1 << $exp_bits) - 1; let neg_bit = *negative as $int; let signif = match val { Some(val) => $int::from_str_radix(val,16).ok()?, None => 1 << (signif_bits - 1), }; // If the significand is zero then this is actually infinity // so we fail to parse it. if signif & signif_mask == 0 { return None; } return Some( (neg_bit << neg_offset) | (exp_bits << exp_offset) | (signif & signif_mask) ); } // This is trickier, handle this below Float::Val { hex, integral, decimal, exponent } => { (hex, integral, decimal, exponent) } }; // Rely on Rust's standard library to parse base 10 floats // correctly. if !*hex { let mut s = integral.to_string(); if let Some(decimal) = decimal { s.push_str("."); s.push_str(&decimal); } if let Some(exponent) = exponent_str { s.push_str("e"); s.push_str(&exponent); } let float = s.parse::<$float>().ok()?; // looks like the `*.wat` format considers infinite overflow to // be invalid. if float.is_infinite() { return None; } return Some(float.to_bits()); } // Parsing hex floats is... hard! I don't really know what most of // this below does. It was copied from Gecko's implementation in // `WasmTextToBinary.cpp`. Would love comments on this if you have // them! let decimal = decimal.as_ref().map(|s| &**s).unwrap_or(""); let negative = integral.starts_with('-'); let integral = integral.trim_start_matches('-').trim_start_matches('0'); // Do a bunch of work up front to locate the first non-zero digit // to determine the initial exponent. There's a number of // adjustments depending on where the digit was found, but the // general idea here is that I'm not really sure why things are // calculated the way they are but it should match Gecko. let decimal_no_leading = decimal.trim_start_matches('0'); let decimal_iter = if integral.is_empty() { decimal_no_leading.chars() } else { decimal.chars() }; let mut digits = integral.chars() .map(|c| (to_hex(c) as $int, false)) .chain(decimal_iter.map(|c| (to_hex(c) as $int, true))); let lead_nonzero_digit = match digits.next() { Some((c, _)) => c, // No digits? Must be `+0` or `-0`, being careful to handle the // sign encoding here. None if negative => return Some(1 << (width - 1)), None => return Some(0), }; let mut significand = 0 as $int; let mut exponent = if !integral.is_empty() { 1 } else { -((decimal.len() - decimal_no_leading.len() + 1) as i32) + 1 }; let lz = (lead_nonzero_digit as u8).leading_zeros() as i32 - 4; exponent = exponent.checked_mul(4)?.checked_sub(lz + 1)?; let mut significand_pos = (width - (4 - (lz as usize))) as isize; assert!(significand_pos >= 0); significand |= lead_nonzero_digit << significand_pos; // Now that we've got an anchor in the string we parse the remaining // digits. Again, not entirely sure why everything is the way it is // here! This is copied frmo gecko. let mut discarded_extra_nonzero = false; for (digit, decimal) in digits { if !decimal { exponent += 4; } if significand_pos > -4 { significand_pos -= 4; } if significand_pos >= 0 { significand |= digit << significand_pos; } else if significand_pos > -4 { significand |= digit >> (4 - significand_pos); discarded_extra_nonzero = (digit & !((!0) >> (4 - significand_pos))) != 0; } else if digit != 0 { discarded_extra_nonzero = true; } } exponent = exponent.checked_add(match exponent_str { Some(s) => s.parse::().ok()?, None => 0, })?; debug_assert!(significand != 0); let (encoded_exponent, encoded_significand, discarded_significand) = if exponent <= -bias { // Underflow to subnormal or zero. let shift = exp_offset as i32 + exponent + bias; if shift == 0 { (0, 0, significand) } else if shift < 0 || shift >= width as i32 { (0, 0, 0) } else { ( 0, significand >> (width as i32 - shift), significand << shift, ) } } else if exponent <= bias { // Normal (non-zero). The significand's leading 1 is encoded // implicitly. ( ((exponent + bias) as $int) << exp_offset, (significand >> (width - exp_offset - 1)) & signif_mask, significand << (exp_offset + 1), ) } else { // Overflow to infinity. ( ((1 << $exp_bits) - 1) << exp_offset, 0, 0, ) }; let bits = encoded_exponent | encoded_significand; // Apply rounding. If this overflows the significand, it carries // into the exponent bit according to the magic of the IEEE 754 // encoding. // // Or rather, the comment above is what Gecko says so it's copied // here too. let msb = 1 << (width - 1); let bits = bits + (((discarded_significand & msb != 0) && ((discarded_significand & !msb != 0) || discarded_extra_nonzero || // ties to even (encoded_significand & 1 != 0))) as $int); // Just before we return the bits be sure to handle the sign bit we // found at the beginning. let bits = if negative { bits | (1 << (width - 1)) } else { bits }; // looks like the `*.wat` format considers infinite overflow to // be invalid. if $float::from_bits(bits).is_infinite() { return None; } Some(bits) } )*) } float! { F32 => { bits: u32, float: f32, exponent_bits: 8, name: strtof, } F64 => { bits: u64, float: f64, exponent_bits: 11, name: strtod, } } fn to_hex(c: char) -> u8 { match c { 'a'..='f' => c as u8 - b'a' + 10, 'A'..='F' => c as u8 - b'A' + 10, _ => c as u8 - b'0', } } /// A convenience type to use with [`Parser::peek`](crate::parser::Parser::peek) /// to see if the next token is an s-expression. pub struct LParen { _priv: (), } impl Peek for LParen { fn peek(cursor: Cursor<'_>) -> Result { cursor.peek_lparen() } fn display() -> &'static str { "left paren" } } /// A convenience type to use with [`Parser::peek`](crate::parser::Parser::peek) /// to see if the next token is the end of an s-expression. pub struct RParen { _priv: (), } impl Peek for RParen { fn peek(cursor: Cursor<'_>) -> Result { cursor.peek_rparen() } fn display() -> &'static str { "right paren" } } #[cfg(test)] mod tests { #[test] fn hex_strtof() { macro_rules! f { ($a:tt) => (f!(@mk $a, None, None)); ($a:tt p $e:tt) => (f!(@mk $a, None, Some($e.into()))); ($a:tt . $b:tt) => (f!(@mk $a, Some($b.into()), None)); ($a:tt . $b:tt p $e:tt) => (f!(@mk $a, Some($b.into()), Some($e.into()))); (@mk $a:tt, $b:expr, $e:expr) => (crate::lexer::Float::Val { hex: true, integral: $a.into(), decimal: $b, exponent: $e }); } assert_eq!(super::strtof(&f!("0")), Some(0)); assert_eq!(super::strtof(&f!("0" . "0")), Some(0)); assert_eq!(super::strtof(&f!("0" . "0" p "2354")), Some(0)); assert_eq!(super::strtof(&f!("-0")), Some(1 << 31)); assert_eq!(super::strtof(&f!("f32")), Some(0x45732000)); assert_eq!(super::strtof(&f!("0" . "f32")), Some(0x3f732000)); assert_eq!(super::strtof(&f!("1" . "2")), Some(0x3f900000)); assert_eq!( super::strtof(&f!("0" . "00000100000000000" p "-126")), Some(0) ); assert_eq!( super::strtof(&f!("1" . "fffff4" p "-106")), Some(0x0afffffa) ); assert_eq!(super::strtof(&f!("fffff98" p "-133")), Some(0x0afffffa)); assert_eq!(super::strtof(&f!("0" . "081" p "023")), Some(0x48810000)); assert_eq!( super::strtof(&f!("1" . "00000100000000000" p "-50")), Some(0x26800000) ); } } wast-217.0.0/src/wast.rs000064400000000000000000000427201046102023000130710ustar 00000000000000use crate::component::WastVal; use crate::core::{WastArgCore, WastRetCore}; use crate::kw; use crate::parser::{self, Cursor, Parse, ParseBuffer, Parser, Peek, Result}; use crate::token::{Id, Span}; use crate::{Error, Wat}; /// A parsed representation of a `*.wast` file. /// /// WAST files are not officially specified but are used in the official test /// suite to write official spec tests for wasm. This type represents a parsed /// `*.wast` file which parses a list of directives in a file. #[derive(Debug)] pub struct Wast<'a> { #[allow(missing_docs)] pub directives: Vec>, } impl<'a> Parse<'a> for Wast<'a> { fn parse(parser: Parser<'a>) -> Result { let mut directives = Vec::new(); parser.with_standard_annotations_registered(|parser| { // If it looks like a directive token is in the stream then we parse a // bunch of directives, otherwise assume this is an inline module. if parser.peek2::()? { while !parser.is_empty() { directives.push(parser.parens(|p| p.parse())?); } } else { let module = parser.parse::()?; directives.push(WastDirective::Module(QuoteWat::Wat(module))); } Ok(Wast { directives }) }) } } struct WastDirectiveToken; impl Peek for WastDirectiveToken { fn peek(cursor: Cursor<'_>) -> Result { let kw = match cursor.keyword()? { Some((kw, _)) => kw, None => return Ok(false), }; Ok(kw.starts_with("assert_") || kw == "module" || kw == "component" || kw == "register" || kw == "invoke") } fn display() -> &'static str { unimplemented!() } } /// The different kinds of directives found in a `*.wast` file. /// /// /// Some more information about these various branches can be found at /// . #[allow(missing_docs)] #[derive(Debug)] pub enum WastDirective<'a> { /// The provided module is defined, validated, and then instantiated. Module(QuoteWat<'a>), /// The provided module is defined and validated. /// /// This module is not instantiated automatically. ModuleDefinition(QuoteWat<'a>), /// The named module is instantiated under the instance name provided. ModuleInstance { span: Span, instance: Option>, module: Option>, }, /// Asserts the module cannot be decoded with the given error. AssertMalformed { span: Span, module: QuoteWat<'a>, message: &'a str, }, /// Asserts the module cannot be validated with the given error. AssertInvalid { span: Span, module: QuoteWat<'a>, message: &'a str, }, /// Registers the `module` instance with the given `name` to be available /// for importing in future module instances. Register { span: Span, name: &'a str, module: Option>, }, /// Invokes the specified export. Invoke(WastInvoke<'a>), /// The invocation provided should trap with the specified error. AssertTrap { span: Span, exec: WastExecute<'a>, message: &'a str, }, /// The invocation provided should succeed with the specified results. AssertReturn { span: Span, exec: WastExecute<'a>, results: Vec>, }, /// The invocation provided should exhaust system resources (e.g. stack /// overflow). AssertExhaustion { span: Span, call: WastInvoke<'a>, message: &'a str, }, /// The provided module should fail to link when instantiation is attempted. AssertUnlinkable { span: Span, module: Wat<'a>, message: &'a str, }, /// The invocation provided should throw an exception. AssertException { span: Span, exec: WastExecute<'a> }, /// Creates a new system thread which executes the given commands. Thread(WastThread<'a>), /// Waits for the specified thread to exit. Wait { span: Span, thread: Id<'a> }, } impl WastDirective<'_> { /// Returns the location in the source that this directive was defined at pub fn span(&self) -> Span { match self { WastDirective::Module(QuoteWat::Wat(w)) | WastDirective::ModuleDefinition(QuoteWat::Wat(w)) => w.span(), WastDirective::Module(QuoteWat::QuoteModule(span, _)) | WastDirective::ModuleDefinition(QuoteWat::QuoteModule(span, _)) => *span, WastDirective::Module(QuoteWat::QuoteComponent(span, _)) | WastDirective::ModuleDefinition(QuoteWat::QuoteComponent(span, _)) => *span, WastDirective::ModuleInstance { span, .. } | WastDirective::AssertMalformed { span, .. } | WastDirective::Register { span, .. } | WastDirective::AssertTrap { span, .. } | WastDirective::AssertReturn { span, .. } | WastDirective::AssertExhaustion { span, .. } | WastDirective::AssertUnlinkable { span, .. } | WastDirective::AssertInvalid { span, .. } | WastDirective::AssertException { span, .. } | WastDirective::Wait { span, .. } => *span, WastDirective::Invoke(i) => i.span, WastDirective::Thread(t) => t.span, } } } impl<'a> Parse<'a> for WastDirective<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? || l.peek::()? { parse_wast_module(parser) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::AssertMalformed { span, module: parser.parens(|p| p.parse())?, message: parser.parse()?, }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::AssertInvalid { span, module: parser.parens(|p| p.parse())?, message: parser.parse()?, }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::Register { span, name: parser.parse()?, module: parser.parse()?, }) } else if l.peek::()? { Ok(WastDirective::Invoke(parser.parse()?)) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::AssertTrap { span, exec: parser.parens(|p| p.parse())?, message: parser.parse()?, }) } else if l.peek::()? { let span = parser.parse::()?.0; let exec = parser.parens(|p| p.parse())?; let mut results = Vec::new(); while !parser.is_empty() { results.push(parser.parens(|p| p.parse())?); } Ok(WastDirective::AssertReturn { span, exec, results, }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::AssertExhaustion { span, call: parser.parens(|p| p.parse())?, message: parser.parse()?, }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::AssertUnlinkable { span, module: parser.parens(parse_wat)?, message: parser.parse()?, }) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::AssertException { span, exec: parser.parens(|p| p.parse())?, }) } else if l.peek::()? { Ok(WastDirective::Thread(parser.parse()?)) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::Wait { span, thread: parser.parse()?, }) } else { Err(l.error()) } } } #[allow(missing_docs)] #[derive(Debug)] pub enum WastExecute<'a> { Invoke(WastInvoke<'a>), Wat(Wat<'a>), Get { span: Span, module: Option>, global: &'a str, }, } impl<'a> WastExecute<'a> { /// Returns the first span for this execute statement. pub fn span(&self) -> Span { match self { WastExecute::Invoke(i) => i.span, WastExecute::Wat(i) => i.span(), WastExecute::Get { span, .. } => *span, } } } impl<'a> Parse<'a> for WastExecute<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? { Ok(WastExecute::Invoke(parser.parse()?)) } else if l.peek::()? || l.peek::()? { Ok(WastExecute::Wat(parse_wat(parser)?)) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastExecute::Get { span, module: parser.parse()?, global: parser.parse()?, }) } else { Err(l.error()) } } } fn parse_wat(parser: Parser) -> Result { // 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::()? { Ok(Wat::Component(parser.parse()?)) } else { Ok(Wat::Module(parser.parse()?)) } } #[allow(missing_docs)] #[derive(Debug)] pub struct WastInvoke<'a> { pub span: Span, pub module: Option>, pub name: &'a str, pub args: Vec>, } impl<'a> Parse<'a> for WastInvoke<'a> { fn parse(parser: Parser<'a>) -> Result { let span = parser.parse::()?.0; let module = parser.parse()?; let name = parser.parse()?; let mut args = Vec::new(); while !parser.is_empty() { args.push(parser.parens(|p| p.parse())?); } Ok(WastInvoke { span, module, name, args, }) } } fn parse_wast_module<'a>(parser: Parser<'a>) -> Result> { if parser.peek2::()? { QuoteWat::parse(parser).map(WastDirective::Module) } else if parser.peek2::()? { fn parse_module(span: Span, parser: Parser<'_>) -> Result> { Ok(Wat::Module( crate::core::Module::parse_without_module_keyword(span, parser)?, )) } fn parse_component(span: Span, parser: Parser<'_>) -> Result> { Ok(Wat::Component( crate::component::Component::parse_without_component_keyword(span, parser)?, )) } let (span, ctor) = if parser.peek::()? { ( parser.parse::()?.0, parse_component as fn(_, _) -> _, ) } else { ( parser.parse::()?.0, parse_module as fn(_, _) -> _, ) }; parser.parse::()?; Ok(WastDirective::ModuleDefinition(QuoteWat::Wat(ctor( span, parser, )?))) } else if parser.peek2::()? { let span = if parser.peek::()? { parser.parse::()?.0 } else { parser.parse::()?.0 }; parser.parse::()?; Ok(WastDirective::ModuleInstance { span, instance: parser.parse()?, module: parser.parse()?, }) } else { QuoteWat::parse(parser).map(WastDirective::Module) } } #[allow(missing_docs)] #[derive(Debug)] pub enum QuoteWat<'a> { Wat(Wat<'a>), QuoteModule(Span, Vec<(Span, &'a [u8])>), QuoteComponent(Span, Vec<(Span, &'a [u8])>), } impl<'a> QuoteWat<'a> { /// 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, Error> { match self.to_test()? { QuoteWatTest::Binary(bytes) => Ok(bytes), QuoteWatTest::Text(text) => { let text = std::str::from_utf8(&text).map_err(|_| { let span = self.span(); Error::new(span, "malformed UTF-8 encoding".to_string()) })?; let buf = ParseBuffer::new(&text)?; let mut wat = parser::parse::>(&buf)?; wat.encode() } } } /// Converts this to either a `QuoteWatTest::Binary` or /// `QuoteWatTest::Text` depending on what it is internally. pub fn to_test(&mut self) -> Result { let (source, prefix) = match self { QuoteWat::Wat(m) => return m.encode().map(QuoteWatTest::Binary), QuoteWat::QuoteModule(_, source) => (source, None), QuoteWat::QuoteComponent(_, source) => (source, Some("(component")), }; let mut ret = Vec::new(); for (_, src) in source { ret.extend_from_slice(src); ret.push(b' '); } if let Some(prefix) = prefix { ret.splice(0..0, prefix.as_bytes().iter().copied()); ret.push(b')'); } Ok(QuoteWatTest::Text(ret)) } /// Returns the identifier, if registered, for this module. pub fn name(&self) -> Option> { match self { QuoteWat::Wat(Wat::Module(m)) => m.id, QuoteWat::Wat(Wat::Component(m)) => m.id, QuoteWat::QuoteModule(..) | QuoteWat::QuoteComponent(..) => None, } } /// Returns the defining span of this module. pub fn span(&self) -> Span { match self { QuoteWat::Wat(w) => w.span(), QuoteWat::QuoteModule(span, _) => *span, QuoteWat::QuoteComponent(span, _) => *span, } } } impl<'a> Parse<'a> for QuoteWat<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek2::()? { let ctor = if parser.peek::()? { parser.parse::()?; QuoteWat::QuoteComponent } else { parser.parse::()?; QuoteWat::QuoteModule }; let span = parser.parse::()?.0; let mut src = Vec::new(); while !parser.is_empty() { let span = parser.cur_span(); let string = parser.parse()?; src.push((span, string)); } Ok(ctor(span, src)) } else { Ok(QuoteWat::Wat(parse_wat(parser)?)) } } } /// Returned from [`QuoteWat::to_test`]. #[allow(missing_docs)] #[derive(Debug)] pub enum QuoteWatTest { Binary(Vec), Text(Vec), } #[derive(Debug)] #[allow(missing_docs)] pub enum WastArg<'a> { Core(WastArgCore<'a>), Component(WastVal<'a>), } impl<'a> Parse<'a> for WastArg<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::>()? { Ok(WastArg::Core(parser.parse()?)) } else { Ok(WastArg::Component(parser.parse()?)) } } } #[derive(Debug)] #[allow(missing_docs)] pub enum WastRet<'a> { Core(WastRetCore<'a>), Component(WastVal<'a>), } impl<'a> Parse<'a> for WastRet<'a> { fn parse(parser: Parser<'a>) -> Result { if parser.peek::>()? { Ok(WastRet::Core(parser.parse()?)) } else { Ok(WastRet::Component(parser.parse()?)) } } } #[derive(Debug)] #[allow(missing_docs)] pub struct WastThread<'a> { pub span: Span, pub name: Id<'a>, pub shared_module: Option>, pub directives: Vec>, } impl<'a> Parse<'a> for WastThread<'a> { fn parse(parser: Parser<'a>) -> Result { parser.depth_check()?; let span = parser.parse::()?.0; let name = parser.parse()?; let shared_module = if parser.peek2::()? { let name = parser.parens(|p| { p.parse::()?; p.parens(|p| { p.parse::()?; p.parse() }) })?; Some(name) } else { None }; let mut directives = Vec::new(); while !parser.is_empty() { directives.push(parser.parens(|p| p.parse())?); } Ok(WastThread { span, name, shared_module, directives, }) } } wast-217.0.0/src/wat.rs000064400000000000000000000041671046102023000127110ustar 00000000000000use 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, crate::Error> { crate::core::EncodeOptions::default().encode_wat(self) } /// Returns the defining span of this file. pub fn span(&self) -> Span { match self { Wat::Module(m) => m.span, Wat::Component(c) => c.span, } } } impl<'a> Parse<'a> for Wat<'a> { fn parse(parser: Parser<'a>) -> Result { if !parser.has_meaningful_tokens() { return Err(parser.error("expected at least one module field")); } parser.with_standard_annotations_registered(|parser| { let wat = if parser.peek2::()? { Wat::Module(parser.parens(|parser| parser.parse())?) } else if parser.peek2::()? { 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) }) } } wast-217.0.0/tests/.gitattributes000064400000000000000000000000551046102023000150060ustar 00000000000000parse-fail/newline-in-string.wat text eol=lf wast-217.0.0/tests/.gitignore000064400000000000000000000000071046102023000141000ustar 00000000000000!*.wat wast-217.0.0/tests/annotations.rs000064400000000000000000000137061046102023000150250ustar 00000000000000use wasmparser::*; #[test] fn name_annotations() -> anyhow::Result<()> { assert_module_name("foo", r#"(module $foo)"#)?; assert_module_name("foo", r#"(module (@name "foo"))"#)?; assert_module_name("foo", r#"(module $bar (@name "foo"))"#)?; assert_module_name("foo bar", r#"(module $bar (@name "foo bar"))"#)?; Ok(()) } fn assert_module_name(expected_name: &str, wat: &str) -> anyhow::Result<()> { let wasm = wat::parse_str(wat)?; let mut found = false; for s in get_name_section(&wasm)? { match s? { Name::Module { name, .. } => { assert_eq!(name, expected_name); found = true; } _ => {} } } assert!(found); Ok(()) } #[test] fn func_annotations() -> anyhow::Result<()> { assert_func_name("foo", r#"(module (func $foo))"#)?; assert_func_name("foo", r#"(module (func (@name "foo")))"#)?; assert_func_name("foo", r#"(module (func $bar (@name "foo")))"#)?; assert_func_name("foo bar", r#"(module (func $bar (@name "foo bar")))"#)?; Ok(()) } fn assert_func_name(name: &str, wat: &str) -> anyhow::Result<()> { let wasm = wat::parse_str(wat)?; let mut found = false; for s in get_name_section(&wasm)? { match s? { Name::Function(n) => { let naming = n.into_iter().next().unwrap()?; assert_eq!(naming.index, 0); assert_eq!(naming.name, name); found = true; } _ => {} } } assert!(found); Ok(()) } #[test] fn local_annotations() -> anyhow::Result<()> { assert_local_name("foo", r#"(module (func (param $foo i32)))"#)?; assert_local_name("foo", r#"(module (func (local $foo i32)))"#)?; assert_local_name("foo", r#"(module (func (param (@name "foo") i32)))"#)?; assert_local_name("foo", r#"(module (func (local (@name "foo") i32)))"#)?; assert_local_name("foo", r#"(module (func (param $bar (@name "foo") i32)))"#)?; assert_local_name("foo", r#"(module (func (local $bar (@name "foo") i32)))"#)?; assert_local_name( "foo bar", r#"(module (func (param $bar (@name "foo bar") i32)))"#, )?; assert_local_name( "foo bar", r#"(module (func (local $bar (@name "foo bar") i32)))"#, )?; Ok(()) } fn assert_local_name(name: &str, wat: &str) -> anyhow::Result<()> { let wasm = wat::parse_str(wat)?; let mut found = false; for s in get_name_section(&wasm)? { match s? { Name::Local(n) => { let naming = n .into_iter() .next() .unwrap()? .names .into_iter() .next() .unwrap()?; assert_eq!(naming.index, 0); assert_eq!(naming.name, name); found = true; } _ => {} } } assert!(found); Ok(()) } fn get_name_section(wasm: &[u8]) -> anyhow::Result> { for payload in Parser::new(0).parse_all(&wasm) { if let Payload::CustomSection(c) = payload? { if let KnownCustom::Name(s) = c.as_known() { return Ok(s); } } } panic!("no name section found"); } #[test] fn custom_section_order() -> anyhow::Result<()> { let bytes = wat::parse_str( r#" (module (@custom "A" "aaa") (type (func)) (@custom "B" (after func) "bbb") (@custom "C" (before func) "ccc") (@custom "D" (after last) "ddd") (table 10 funcref) (func (type 0)) (@custom "E" (after import) "eee") (@custom "F" (before type) "fff") (@custom "G" (after data) "ggg") (@custom "H" (after code) "hhh") (@custom "I" (after func) "iii") (@custom "J" (before func) "jjj") (@custom "K" (before first) "kkk") ) "#, )?; macro_rules! assert_matches { ($a:expr, $b:pat $(if $cond:expr)? $(,)?) => { match &$a { $b $(if $cond)? => {} a => panic!("`{:?}` doesn't match `{}`", a, stringify!($b)), } }; } let wasm = Parser::new(0) .parse_all(&bytes) .collect::>>()?; assert_matches!(wasm[0], Payload::Version { .. }); 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(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(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(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(()) } wast-217.0.0/tests/comments.rs000064400000000000000000000032761046102023000143160ustar 00000000000000use wast::parser::{self, Parse, ParseBuffer, Parser, Result}; pub struct Comments<'a> { comments: Vec<&'a str>, } impl<'a> Parse<'a> for Comments<'a> { fn parse(parser: Parser<'a>) -> Result> { let comments = parser.step(|mut cursor| { let mut comments = Vec::new(); loop { let (comment, c) = match cursor.comment()? { Some(pair) => pair, None => break, }; cursor = c; comments.push(if comment.starts_with(";;") { &comment[2..] } else { &comment[2..comment.len() - 2] }); } Ok((comments, cursor)) })?; Ok(Comments { comments }) } } pub struct Documented<'a, T> { comments: Comments<'a>, item: T, } impl<'a, T: Parse<'a>> Parse<'a> for Documented<'a, T> { fn parse(parser: Parser<'a>) -> Result { let comments = parser.parse()?; let item = parser.parens(T::parse)?; Ok(Documented { comments, item }) } } #[test] fn parse_comments() -> anyhow::Result<()> { let buf = ParseBuffer::new( r#" ;; hello (; again ;) (module) "#, )?; let d: Documented = parser::parse(&buf)?; assert_eq!(d.comments.comments, vec![" hello", " again "]); drop(d.item); let buf = ParseBuffer::new( r#" ;; this (; is on multiple;) ;; lines (func) "#, )?; let d: Documented = parser::parse(&buf)?; assert_eq!( d.comments.comments, vec![" this", " is\non\nmultiple", " lines"] ); drop(d.item); Ok(()) } wast-217.0.0/tests/parse-fail/bad-core-func-alias.wat000064400000000000000000000003721046102023000203510ustar 00000000000000(component (core module $mod (func (export "fun"))) (core instance $inst (instantiate $mod)) (alias core export $inst "fun" (core func $fun1)) (core func $fun2 (alias core export $inst "fun")) (core func $fun3 (alias export $inst "fun")) ) wast-217.0.0/tests/parse-fail/bad-core-func-alias.wat.err000064400000000000000000000002621046102023000211360ustar 00000000000000expected keyword `core` --> tests/parse-fail/bad-core-func-alias.wat:6:27 | 6 | (core func $fun3 (alias export $inst "fun")) | ^ wast-217.0.0/tests/parse-fail/bad-func-alias.wat000064400000000000000000000003331046102023000174200ustar 00000000000000(component (component $comp) (instance $inst (instantiate $comp)) (func $alias1 (alias export $inst "fun")) (alias export $inst "fun" (func $alias2)) (alias export $inst "fun" (core func $alias3)) ) wast-217.0.0/tests/parse-fail/bad-func-alias.wat.err000064400000000000000000000003071046102023000202100ustar 00000000000000unexpected token, expected `module` --> tests/parse-fail/bad-func-alias.wat:6:37 | 6 | (alias export $inst "fun" (core func $alias3)) | ^ wast-217.0.0/tests/parse-fail/bad-index.wat000064400000000000000000000000251046102023000165030ustar 00000000000000(func br_on_null $s) wast-217.0.0/tests/parse-fail/bad-index.wat.err000064400000000000000000000002251046102023000172740ustar 00000000000000unknown label: failed to find name `$s` --> tests/parse-fail/bad-index.wat:1:18 | 1 | (func br_on_null $s) | ^ wast-217.0.0/tests/parse-fail/bad-name.wat000064400000000000000000000000211046102023000163100ustar 00000000000000(module (@name)) wast-217.0.0/tests/parse-fail/bad-name.wat.err000064400000000000000000000001671046102023000171120ustar 00000000000000expected a string --> tests/parse-fail/bad-name.wat:1:15 | 1 | (module (@name)) | ^ wast-217.0.0/tests/parse-fail/bad-name2.wat000064400000000000000000000000241046102023000163750ustar 00000000000000(module (@name 2)) wast-217.0.0/tests/parse-fail/bad-name2.wat.err000064400000000000000000000001731046102023000171710ustar 00000000000000expected a string --> tests/parse-fail/bad-name2.wat:1:16 | 1 | (module (@name 2)) | ^ wast-217.0.0/tests/parse-fail/bad-name3.wat000064400000000000000000000000321046102023000163750ustar 00000000000000(module (func (@name 2))) wast-217.0.0/tests/parse-fail/bad-name3.wat.err000064400000000000000000000002101046102023000171620ustar 00000000000000expected a string --> tests/parse-fail/bad-name3.wat:1:22 | 1 | (module (func (@name 2))) | ^ wast-217.0.0/tests/parse-fail/block1.wat000064400000000000000000000000031046102023000160170ustar 00000000000000(; wast-217.0.0/tests/parse-fail/block1.wat.err000064400000000000000000000001411046102023000166110ustar 00000000000000unterminated block comment --> tests/parse-fail/block1.wat:1:1 | 1 | (; | ^ wast-217.0.0/tests/parse-fail/block2.wat000064400000000000000000000000101046102023000160160ustar 00000000000000(; (;;) wast-217.0.0/tests/parse-fail/block2.wat.err000064400000000000000000000001461046102023000166170ustar 00000000000000unterminated block comment --> tests/parse-fail/block2.wat:1:1 | 1 | (; (;;) | ^ wast-217.0.0/tests/parse-fail/block3.wat000064400000000000000000000000051046102023000160230ustar 00000000000000(; ; wast-217.0.0/tests/parse-fail/block3.wat.err000064400000000000000000000001431046102023000166150ustar 00000000000000unterminated block comment --> tests/parse-fail/block3.wat:1:1 | 1 | (; ; | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment0.wat000064400000000000000000000000231046102023000214510ustar 00000000000000(module) (; ‪ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment0.wat.err000064400000000000000000000002461046102023000222470ustar 00000000000000likely-confusing unicode character found '\u{202a}' --> tests/parse-fail/confusing-block-comment0.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment1.wat000064400000000000000000000000231046102023000214520ustar 00000000000000(module) (; ‫ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment1.wat.err000064400000000000000000000002461046102023000222500ustar 00000000000000likely-confusing unicode character found '\u{202b}' --> tests/parse-fail/confusing-block-comment1.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment2.wat000064400000000000000000000000231046102023000214530ustar 00000000000000(module) (; ‭ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment2.wat.err000064400000000000000000000002461046102023000222510ustar 00000000000000likely-confusing unicode character found '\u{202d}' --> tests/parse-fail/confusing-block-comment2.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment3.wat000064400000000000000000000000231046102023000214540ustar 00000000000000(module) (; ‮ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment3.wat.err000064400000000000000000000002461046102023000222520ustar 00000000000000likely-confusing unicode character found '\u{202e}' --> tests/parse-fail/confusing-block-comment3.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment4.wat000064400000000000000000000000231046102023000214550ustar 00000000000000(module) (; ⁦ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment4.wat.err000064400000000000000000000002461046102023000222530ustar 00000000000000likely-confusing unicode character found '\u{2066}' --> tests/parse-fail/confusing-block-comment4.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment5.wat000064400000000000000000000000231046102023000214560ustar 00000000000000(module) (; ⁧ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment5.wat.err000064400000000000000000000002461046102023000222540ustar 00000000000000likely-confusing unicode character found '\u{2067}' --> tests/parse-fail/confusing-block-comment5.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment6.wat000064400000000000000000000000231046102023000214570ustar 00000000000000(module) (; ⁨ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment6.wat.err000064400000000000000000000002461046102023000222550ustar 00000000000000likely-confusing unicode character found '\u{2068}' --> tests/parse-fail/confusing-block-comment6.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment7.wat000064400000000000000000000000231046102023000214600ustar 00000000000000(module) (;  ;) wast-217.0.0/tests/parse-fail/confusing-block-comment7.wat.err000064400000000000000000000002461046102023000222560ustar 00000000000000likely-confusing unicode character found '\u{206c}' --> tests/parse-fail/confusing-block-comment7.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-block-comment8.wat000064400000000000000000000000231046102023000214610ustar 00000000000000(module) (; ⁩ ;) wast-217.0.0/tests/parse-fail/confusing-block-comment8.wat.err000064400000000000000000000002461046102023000222570ustar 00000000000000likely-confusing unicode character found '\u{2069}' --> tests/parse-fail/confusing-block-comment8.wat:1:13 | 1 | (module) (; ;) | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment0.wat000064400000000000000000000000201046102023000213030ustar 00000000000000(module) ;; ‪ wast-217.0.0/tests/parse-fail/confusing-line-comment0.wat.err000064400000000000000000000002421046102023000221000ustar 00000000000000likely-confusing unicode character found '\u{202a}' --> tests/parse-fail/confusing-line-comment0.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment1.wat000064400000000000000000000000201046102023000213040ustar 00000000000000(module) ;; ‫ wast-217.0.0/tests/parse-fail/confusing-line-comment1.wat.err000064400000000000000000000002421046102023000221010ustar 00000000000000likely-confusing unicode character found '\u{202b}' --> tests/parse-fail/confusing-line-comment1.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment2.wat000064400000000000000000000000201046102023000213050ustar 00000000000000(module) ;; ‭ wast-217.0.0/tests/parse-fail/confusing-line-comment2.wat.err000064400000000000000000000002421046102023000221020ustar 00000000000000likely-confusing unicode character found '\u{202d}' --> tests/parse-fail/confusing-line-comment2.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment3.wat000064400000000000000000000000201046102023000213060ustar 00000000000000(module) ;; ‮ wast-217.0.0/tests/parse-fail/confusing-line-comment3.wat.err000064400000000000000000000002421046102023000221030ustar 00000000000000likely-confusing unicode character found '\u{202e}' --> tests/parse-fail/confusing-line-comment3.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment4.wat000064400000000000000000000000201046102023000213070ustar 00000000000000(module) ;; ⁦ wast-217.0.0/tests/parse-fail/confusing-line-comment4.wat.err000064400000000000000000000002421046102023000221040ustar 00000000000000likely-confusing unicode character found '\u{2066}' --> tests/parse-fail/confusing-line-comment4.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment5.wat000064400000000000000000000000201046102023000213100ustar 00000000000000(module) ;; ⁧ wast-217.0.0/tests/parse-fail/confusing-line-comment5.wat.err000064400000000000000000000002421046102023000221050ustar 00000000000000likely-confusing unicode character found '\u{2067}' --> tests/parse-fail/confusing-line-comment5.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment6.wat000064400000000000000000000000201046102023000213110ustar 00000000000000(module) ;; ⁨ wast-217.0.0/tests/parse-fail/confusing-line-comment6.wat.err000064400000000000000000000002421046102023000221060ustar 00000000000000likely-confusing unicode character found '\u{2068}' --> tests/parse-fail/confusing-line-comment6.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment7.wat000064400000000000000000000000201046102023000213120ustar 00000000000000(module) ;;  wast-217.0.0/tests/parse-fail/confusing-line-comment7.wat.err000064400000000000000000000002421046102023000221070ustar 00000000000000likely-confusing unicode character found '\u{206c}' --> tests/parse-fail/confusing-line-comment7.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-line-comment8.wat000064400000000000000000000000201046102023000213130ustar 00000000000000(module) ;; ⁩ wast-217.0.0/tests/parse-fail/confusing-line-comment8.wat.err000064400000000000000000000002421046102023000221100ustar 00000000000000likely-confusing unicode character found '\u{2069}' --> tests/parse-fail/confusing-line-comment8.wat:1:13 | 1 | (module) ;; | ^ wast-217.0.0/tests/parse-fail/confusing-string0.wat000064400000000000000000000000411046102023000202250ustar 00000000000000(module (export "‪" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string0.wat.err000064400000000000000000000002621046102023000210210ustar 00000000000000likely-confusing unicode character found '\u{202a}' --> tests/parse-fail/confusing-string0.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string1.wat000064400000000000000000000000411046102023000202260ustar 00000000000000(module (export "‫" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string1.wat.err000064400000000000000000000002621046102023000210220ustar 00000000000000likely-confusing unicode character found '\u{202b}' --> tests/parse-fail/confusing-string1.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string2.wat000064400000000000000000000000411046102023000202270ustar 00000000000000(module (export "‭" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string2.wat.err000064400000000000000000000002621046102023000210230ustar 00000000000000likely-confusing unicode character found '\u{202d}' --> tests/parse-fail/confusing-string2.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string3.wat000064400000000000000000000000411046102023000202300ustar 00000000000000(module (export "‮" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string3.wat.err000064400000000000000000000002621046102023000210240ustar 00000000000000likely-confusing unicode character found '\u{202e}' --> tests/parse-fail/confusing-string3.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string4.wat000064400000000000000000000000411046102023000202310ustar 00000000000000(module (export "⁦" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string4.wat.err000064400000000000000000000002621046102023000210250ustar 00000000000000likely-confusing unicode character found '\u{2066}' --> tests/parse-fail/confusing-string4.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string5.wat000064400000000000000000000000411046102023000202320ustar 00000000000000(module (export "⁧" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string5.wat.err000064400000000000000000000002621046102023000210260ustar 00000000000000likely-confusing unicode character found '\u{2067}' --> tests/parse-fail/confusing-string5.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string6.wat000064400000000000000000000000411046102023000202330ustar 00000000000000(module (export "⁨" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string6.wat.err000064400000000000000000000002621046102023000210270ustar 00000000000000likely-confusing unicode character found '\u{2068}' --> tests/parse-fail/confusing-string6.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string7.wat000064400000000000000000000000411046102023000202340ustar 00000000000000(module (export "" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string7.wat.err000064400000000000000000000002621046102023000210300ustar 00000000000000likely-confusing unicode character found '\u{206c}' --> tests/parse-fail/confusing-string7.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/confusing-string8.wat000064400000000000000000000000411046102023000202350ustar 00000000000000(module (export "⁩" (func 0))) wast-217.0.0/tests/parse-fail/confusing-string8.wat.err000064400000000000000000000002621046102023000210310ustar 00000000000000likely-confusing unicode character found '\u{2069}' --> tests/parse-fail/confusing-string8.wat:1:18 | 1 | (module (export "" (func 0))) | ^ wast-217.0.0/tests/parse-fail/inline1.wat000064400000000000000000000001621046102023000162110ustar 00000000000000(type $sig (func)) (table 0 funcref) (func (result i32) (call_indirect (type $sig) (result i32) (i32.const 0))) wast-217.0.0/tests/parse-fail/inline1.wat.err000064400000000000000000000003101046102023000167730ustar 00000000000000inline function type doesn't match type reference --> tests/parse-fail/inline1.wat:4:24 | 4 | (call_indirect (type $sig) (result i32) (i32.const 0))) | ^ wast-217.0.0/tests/parse-fail/newline-in-string.wat000064400000000000000000000000021046102023000202140ustar 00000000000000" wast-217.0.0/tests/parse-fail/newline-in-string.wat.err000064400000000000000000000001621046102023000210120ustar 00000000000000invalid character in string '\n' --> tests/parse-fail/newline-in-string.wat:1:2 | 1 | " | ^ wast-217.0.0/tests/parse-fail/string1.wat000064400000000000000000000000231046102023000162350ustar 00000000000000(module (import "wast-217.0.0/tests/parse-fail/string1.wat.err000064400000000000000000000001631046102023000170310ustar 00000000000000unexpected end-of-file --> tests/parse-fail/string1.wat:2:12 | 2 | (import " | ^ wast-217.0.0/tests/parse-fail/string10.wat000064400000000000000000000000111046102023000163120ustar 00000000000000"\u{1_}" wast-217.0.0/tests/parse-fail/string10.wat.err000064400000000000000000000001661046102023000171140ustar 00000000000000bare underscore in numeric literal --> tests/parse-fail/string10.wat:1:6 | 1 | "\u{1_}" | ^ wast-217.0.0/tests/parse-fail/string11.wat000064400000000000000000000000301046102023000163140ustar 00000000000000"\u{fffffffffffffffff}" wast-217.0.0/tests/parse-fail/string11.wat.err000064400000000000000000000002051046102023000171070ustar 00000000000000number is too big to parse --> tests/parse-fail/string11.wat:1:13 | 1 | "\u{fffffffffffffffff}" | ^ wast-217.0.0/tests/parse-fail/string12.wat000064400000000000000000000000171046102023000163220ustar 00000000000000"\u{ffffffff}" wast-217.0.0/tests/parse-fail/string12.wat.err000064400000000000000000000002101046102023000171040ustar 00000000000000invalid unicode scalar value 0xffffffff --> tests/parse-fail/string12.wat:1:12 | 1 | "\u{ffffffff}" | ^ wast-217.0.0/tests/parse-fail/string13.wat000064400000000000000000000000051046102023000163200ustar 00000000000000"\u" wast-217.0.0/tests/parse-fail/string13.wat.err000064400000000000000000000001501046102023000171100ustar 00000000000000expected '{' but found '"' --> tests/parse-fail/string13.wat:1:4 | 1 | "\u" | ^ wast-217.0.0/tests/parse-fail/string14.wat000064400000000000000000000000061046102023000163220ustar 00000000000000"\u{" wast-217.0.0/tests/parse-fail/string14.wat.err000064400000000000000000000001451046102023000171150ustar 00000000000000invalid hex digit '"' --> tests/parse-fail/string14.wat:1:5 | 1 | "\u{" | ^ wast-217.0.0/tests/parse-fail/string15.wat000064400000000000000000000000071046102023000163240ustar 00000000000000"\u{3" wast-217.0.0/tests/parse-fail/string15.wat.err000064400000000000000000000001541046102023000171160ustar 00000000000000expected '}' but found '"' --> tests/parse-fail/string15.wat:1:6 | 1 | "\u{3" | ^ wast-217.0.0/tests/parse-fail/string16.wat000064400000000000000000000000051046102023000163230ustar 00000000000000"\u{3wast-217.0.0/tests/parse-fail/string16.wat.err000064400000000000000000000001471046102023000171210ustar 00000000000000unexpected end-of-file --> tests/parse-fail/string16.wat:1:6 | 1 | "\u{3 | ^ wast-217.0.0/tests/parse-fail/string2.wat000064400000000000000000000000311046102023000162350ustar 00000000000000(module (import "\x")) wast-217.0.0/tests/parse-fail/string2.wat.err000064400000000000000000000001741046102023000170340ustar 00000000000000invalid string escape 'x' --> tests/parse-fail/string2.wat:2:13 | 2 | (import "\x")) | ^ wast-217.0.0/tests/parse-fail/string3.wat000064400000000000000000000000321046102023000162370ustar 00000000000000(module (import "\0")) wast-217.0.0/tests/parse-fail/string3.wat.err000064400000000000000000000001711046102023000170320ustar 00000000000000invalid hex digit '"' --> tests/parse-fail/string3.wat:2:14 | 2 | (import "\0")) | ^ wast-217.0.0/tests/parse-fail/string4.wat000064400000000000000000000000251046102023000162420ustar 00000000000000(module (import "\0wast-217.0.0/tests/parse-fail/string4.wat.err000064400000000000000000000001671046102023000170400ustar 00000000000000unexpected end-of-file --> tests/parse-fail/string4.wat:2:14 | 2 | (import "\0 | ^ wast-217.0.0/tests/parse-fail/string5.wat000064400000000000000000000000021046102023000162360ustar 00000000000000"\wast-217.0.0/tests/parse-fail/string5.wat.err000064400000000000000000000001401046102023000170300ustar 00000000000000unexpected end-of-file --> tests/parse-fail/string5.wat:1:3 | 1 | "\ | ^ wast-217.0.0/tests/parse-fail/string6.wat000064400000000000000000000000041046102023000162410ustar 00000000000000"" wast-217.0.0/tests/parse-fail/string6.wat.err000064400000000000000000000001551046102023000170370ustar 00000000000000invalid character in string '\u{0}' --> tests/parse-fail/string6.wat:1:2 | 1 | "" | ^ wast-217.0.0/tests/parse-fail/string7.wat000064400000000000000000000000041046102023000162420ustar 00000000000000"" wast-217.0.0/tests/parse-fail/string7.wat.err000064400000000000000000000001561046102023000170410ustar 00000000000000invalid character in string '\u{7f}' --> tests/parse-fail/string7.wat:1:2 | 1 | "" | ^ wast-217.0.0/tests/parse-fail/string8.wat000064400000000000000000000000041046102023000162430ustar 00000000000000"" wast-217.0.0/tests/parse-fail/string8.wat.err000064400000000000000000000001561046102023000170420ustar 00000000000000invalid character in string '\u{1f}' --> tests/parse-fail/string8.wat:1:2 | 1 | "" | ^ wast-217.0.0/tests/parse-fail/string9.wat000064400000000000000000000000101046102023000162410ustar 00000000000000"\u{x}" wast-217.0.0/tests/parse-fail/string9.wat.err000064400000000000000000000001461046102023000170420ustar 00000000000000invalid hex digit 'x' --> tests/parse-fail/string9.wat:1:5 | 1 | "\u{x}" | ^ wast-217.0.0/tests/parse-fail/unbalanced.wat000064400000000000000000000000141046102023000167420ustar 00000000000000(module) ) wast-217.0.0/tests/parse-fail/unbalanced.wat.err000064400000000000000000000001541046102023000175360ustar 00000000000000extra tokens remaining after parse --> tests/parse-fail/unbalanced.wat:3:1 | 3 | ) | ^ wast-217.0.0/tests/parse-fail.rs000064400000000000000000000043621046102023000145110ustar 00000000000000//! A test suite to parse everything in `parse-fail` and assert that it matches //! the `*.err` file it generates. //! //! Use `BLESS=1` in the environment to auto-update `*.err` files. Be sure to //! look at the diff! use libtest_mimic::{Arguments, Trial}; use std::env; use std::path::{Path, PathBuf}; fn main() { let mut tests = Vec::new(); find_tests("tests/parse-fail".as_ref(), &mut tests); let bless = env::var("BLESS").is_ok(); let mut trials = Vec::new(); for test in tests { let trial = Trial::test(format!("{test:?}"), move || { run_test(&test, bless).map_err(|e| format!("{e:?}").into()) }); trials.push(trial); } let mut args = Arguments::from_args(); if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") { args.test_threads = Some(1); } libtest_mimic::run(&args, trials).exit(); } fn run_test(test: &Path, bless: bool) -> anyhow::Result<()> { let err = match wat::parse_file(test) { Ok(_) => anyhow::bail!("{} parsed successfully", test.display()), Err(e) => e.to_string() + "\n", }; let assert = test.with_extension("wat.err"); if bless { std::fs::write(assert, err.to_string())?; return Ok(()); } // Ignore CRLF line ending and force always `\n` let assert = std::fs::read_to_string(assert) .unwrap_or(String::new()) .replace("\r\n", "\n"); // Compare normalize verisons which handles weirdness like path differences if normalize(&assert) == normalize(&err) { return Ok(()); } anyhow::bail!( "errors did not match:\n\nexpected:\n\t{}\nactual:\n\t{}\n", tab(&assert), tab(&err), ); fn normalize(s: &str) -> String { s.replace("\\", "/") } fn tab(s: &str) -> String { s.replace("\n", "\n\t") } } fn find_tests(path: &Path, tests: &mut Vec) { for f in path.read_dir().unwrap() { let f = f.unwrap(); if f.file_type().unwrap().is_dir() { find_tests(&f.path(), tests); continue; } match f.path().extension().and_then(|s| s.to_str()) { Some("wat") => {} _ => continue, } tests.push(f.path()); } } wast-217.0.0/tests/recursive.rs000064400000000000000000000002731046102023000144720ustar 00000000000000#[test] fn no_stack_overflow() { let mut s = format!("(module (func \n"); for _ in 0..10_000 { s.push_str("(i32.add\n"); } assert!(wat::parse_str(&s).is_err()); }