wasmprinter-0.239.0/.cargo_vcs_info.json0000644000000001600000000000100135570ustar { "git": { "sha1": "35f8671bce74190ef0b00ce36c399b053b490374" }, "path_in_vcs": "crates/wasmprinter" }wasmprinter-0.239.0/Cargo.lock0000644000000104750000000000100115440ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "indexmap" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "wasmparser" version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" dependencies = [ "bitflags", "indexmap", "semver", ] [[package]] name = "wasmprinter" version = "0.239.0" dependencies = [ "anyhow", "termcolor", "wasmparser", ] [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" wasmprinter-0.239.0/Cargo.toml0000644000000047140000000000100115660ustar # 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 = "wasmprinter" version = "0.239.0" authors = ["Alex Crichton "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Rust converter from the WebAssembly binary format to the text format. """ homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmprinter" documentation = "https://docs.rs/wasmprinter" 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/wasmprinter" [features] component-model = ["wasmparser/component-model"] default = [ "component-model", "validate", ] validate = [ "wasmparser/validate", "wasmparser/features", ] [lib] name = "wasmprinter" path = "src/lib.rs" [[test]] name = "all" path = "tests/all.rs" [dependencies.anyhow] version = "1.0.58" [dependencies.termcolor] version = "1.2.0" [dependencies.wasmparser] version = "0.239.0" features = [ "simd", "std", "simd", ] default-features = false [dev-dependencies] [lints.clippy] allow_attributes_without_reason = "warn" clone_on_copy = "warn" manual_strip = "warn" map_clone = "warn" uninlined_format_args = "warn" unnecessary_cast = "warn" unnecessary_fallible_conversions = "warn" unnecessary_mut_passed = "warn" unnecessary_to_owned = "warn" [lints.clippy.all] level = "allow" priority = -1 [lints.rust] deprecated-safe-2024 = "warn" keyword_idents_2024 = "warn" missing-unsafe-on-extern = "warn" rust-2024-guarded-string-incompatible-syntax = "warn" rust-2024-incompatible-pat = "warn" rust-2024-prelude-collisions = "warn" unsafe-attr-outside-unsafe = "warn" unsafe-op-in-unsafe-fn = "warn" unsafe_code = "deny" unstable_features = "warn" unused-lifetimes = "warn" unused-macro-rules = "warn" unused_extern_crates = "warn" unused_import_braces = "warn" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(fuzzing)"] wasmprinter-0.239.0/Cargo.toml.orig000064400000000000000000000016051046102023000152430ustar 00000000000000[package] name = "wasmprinter" version.workspace = true authors = ["Alex Crichton "] edition.workspace = true license.workspace = true readme = "README.md" repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmprinter" homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmprinter" documentation = "https://docs.rs/wasmprinter" description = """ Rust converter from the WebAssembly binary format to the text format. """ rust-version.workspace = true [lints] workspace = true [dependencies] anyhow = { workspace = true } termcolor = { workspace = true } wasmparser = { workspace = true, features = ['std', 'simd'] } [dev-dependencies] wat = { path = "../wat" } [features] default = ['component-model', 'validate'] component-model = ['wasmparser/component-model'] validate = ['wasmparser/validate', 'wasmparser/features'] wasmprinter-0.239.0/README.md000064400000000000000000000027751046102023000136440ustar 00000000000000

wasmprinter

A Bytecode Alliance project

A Rust parser for printing a WebAssembly binary in the WebAssembly Text Format (WAT).

Crates.io version Download docs.rs docs

## Usage Add `wasmprinter` to your `Cargo.toml` ```sh $ cargo add wasmprinter ``` You can then convert wasm binaries to strings like so: ```rust fn main() -> Result<()> { let foo_wat = wasmprinter::print_file("path/to/foo.wasm")?; let binary = /* ... */; let wat = wasmprinter::print_bytes(&binary)?; // ... } ``` ## 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 licensed as above, without any additional terms or conditions. wasmprinter-0.239.0/src/component.rs000064400000000000000000001612251046102023000155200ustar 00000000000000use crate::{Naming, Printer, State, name_map}; use anyhow::{Result, bail}; use wasmparser::*; impl Printer<'_, '_> { pub(crate) fn register_component_names( &mut self, state: &mut State, names: ComponentNameSectionReader<'_>, ) -> Result<()> { for section in names { match section? { ComponentName::Component { name, .. } => { let name = Naming::new(name, 0, "component", None); state.name = Some(name); } ComponentName::CoreFuncs(n) => { name_map(&mut state.core.func_names, n, "core-func")? } ComponentName::CoreTypes(n) => { name_map(&mut state.core.type_names, n, "core-type")? } ComponentName::CoreTables(n) => { name_map(&mut state.core.table_names, n, "core-table")? } ComponentName::CoreMemories(n) => { name_map(&mut state.core.memory_names, n, "core-memory")? } ComponentName::CoreGlobals(n) => { name_map(&mut state.core.global_names, n, "core-global")? } ComponentName::CoreTags(n) => name_map(&mut state.core.tag_names, n, "core-tag")?, ComponentName::CoreModules(n) => { name_map(&mut state.core.module_names, n, "core-module")? } ComponentName::CoreInstances(n) => { name_map(&mut state.core.instance_names, n, "core-instance")? } ComponentName::Types(n) => name_map(&mut state.component.type_names, n, "type")?, ComponentName::Instances(n) => { name_map(&mut state.component.instance_names, n, "instance")? } ComponentName::Components(n) => { name_map(&mut state.component.component_names, n, "component")? } ComponentName::Funcs(n) => name_map(&mut state.component.func_names, n, "func")?, ComponentName::Values(n) => name_map(&mut state.component.value_names, n, "value")?, ComponentName::Unknown { .. } => (), } } Ok(()) } pub(crate) fn print_core_types( &mut self, states: &mut Vec, parser: CoreTypeSectionReader<'_>, ) -> Result<()> { for ty in parser.into_iter_with_offsets() { let (offset, ty) = ty?; self.newline(offset)?; self.print_core_type(states, ty)?; } Ok(()) } pub(crate) fn print_core_type(&mut self, states: &mut Vec, ty: CoreType) -> Result<()> { match ty { CoreType::Rec(rec) => { self.print_rec(states.last_mut().unwrap(), None, rec, true)?; } CoreType::Module(decls) => { self.start_group("core type ")?; self.print_name( &states.last().unwrap().core.type_names, states.last().unwrap().core.types.len() as u32, )?; self.print_module_type(states, decls.into_vec())?; self.end_group()?; // `core type` itself states.last_mut().unwrap().core.types.push(None); } } Ok(()) } pub(crate) fn print_component_type_ref(&mut self, state: &State, idx: u32) -> Result<()> { self.start_group("type ")?; self.print_idx(&state.component.type_names, idx)?; self.end_group()?; Ok(()) } pub(crate) fn print_primitive_val_type(&mut self, ty: &PrimitiveValType) -> Result<()> { match ty { PrimitiveValType::Bool => self.print_type_keyword("bool")?, PrimitiveValType::S8 => self.print_type_keyword("s8")?, PrimitiveValType::U8 => self.print_type_keyword("u8")?, PrimitiveValType::S16 => self.print_type_keyword("s16")?, PrimitiveValType::U16 => self.print_type_keyword("u16")?, PrimitiveValType::S32 => self.print_type_keyword("s32")?, PrimitiveValType::U32 => self.print_type_keyword("u32")?, PrimitiveValType::S64 => self.print_type_keyword("s64")?, PrimitiveValType::U64 => self.print_type_keyword("u64")?, PrimitiveValType::F32 => self.print_type_keyword("f32")?, PrimitiveValType::F64 => self.print_type_keyword("f64")?, PrimitiveValType::Char => self.print_type_keyword("char")?, PrimitiveValType::String => self.print_type_keyword("string")?, PrimitiveValType::ErrorContext => self.print_type_keyword("error-context")?, } Ok(()) } pub(crate) fn print_record_type( &mut self, state: &State, fields: &[(&str, ComponentValType)], ) -> Result<()> { self.start_group("record")?; for (name, ty) in fields.iter() { self.result.write_str(" ")?; self.start_group("field ")?; self.print_str(name)?; self.result.write_str(" ")?; self.print_component_val_type(state, ty)?; self.end_group()?; } self.end_group()?; Ok(()) } pub(crate) fn print_variant_type( &mut self, state: &State, cases: &[VariantCase], ) -> Result<()> { self.start_group("variant")?; for case in cases { self.result.write_str(" ")?; self.start_group("case ")?; self.print_str(case.name)?; if let Some(ty) = case.ty { self.result.write_str(" ")?; self.print_component_val_type(state, &ty)?; } if let Some(refines) = case.refines { self.result.write_str(" ")?; self.start_group("refines ")?; write!(&mut self.result, "{refines}")?; self.end_group()?; } self.end_group()?; } self.end_group()?; Ok(()) } pub(crate) fn print_list_type( &mut self, state: &State, element_ty: &ComponentValType, ) -> Result<()> { self.start_group("list ")?; self.print_component_val_type(state, element_ty)?; self.end_group()?; Ok(()) } pub(crate) fn print_fixed_size_list_type( &mut self, state: &State, element_ty: &ComponentValType, elements: u32, ) -> Result<()> { self.start_group("list ")?; self.print_component_val_type(state, element_ty)?; self.result.write_str(&format!(" {elements}"))?; self.end_group()?; Ok(()) } pub(crate) fn print_tuple_type( &mut self, state: &State, tys: &[ComponentValType], ) -> Result<()> { self.start_group("tuple")?; for ty in tys { self.result.write_str(" ")?; self.print_component_val_type(state, ty)?; } self.end_group()?; Ok(()) } pub(crate) fn print_flag_or_enum_type(&mut self, ty: &str, names: &[&str]) -> Result<()> { self.start_group(ty)?; for name in names { self.result.write_str(" ")?; self.print_str(name)?; } self.end_group()?; Ok(()) } pub(crate) fn print_option_type( &mut self, state: &State, inner: &ComponentValType, ) -> Result<()> { self.start_group("option ")?; self.print_component_val_type(state, inner)?; self.end_group()?; Ok(()) } pub(crate) fn print_result_type( &mut self, state: &State, ok: Option, err: Option, ) -> Result<()> { self.start_group("result")?; if let Some(ok) = ok { self.result.write_str(" ")?; self.print_component_val_type(state, &ok)?; } if let Some(err) = err { self.result.write_str(" ")?; self.start_group("error ")?; self.print_component_val_type(state, &err)?; self.end_group()?; } self.end_group()?; Ok(()) } fn print_future_type(&mut self, state: &State, ty: Option) -> Result<()> { self.start_group("future")?; if let Some(ty) = ty { self.result.write_str(" ")?; self.print_component_val_type(state, &ty)?; } self.end_group()?; Ok(()) } fn print_stream_type(&mut self, state: &State, ty: Option) -> Result<()> { self.start_group("stream")?; if let Some(ty) = ty { self.result.write_str(" ")?; self.print_component_val_type(state, &ty)?; } self.end_group()?; Ok(()) } pub(crate) fn print_defined_type( &mut self, state: &State, ty: &ComponentDefinedType, ) -> Result<()> { match ty { ComponentDefinedType::Primitive(ty) => self.print_primitive_val_type(ty)?, ComponentDefinedType::Record(fields) => self.print_record_type(state, fields)?, ComponentDefinedType::Variant(cases) => self.print_variant_type(state, cases)?, ComponentDefinedType::List(ty) => self.print_list_type(state, ty)?, ComponentDefinedType::FixedSizeList(ty, elements) => { self.print_fixed_size_list_type(state, ty, *elements)? } ComponentDefinedType::Tuple(tys) => self.print_tuple_type(state, tys)?, ComponentDefinedType::Flags(names) => self.print_flag_or_enum_type("flags", names)?, ComponentDefinedType::Enum(cases) => self.print_flag_or_enum_type("enum", cases)?, ComponentDefinedType::Option(ty) => self.print_option_type(state, ty)?, ComponentDefinedType::Result { ok, err } => self.print_result_type(state, *ok, *err)?, ComponentDefinedType::Own(idx) => { self.start_group("own ")?; self.print_idx(&state.component.type_names, *idx)?; self.end_group()?; } ComponentDefinedType::Borrow(idx) => { self.start_group("borrow ")?; self.print_idx(&state.component.type_names, *idx)?; self.end_group()?; } ComponentDefinedType::Future(ty) => self.print_future_type(state, *ty)?, ComponentDefinedType::Stream(ty) => self.print_stream_type(state, *ty)?, } Ok(()) } pub(crate) fn print_component_val_type( &mut self, state: &State, ty: &ComponentValType, ) -> Result<()> { match ty { ComponentValType::Primitive(ty) => self.print_primitive_val_type(ty), ComponentValType::Type(idx) => self.print_idx(&state.component.type_names, *idx), } } pub(crate) fn print_module_type( &mut self, states: &mut Vec, decls: Vec, ) -> Result<()> { states.push(State::new(Encoding::Module)); self.newline_unknown_pos()?; self.start_group("module")?; for decl in decls { self.newline_unknown_pos()?; match decl { ModuleTypeDeclaration::Type(rec) => { self.print_rec(states.last_mut().unwrap(), None, rec, false)? } ModuleTypeDeclaration::OuterAlias { kind, count, index } => { self.print_outer_alias(states, kind, count, index)?; } ModuleTypeDeclaration::Export { name, ty } => { self.start_group("export ")?; self.print_str(name)?; self.result.write_str(" ")?; self.print_import_ty(states.last_mut().unwrap(), &ty, false)?; self.end_group()?; } ModuleTypeDeclaration::Import(import) => { self.print_import(states.last_mut().unwrap(), &import, false)?; } } } self.end_group()?; states.pop(); Ok(()) } pub(crate) fn print_component_type<'a>( &mut self, states: &mut Vec, decls: Vec>, ) -> Result<()> { states.push(State::new(Encoding::Component)); self.newline_unknown_pos()?; self.start_group("component")?; for decl in decls { self.newline_unknown_pos()?; match decl { ComponentTypeDeclaration::CoreType(ty) => self.print_core_type(states, ty)?, ComponentTypeDeclaration::Type(ty) => self.print_component_type_def(states, ty)?, ComponentTypeDeclaration::Alias(alias) => { self.print_component_alias(states, alias)?; } ComponentTypeDeclaration::Export { name, ty } => { self.start_group("export ")?; self.print_component_kind_name(states.last_mut().unwrap(), ty.kind())?; self.result.write_str(" ")?; self.print_str(name.0)?; self.result.write_str(" ")?; self.print_component_import_ty(states.last_mut().unwrap(), &ty, false)?; self.end_group()?; } ComponentTypeDeclaration::Import(import) => { self.print_component_import(states.last_mut().unwrap(), &import, true)? } } } self.end_group()?; states.pop().unwrap(); Ok(()) } pub(crate) fn print_instance_type<'a>( &mut self, states: &mut Vec, decls: Vec>, ) -> Result<()> { states.push(State::new(Encoding::Component)); self.newline_unknown_pos()?; self.start_group("instance")?; for decl in decls { self.newline_unknown_pos()?; match decl { InstanceTypeDeclaration::CoreType(ty) => self.print_core_type(states, ty)?, InstanceTypeDeclaration::Type(ty) => self.print_component_type_def(states, ty)?, InstanceTypeDeclaration::Alias(alias) => { self.print_component_alias(states, alias)?; } InstanceTypeDeclaration::Export { name, ty } => { self.start_group("export ")?; self.print_component_kind_name(states.last_mut().unwrap(), ty.kind())?; self.result.write_str(" ")?; self.print_str(name.0)?; self.result.write_str(" ")?; self.print_component_import_ty(states.last_mut().unwrap(), &ty, false)?; self.end_group()?; } } } self.end_group()?; states.pop().unwrap(); Ok(()) } pub(crate) fn outer_state(states: &[State], count: u32) -> Result<&State> { if count as usize >= states.len() { bail!("invalid outer alias count {}", count); } let count: usize = std::cmp::min(count as usize, states.len() - 1); Ok(&states[states.len() - count - 1]) } pub(crate) fn print_outer_alias( &mut self, states: &mut [State], kind: OuterAliasKind, count: u32, index: u32, ) -> Result<()> { let state = states.last().unwrap(); let default_state = State::new(Encoding::Component); let outer = match Self::outer_state(states, count) { Ok(o) => o, Err(e) => { write!(self.result, "(; {e} ;) ")?; &default_state } }; self.start_group("alias outer ")?; if let Some(name) = outer.name.as_ref() { name.write(self)?; } else { write!(self.result, "{count}")?; } self.result.write_str(" ")?; match kind { OuterAliasKind::Type => { self.print_idx(&outer.core.type_names, index)?; self.result.write_str(" ")?; self.start_group("type ")?; self.print_name(&state.core.type_names, state.core.types.len() as u32)?; } } self.end_group()?; // kind self.end_group()?; // alias let state = states.last_mut().unwrap(); match kind { OuterAliasKind::Type => state.core.types.push(None), } Ok(()) } pub(crate) fn print_component_func_type( &mut self, state: &State, ty: &ComponentFuncType, ) -> Result<()> { self.start_group("func")?; for (name, ty) in ty.params.iter() { self.result.write_str(" ")?; self.start_group("param ")?; self.print_str(name)?; self.result.write_str(" ")?; self.print_component_val_type(state, ty)?; self.end_group()?; } if let Some(ty) = &ty.result { self.result.write_str(" ")?; self.start_group("result ")?; self.print_component_val_type(state, ty)?; self.end_group()?; } self.end_group()?; Ok(()) } pub(crate) fn print_component_type_def<'a>( &mut self, states: &mut Vec, ty: ComponentType<'a>, ) -> Result<()> { self.start_group("type ")?; { let state = states.last_mut().unwrap(); self.print_name(&state.component.type_names, state.component.types)?; } match ty { ComponentType::Defined(ty) => { self.result.write_str(" ")?; self.print_defined_type(states.last_mut().unwrap(), &ty)?; } ComponentType::Func(ty) => { self.result.write_str(" ")?; self.print_component_func_type(states.last_mut().unwrap(), &ty)?; } ComponentType::Component(decls) => { self.print_component_type(states, decls.into_vec())?; } ComponentType::Instance(decls) => { self.print_instance_type(states, decls.into_vec())?; } ComponentType::Resource { rep, dtor } => { self.result.write_str(" ")?; self.start_group("resource ")?; self.start_group("rep ")?; self.print_valtype(states.last().unwrap(), rep)?; self.end_group()?; if let Some(dtor) = dtor { self.result.write_str(" ")?; self.start_group("dtor ")?; self.start_group("func ")?; self.print_idx(&states.last().unwrap().core.func_names, dtor)?; self.end_group()?; self.end_group()?; } self.end_group()?; } } self.end_group()?; states.last_mut().unwrap().component.types += 1; Ok(()) } pub(crate) fn print_component_types<'a>( &mut self, states: &mut Vec, parser: ComponentTypeSectionReader<'a>, ) -> Result<()> { for ty in parser.into_iter_with_offsets() { let (offset, ty) = ty?; self.newline(offset)?; self.print_component_type_def(states, ty)?; } Ok(()) } pub(crate) fn print_component_imports( &mut self, state: &mut State, parser: ComponentImportSectionReader, ) -> Result<()> { for import in parser.into_iter_with_offsets() { let (offset, import) = import?; self.newline(offset)?; self.print_component_import(state, &import, true)?; } Ok(()) } pub(crate) fn print_component_import( &mut self, state: &mut State, import: &ComponentImport, index: bool, ) -> Result<()> { self.start_group("import ")?; self.print_str(import.name.0)?; self.result.write_str(" ")?; self.print_component_import_ty(state, &import.ty, index)?; self.end_group()?; Ok(()) } pub(crate) fn print_component_import_ty( &mut self, state: &mut State, ty: &ComponentTypeRef, index: bool, ) -> Result<()> { match ty { ComponentTypeRef::Module(idx) => { self.start_group("core module ")?; if index { self.print_name(&state.core.module_names, state.core.modules)?; self.result.write_str(" ")?; state.core.modules += 1; } self.print_core_type_ref(state, *idx)?; self.end_group()?; } ComponentTypeRef::Func(idx) => { self.start_group("func ")?; if index { self.print_name(&state.component.func_names, state.component.funcs)?; self.result.write_str(" ")?; state.component.funcs += 1; } self.print_component_type_ref(state, *idx)?; self.end_group()?; } ComponentTypeRef::Value(ty) => { self.start_group("value ")?; if index { self.print_name(&state.component.value_names, state.component.values)?; self.result.write_str(" ")?; state.component.values += 1; } match ty { ComponentValType::Primitive(ty) => self.print_primitive_val_type(ty)?, ComponentValType::Type(idx) => self.print_component_type_ref(state, *idx)?, } self.end_group()?; } ComponentTypeRef::Type(bounds) => { self.start_group("type ")?; if index { self.print_name(&state.component.type_names, state.component.types)?; self.result.write_str(" ")?; state.component.types += 1; } match bounds { TypeBounds::Eq(idx) => { self.start_group("eq ")?; self.print_idx(&state.component.type_names, *idx)?; self.end_group()?; } TypeBounds::SubResource => { self.start_group("sub ")?; self.print_type_keyword("resource")?; self.end_group()?; } }; self.end_group()?; } ComponentTypeRef::Instance(idx) => { self.start_group("instance ")?; if index { self.print_name(&state.component.instance_names, state.component.instances)?; self.result.write_str(" ")?; state.component.instances += 1; } self.print_component_type_ref(state, *idx)?; self.end_group()?; } ComponentTypeRef::Component(idx) => { self.start_group("component ")?; if index { self.print_name(&state.component.component_names, state.component.components)?; self.result.write_str(" ")?; state.component.components += 1; } self.print_component_type_ref(state, *idx)?; self.end_group()?; } } Ok(()) } pub(crate) fn print_component_exports( &mut self, state: &mut State, parser: ComponentExportSectionReader, ) -> Result<()> { for export in parser.into_iter_with_offsets() { let (offset, export) = export?; self.newline(offset)?; self.print_component_export(state, &export, true)?; } Ok(()) } pub(crate) fn print_component_export( &mut self, state: &mut State, export: &ComponentExport, named: bool, ) -> Result<()> { self.start_group("export ")?; if named { self.print_component_kind_name(state, export.kind)?; self.result.write_str(" ")?; } self.print_str(export.name.0)?; self.result.write_str(" ")?; self.print_component_external_kind(state, export.kind, export.index)?; if let Some(ty) = &export.ty { self.result.write_str(" ")?; self.print_component_import_ty(state, &ty, false)?; } self.end_group()?; Ok(()) } pub(crate) fn print_component_kind_name( &mut self, state: &mut State, kind: ComponentExternalKind, ) -> Result<()> { match kind { ComponentExternalKind::Func => { self.print_name(&state.component.func_names, state.component.funcs)?; state.component.funcs += 1; } ComponentExternalKind::Module => { self.print_name(&state.core.module_names, state.core.modules)?; state.core.modules += 1; } ComponentExternalKind::Value => { self.print_name(&state.component.value_names, state.component.values)?; state.component.values += 1; } ComponentExternalKind::Type => { self.print_name(&state.component.type_names, state.component.types)?; state.component.types += 1; } ComponentExternalKind::Instance => { self.print_name(&state.component.instance_names, state.component.instances)?; state.component.instances += 1; } ComponentExternalKind::Component => { self.print_name(&state.component.component_names, state.component.components)?; state.component.components += 1; } } Ok(()) } pub(crate) fn start_component_external_kind_group( &mut self, kind: ComponentExternalKind, ) -> Result<()> { match kind { ComponentExternalKind::Module => { self.start_group("core module ")?; } ComponentExternalKind::Component => { self.start_group("component ")?; } ComponentExternalKind::Instance => { self.start_group("instance ")?; } ComponentExternalKind::Func => { self.start_group("func ")?; } ComponentExternalKind::Value => { self.start_group("value ")?; } ComponentExternalKind::Type => { self.start_group("type ")?; } } Ok(()) } pub(crate) fn print_component_external_kind( &mut self, state: &State, kind: ComponentExternalKind, index: u32, ) -> Result<()> { self.start_component_external_kind_group(kind)?; match kind { ComponentExternalKind::Module => { self.print_idx(&state.core.module_names, index)?; } ComponentExternalKind::Component => { self.print_idx(&state.component.component_names, index)?; } ComponentExternalKind::Instance => { self.print_idx(&state.component.instance_names, index)?; } ComponentExternalKind::Func => { self.print_idx(&state.component.func_names, index)?; } ComponentExternalKind::Value => { self.print_idx(&state.component.value_names, index)?; } ComponentExternalKind::Type => { self.print_idx(&state.component.type_names, index)?; } } self.end_group()?; Ok(()) } pub(crate) fn print_canonical_options( &mut self, state: &State, options: &[CanonicalOption], ) -> Result<()> { for option in options { self.result.write_str(" ")?; match option { CanonicalOption::UTF8 => self.result.write_str("string-encoding=utf8")?, CanonicalOption::UTF16 => self.result.write_str("string-encoding=utf16")?, CanonicalOption::CompactUTF16 => { self.result.write_str("string-encoding=latin1+utf16")? } CanonicalOption::Memory(idx) => { self.start_group("memory ")?; self.print_idx(&state.core.memory_names, *idx)?; self.end_group()?; } CanonicalOption::Realloc(idx) => { self.start_group("realloc ")?; self.print_idx(&state.core.func_names, *idx)?; self.end_group()?; } CanonicalOption::PostReturn(idx) => { self.start_group("post-return ")?; self.print_idx(&state.core.func_names, *idx)?; self.end_group()?; } CanonicalOption::Async => self.print_type_keyword("async")?, CanonicalOption::Callback(idx) => { self.start_group("callback ")?; self.print_idx(&state.core.func_names, *idx)?; self.end_group()?; } CanonicalOption::CoreType(idx) => { self.start_group("core-type ")?; self.print_idx(&state.core.type_names, *idx)?; self.end_group()?; } CanonicalOption::Gc => self.result.write_str("gc")?, } } Ok(()) } fn print_intrinsic( &mut self, state: &mut State, group: &str, body: &dyn Fn(&mut Self, &mut State) -> Result<()>, ) -> Result<()> { self.start_group("core func ")?; self.print_name(&state.core.func_names, state.core.funcs)?; self.result.write_str(" ")?; self.start_group(group)?; body(self, state)?; self.end_group()?; self.end_group()?; debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; state.core.func_to_type.push(None); Ok(()) } pub(crate) fn print_canonical_functions( &mut self, state: &mut State, parser: ComponentCanonicalSectionReader, ) -> Result<()> { for func in parser.into_iter_with_offsets() { let (offset, func) = func?; self.newline(offset)?; match func { CanonicalFunction::Lift { core_func_index, type_index, options, } => { self.start_group("func ")?; self.print_name(&state.component.func_names, state.component.funcs)?; self.result.write_str(" ")?; self.start_group("type ")?; self.print_idx(&state.component.type_names, type_index)?; self.end_group()?; self.result.write_str(" ")?; self.start_group("canon lift ")?; self.start_group("core func ")?; self.print_idx(&state.core.func_names, core_func_index)?; self.end_group()?; self.print_canonical_options(state, &options)?; self.end_group()?; self.end_group()?; state.component.funcs += 1; } CanonicalFunction::Lower { func_index, options, } => { self.print_intrinsic(state, "canon lower ", &|me, state| { me.start_group("func ")?; me.print_idx(&state.component.func_names, func_index)?; me.end_group()?; me.print_canonical_options(state, &options) })?; } CanonicalFunction::ResourceNew { resource } => { self.print_intrinsic(state, "canon resource.new ", &|me, state| { me.print_idx(&state.component.type_names, resource) })?; } CanonicalFunction::ResourceDrop { resource } => { self.print_intrinsic(state, "canon resource.drop ", &|me, state| { me.print_idx(&state.component.type_names, resource) })?; } CanonicalFunction::ResourceDropAsync { resource } => { self.print_intrinsic(state, "canon resource.drop ", &|me, state| { me.print_idx(&state.component.type_names, resource)?; me.print_type_keyword(" async") })?; } CanonicalFunction::ResourceRep { resource } => { self.print_intrinsic(state, "canon resource.rep ", &|me, state| { me.print_idx(&state.component.type_names, resource) })?; } CanonicalFunction::ThreadSpawnRef { func_ty_index } => { self.print_intrinsic(state, "canon thread.spawn_ref ", &|me, state| { me.print_idx(&state.core.type_names, func_ty_index) })?; } CanonicalFunction::ThreadSpawnIndirect { func_ty_index, table_index, } => { self.print_intrinsic(state, "canon thread.spawn_indirect ", &|me, state| { me.print_idx(&state.core.type_names, func_ty_index)?; me.result.write_str(" ")?; me.start_group("table ")?; me.print_idx(&state.core.table_names, table_index)?; me.end_group() })?; } CanonicalFunction::ThreadAvailableParallelism => { self.print_intrinsic(state, "canon thread.available_parallelism", &|_, _| { Ok(()) })?; } CanonicalFunction::BackpressureSet => { self.print_intrinsic(state, "canon backpressure.set", &|_, _| Ok(()))?; } CanonicalFunction::BackpressureInc => { self.print_intrinsic(state, "canon backpressure.inc", &|_, _| Ok(()))?; } CanonicalFunction::BackpressureDec => { self.print_intrinsic(state, "canon backpressure.dec", &|_, _| Ok(()))?; } CanonicalFunction::TaskReturn { result, options } => { self.print_intrinsic(state, "canon task.return", &|me, state| { if let Some(ty) = result { me.result.write_str(" ")?; me.start_group("result ")?; me.print_component_val_type(state, &ty)?; me.end_group()?; } me.print_canonical_options(state, &options)?; Ok(()) })?; } CanonicalFunction::TaskCancel => { self.print_intrinsic(state, "canon task.cancel", &|_, _| Ok(()))?; } CanonicalFunction::ContextGet(i) => { self.print_intrinsic(state, "canon context.get", &|me, _state| { write!(me.result, " i32 {i}")?; Ok(()) })?; } CanonicalFunction::ContextSet(i) => { self.print_intrinsic(state, "canon context.set", &|me, _state| { write!(me.result, " i32 {i}")?; Ok(()) })?; } CanonicalFunction::ThreadYield { cancellable } => { self.print_intrinsic(state, "canon thread.yield", &|me, _| { if cancellable { me.print_type_keyword(" cancellable")?; } Ok(()) })?; } CanonicalFunction::SubtaskDrop => { self.print_intrinsic(state, "canon subtask.drop", &|_, _| Ok(()))?; } CanonicalFunction::SubtaskCancel { async_ } => { self.print_intrinsic(state, "canon subtask.cancel", &|me, _| { if async_ { me.print_type_keyword(" async")?; } Ok(()) })?; } CanonicalFunction::StreamNew { ty } => { self.print_intrinsic(state, "canon stream.new ", &|me, state| { me.print_idx(&state.component.type_names, ty) })?; } CanonicalFunction::StreamRead { ty, options } => { self.print_intrinsic(state, "canon stream.read ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; me.print_canonical_options(state, &options) })?; } CanonicalFunction::StreamWrite { ty, options } => { self.print_intrinsic(state, "canon stream.write ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; me.print_canonical_options(state, &options) })?; } CanonicalFunction::StreamCancelRead { ty, async_ } => { self.print_intrinsic(state, "canon stream.cancel-read ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; if async_ { me.print_type_keyword(" async")?; } Ok(()) })?; } CanonicalFunction::StreamCancelWrite { ty, async_ } => { self.print_intrinsic(state, "canon stream.cancel-write ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; if async_ { me.print_type_keyword(" async")?; } Ok(()) })?; } CanonicalFunction::StreamDropReadable { ty } => { self.print_intrinsic(state, "canon stream.drop-readable ", &|me, state| { me.print_idx(&state.component.type_names, ty) })?; } CanonicalFunction::StreamDropWritable { ty } => { self.print_intrinsic(state, "canon stream.drop-writable ", &|me, state| { me.print_idx(&state.component.type_names, ty) })?; } CanonicalFunction::FutureNew { ty } => { self.print_intrinsic(state, "canon future.new ", &|me, state| { me.print_idx(&state.component.type_names, ty) })?; } CanonicalFunction::FutureRead { ty, options } => { self.print_intrinsic(state, "canon future.read ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; me.print_canonical_options(state, &options) })?; } CanonicalFunction::FutureWrite { ty, options } => { self.print_intrinsic(state, "canon future.write ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; me.print_canonical_options(state, &options) })?; } CanonicalFunction::FutureCancelRead { ty, async_ } => { self.print_intrinsic(state, "canon future.cancel-read ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; if async_ { me.print_type_keyword(" async")?; } Ok(()) })?; } CanonicalFunction::FutureCancelWrite { ty, async_ } => { self.print_intrinsic(state, "canon future.cancel-write ", &|me, state| { me.print_idx(&state.component.type_names, ty)?; if async_ { me.print_type_keyword(" async")?; } Ok(()) })?; } CanonicalFunction::FutureDropReadable { ty } => { self.print_intrinsic(state, "canon future.drop-readable ", &|me, state| { me.print_idx(&state.component.type_names, ty) })?; } CanonicalFunction::FutureDropWritable { ty } => { self.print_intrinsic(state, "canon future.drop-writable ", &|me, state| { me.print_idx(&state.component.type_names, ty) })?; } CanonicalFunction::ErrorContextNew { options } => { self.print_intrinsic(state, "canon error-context.new", &|me, state| { me.print_canonical_options(state, &options) })?; } CanonicalFunction::ErrorContextDebugMessage { options } => { self.print_intrinsic( state, "canon error-context.debug-message", &|me, state| me.print_canonical_options(state, &options), )?; } CanonicalFunction::ErrorContextDrop => { self.print_intrinsic(state, "canon error-context.drop", &|_, _| Ok(()))?; } CanonicalFunction::WaitableSetNew => { self.print_intrinsic(state, "canon waitable-set.new", &|_, _| Ok(()))?; } CanonicalFunction::WaitableSetWait { cancellable, memory, } => { self.print_intrinsic(state, "canon waitable-set.wait ", &|me, state| { if cancellable { me.result.write_str("cancellable ")?; } me.start_group("memory ")?; me.print_idx(&state.core.memory_names, memory)?; me.end_group() })?; } CanonicalFunction::WaitableSetPoll { cancellable, memory, } => { self.print_intrinsic(state, "canon waitable-set.poll ", &|me, state| { if cancellable { me.result.write_str("cancellable ")?; } me.start_group("memory ")?; me.print_idx(&state.core.memory_names, memory)?; me.end_group() })?; } CanonicalFunction::WaitableSetDrop => { self.print_intrinsic(state, "canon waitable-set.drop", &|_, _| Ok(()))?; } CanonicalFunction::WaitableJoin => { self.print_intrinsic(state, "canon waitable.join", &|_, _| Ok(()))?; } CanonicalFunction::ThreadIndex => { self.print_intrinsic(state, "canon thread.index", &|_, _| Ok(()))?; } CanonicalFunction::ThreadNewIndirect { func_ty_index, table_index, } => { self.print_intrinsic(state, "canon thread.new_indirect ", &|me, state| { me.print_idx(&state.core.type_names, func_ty_index)?; me.result.write_str(" ")?; me.start_group("table ")?; me.print_idx(&state.core.table_names, table_index)?; me.end_group() })?; } CanonicalFunction::ThreadSwitchTo { cancellable } => { self.print_intrinsic(state, "canon thread.switch-to", &|me, _| { if cancellable { me.result.write_str(" cancellable")?; } Ok(()) })?; } CanonicalFunction::ThreadSuspend { cancellable } => { self.print_intrinsic(state, "canon thread.suspend", &|me, _| { if cancellable { me.result.write_str(" cancellable")?; } Ok(()) })?; } CanonicalFunction::ThreadResumeLater => { self.print_intrinsic(state, "canon thread.resume-later", &|_, _| Ok(()))?; } CanonicalFunction::ThreadYieldTo { cancellable } => { self.print_intrinsic(state, "canon thread.yield-to", &|me, _| { if cancellable { me.result.write_str(" cancellable")?; } Ok(()) })?; } } } Ok(()) } pub(crate) fn print_instances( &mut self, state: &mut State, parser: InstanceSectionReader, ) -> Result<()> { for instance in parser.into_iter_with_offsets() { let (offset, instance) = instance?; self.newline(offset)?; self.start_group("core instance ")?; self.print_name(&state.core.instance_names, state.core.instances)?; match instance { Instance::Instantiate { module_index, args } => { self.result.write_str(" ")?; self.start_group("instantiate ")?; self.print_idx(&state.core.module_names, module_index)?; for arg in args.iter() { self.newline(offset)?; self.print_instantiation_arg(state, arg)?; } self.end_group()?; state.core.instances += 1; } Instance::FromExports(exports) => { for export in exports.iter() { self.newline(offset)?; self.print_export(state, export)?; } state.core.instances += 1; } } self.end_group()?; } Ok(()) } pub(crate) fn print_component_instances( &mut self, state: &mut State, parser: ComponentInstanceSectionReader, ) -> Result<()> { for instance in parser.into_iter_with_offsets() { let (offset, instance) = instance?; self.newline(offset)?; self.start_group("instance ")?; self.print_name(&state.component.instance_names, state.component.instances)?; state.component.instances += 1; match instance { ComponentInstance::Instantiate { component_index, args, } => { self.result.write_str(" ")?; self.start_group("instantiate ")?; self.print_idx(&state.component.component_names, component_index)?; for arg in args.iter() { self.newline(offset)?; self.print_component_instantiation_arg(state, arg)?; } self.end_group()?; } ComponentInstance::FromExports(exports) => { for export in exports.iter() { self.newline(offset)?; self.print_component_export(state, export, false)?; } } } self.end_group()?; } Ok(()) } pub(crate) fn print_instantiation_arg( &mut self, state: &State, arg: &InstantiationArg, ) -> Result<()> { self.start_group("with ")?; self.print_str(arg.name)?; self.result.write_str(" ")?; match arg.kind { InstantiationArgKind::Instance => { self.start_group("instance ")?; self.print_idx(&state.core.instance_names, arg.index)?; self.end_group()?; } } self.end_group()?; Ok(()) } pub(crate) fn print_component_instantiation_arg( &mut self, state: &State, arg: &ComponentInstantiationArg, ) -> Result<()> { self.start_group("with ")?; self.print_str(arg.name)?; self.result.write_str(" ")?; self.print_component_external_kind(state, arg.kind, arg.index)?; self.end_group()?; Ok(()) } pub(crate) fn print_component_start( &mut self, state: &mut State, pos: usize, start: ComponentStartFunction, ) -> Result<()> { self.newline(pos)?; self.start_group("start ")?; self.print_idx(&state.component.func_names, start.func_index)?; for arg in start.arguments.iter() { self.result.write_str(" ")?; self.start_group("value ")?; self.print_idx(&state.component.value_names, *arg)?; self.end_group()?; } for _ in 0..start.results { self.result.write_str(" ")?; self.start_group("result ")?; self.start_group("value ")?; self.print_name(&state.component.value_names, state.component.values)?; self.end_group()?; self.end_group()?; state.component.values += 1; } self.end_group()?; // start Ok(()) } pub(crate) fn print_component_aliases( &mut self, states: &mut [State], parser: ComponentAliasSectionReader, ) -> Result<()> { for alias in parser.into_iter_with_offsets() { let (offset, alias) = alias?; self.newline(offset)?; self.print_component_alias(states, alias)?; } Ok(()) } pub(crate) fn print_component_alias( &mut self, states: &mut [State], alias: ComponentAlias<'_>, ) -> Result<()> { match alias { ComponentAlias::InstanceExport { kind, instance_index, name, } => { let state = states.last_mut().unwrap(); self.start_group("alias export ")?; self.print_idx(&state.component.instance_names, instance_index)?; self.result.write_str(" ")?; self.print_str(name)?; self.result.write_str(" ")?; self.start_component_external_kind_group(kind)?; self.print_component_kind_name(state, kind)?; self.end_group()?; self.end_group()?; // alias export } ComponentAlias::CoreInstanceExport { instance_index, kind, name, } => { let state = states.last_mut().unwrap(); self.start_group("alias core export ")?; self.print_idx(&state.core.instance_names, instance_index)?; self.result.write_str(" ")?; self.print_str(name)?; self.result.write_str(" ")?; match kind { ExternalKind::Func => { self.start_group("core func ")?; self.print_name(&state.core.func_names, state.core.funcs)?; self.end_group()?; debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; state.core.func_to_type.push(None) } ExternalKind::Table => { self.start_group("core table ")?; self.print_name(&state.core.table_names, state.core.tables)?; self.end_group()?; state.core.tables += 1; } ExternalKind::Memory => { self.start_group("core memory ")?; self.print_name(&state.core.memory_names, state.core.memories)?; self.end_group()?; state.core.memories += 1; } ExternalKind::Global => { self.start_group("core global ")?; self.print_name(&state.core.global_names, state.core.globals)?; self.end_group()?; state.core.globals += 1; } ExternalKind::Tag => { self.start_group("core tag ")?; self.print_name(&state.core.tag_names, state.core.tags)?; self.end_group()?; debug_assert_eq!(state.core.tag_to_type.len(), state.core.tags as usize); state.core.tags += 1; state.core.tag_to_type.push(None) } } self.end_group()?; // alias export } ComponentAlias::Outer { kind, count, index } => { let state = states.last().unwrap(); let default_state = State::new(Encoding::Component); let outer = match Self::outer_state(states, count) { Ok(o) => o, Err(e) => { write!(self.result, "(; {e} ;) ")?; &default_state } }; self.start_group("alias outer ")?; if let Some(name) = outer.name.as_ref() { name.write(self)?; } else { write!(self.result, "{count}")?; } self.result.write_str(" ")?; match kind { ComponentOuterAliasKind::CoreModule => { self.print_idx(&outer.core.module_names, index)?; self.result.write_str(" ")?; self.start_group("core module ")?; self.print_name(&state.core.module_names, state.core.modules)?; } ComponentOuterAliasKind::CoreType => { self.print_idx(&outer.core.type_names, index)?; self.result.write_str(" ")?; self.start_group("core type ")?; self.print_name(&state.core.type_names, state.core.types.len() as u32)?; } ComponentOuterAliasKind::Type => { self.print_idx(&outer.component.type_names, index)?; self.result.write_str(" ")?; self.start_group("type ")?; self.print_name(&state.component.type_names, state.component.types)?; } ComponentOuterAliasKind::Component => { self.print_idx(&outer.component.component_names, index)?; self.result.write_str(" ")?; self.start_group("component ")?; self.print_name( &state.component.component_names, state.component.components, )?; } } self.end_group()?; // kind self.end_group()?; // alias let state = states.last_mut().unwrap(); match kind { ComponentOuterAliasKind::CoreModule => state.core.modules += 1, ComponentOuterAliasKind::CoreType => state.core.types.push(None), ComponentOuterAliasKind::Type => state.component.types += 1, ComponentOuterAliasKind::Component => state.component.components += 1, } } } Ok(()) } } wasmprinter-0.239.0/src/lib.rs000064400000000000000000002572501046102023000142700ustar 00000000000000//! A crate to convert a WebAssembly binary to its textual representation in the //! WebAssembly Text Format (WAT). //! //! This crate is intended for developer toolchains and debugging, supporting //! human-readable versions of a wasm binary. This can also be useful when //! developing wasm toolchain support in Rust for various purposes like testing //! and debugging and such. #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] use anyhow::{Context, Result, anyhow, bail}; use operator::{OpPrinter, OperatorSeparator, OperatorState, PrintOperator, PrintOperatorFolded}; use std::collections::{HashMap, HashSet}; use std::fmt; use std::io; use std::marker; use std::mem; use std::path::Path; use wasmparser::*; const MAX_LOCALS: u32 = 50000; const MAX_NESTING_TO_PRINT: u32 = 50; const MAX_WASM_FUNCTIONS: u32 = 1_000_000; const MAX_WASM_FUNCTION_SIZE: u32 = 128 * 1024; #[cfg(feature = "component-model")] mod component; #[cfg(feature = "validate")] mod operand_stack; #[cfg(not(feature = "validate"))] mod operand_stack_disabled; #[cfg(not(feature = "validate"))] use operand_stack_disabled as operand_stack; mod operator; mod print; pub use self::print::*; /// Reads a WebAssembly `file` from the filesystem and then prints it into an /// in-memory `String`. pub fn print_file(file: impl AsRef) -> Result { let file = file.as_ref(); let contents = std::fs::read(file).context(format!("failed to read `{}`", file.display()))?; print_bytes(contents) } /// Prints an in-memory `wasm` binary blob into an in-memory `String` which is /// its textual representation. pub fn print_bytes(wasm: impl AsRef<[u8]>) -> Result { let mut dst = String::new(); Config::new().print(wasm.as_ref(), &mut PrintFmtWrite(&mut dst))?; Ok(dst) } /// Configuration used to print a WebAssembly binary. /// /// This structure is used to control the overal structure of how wasm binaries /// are printed and tweaks various ways that configures the output. #[derive(Debug)] pub struct Config { print_offsets: bool, print_skeleton: bool, name_unnamed: bool, fold_instructions: bool, indent_text: String, print_operand_stack: bool, } impl Default for Config { fn default() -> Self { Self { print_offsets: false, print_skeleton: false, name_unnamed: false, fold_instructions: false, indent_text: " ".to_string(), print_operand_stack: false, } } } /// This structure is the actual structure that prints WebAssembly binaries. struct Printer<'cfg, 'env> { config: &'cfg Config, result: &'cfg mut (dyn Print + 'env), nesting: u32, line: usize, group_lines: Vec, code_section_hints: Vec<(u32, Vec<(usize, BranchHint)>)>, } #[derive(Default)] struct CoreState { types: Vec>, funcs: u32, func_to_type: Vec>, memories: u32, tags: u32, tag_to_type: Vec>, globals: u32, tables: u32, #[cfg(feature = "component-model")] modules: u32, #[cfg(feature = "component-model")] instances: u32, func_names: NamingMap, local_names: NamingMap<(u32, u32), NameLocal>, label_names: NamingMap<(u32, u32), NameLabel>, type_names: NamingMap, field_names: NamingMap<(u32, u32), NameField>, tag_names: NamingMap, table_names: NamingMap, memory_names: NamingMap, global_names: NamingMap, element_names: NamingMap, data_names: NamingMap, #[cfg(feature = "component-model")] module_names: NamingMap, #[cfg(feature = "component-model")] instance_names: NamingMap, } /// A map of index-to-name for tracking what are the contents of the name /// section. /// /// The type parameter `T` is either `u32` for most index-based maps or a `(u32, /// u32)` for label/local maps where there are two levels of indices. /// /// The type parameter `K` is a static description/namespace for what kind of /// item is contained within this map. That's used by some helper methods to /// synthesize reasonable names automatically. struct NamingMap { index_to_name: HashMap, _marker: marker::PhantomData, } impl Default for NamingMap { fn default() -> NamingMap { NamingMap { index_to_name: HashMap::new(), _marker: marker::PhantomData, } } } #[derive(Default)] #[cfg(feature = "component-model")] struct ComponentState { types: u32, funcs: u32, instances: u32, components: u32, values: u32, type_names: NamingMap, func_names: NamingMap, component_names: NamingMap, instance_names: NamingMap, value_names: NamingMap, } struct State { encoding: Encoding, name: Option, core: CoreState, #[cfg(feature = "component-model")] component: ComponentState, custom_section_place: Option<(&'static str, usize)>, // `custom_section_place` stores the text representation of the location where // a custom section should be serialized in the binary format. // The tuple elements are a str (e.g. "after elem") and the line number // where the custom section place was set. `update_custom_section_place` won't // update the custom section place unless the line number changes; this prevents // printing a place "after xxx" where the xxx section doesn't appear in the text format // (e.g. because it was present but empty in the binary format). } impl State { fn new(encoding: Encoding) -> Self { Self { encoding, name: None, core: CoreState::default(), #[cfg(feature = "component-model")] component: ComponentState::default(), custom_section_place: None, } } } struct Naming { name: String, kind: NamingKind, } enum NamingKind { DollarName, DollarQuotedName, SyntheticPrefix(String), } impl Config { /// Creates a new [`Config`] object that's ready to start printing wasm /// binaries to strings. pub fn new() -> Self { Self::default() } /// Whether or not to print binary offsets of each item as comments in the /// text format whenever a newline is printed. pub fn print_offsets(&mut self, print: bool) -> &mut Self { self.print_offsets = print; self } /// Whether or not to print only a "skeleton" which skips function bodies, /// data segment contents, element segment contents, etc. pub fn print_skeleton(&mut self, print: bool) -> &mut Self { self.print_skeleton = print; self } /// Assign names to all unnamed items. /// /// If enabled then any previously unnamed item will have a name synthesized /// that looks like `$#func10` for example. The leading `#` indicates that /// it's `wasmprinter`-generated. The `func` is the namespace of the name /// and provides extra context about the item when referenced. The 10 is the /// local index of the item. /// /// Note that if the resulting text output is converted back to binary the /// resulting `name` custom section will not be the same as before. pub fn name_unnamed(&mut self, enable: bool) -> &mut Self { self.name_unnamed = enable; self } /// Print instructions in folded form where possible. /// /// This will cause printing to favor the s-expression (parenthesized) form /// of WebAssembly instructions. For example this output would be generated /// for a simple `add` function: /// /// ```wasm /// (module /// (func $foo (param i32 i32) (result i32) /// (i32.add /// (local.get 0) /// (local.get 1)) /// ) /// ) /// ``` pub fn fold_instructions(&mut self, enable: bool) -> &mut Self { self.fold_instructions = enable; self } /// Print the operand stack types within function bodies, /// flagging newly pushed operands when color output is enabled. E.g.: /// /// ```wasm /// (module /// (type (;0;) (func)) /// (func (;0;) (type 0) /// i32.const 4 /// ;; [i32] /// i32.const 5 /// ;; [i32 i32] /// i32.add /// ;; [i32] /// drop /// ;; [] /// ) /// ) /// ``` #[cfg(feature = "validate")] pub fn print_operand_stack(&mut self, enable: bool) -> &mut Self { self.print_operand_stack = enable; self } /// Select the string to use when indenting. /// /// The indent allowed here are arbitrary and unchecked. You should enter /// blank text like `" "` or `"\t"`, rather than something like `"(;;)"`. /// /// The default setting is double spaces `" "` pub fn indent_text(&mut self, text: impl Into) -> &mut Self { self.indent_text = text.into(); self } /// Print a WebAssembly binary. /// /// This function takes an entire `wasm` binary blob and prints it to the /// `result` in the WebAssembly Text Format. pub fn print(&self, wasm: &[u8], result: &mut impl Print) -> Result<()> { Printer { config: self, result, code_section_hints: Vec::new(), group_lines: Vec::new(), line: 0, nesting: 0, } .print_contents(wasm) } /// Get the line-by-line WAT disassembly for the given Wasm, along with the /// binary offsets for each line. pub fn offsets_and_lines<'a>( &self, wasm: &[u8], storage: &'a mut String, ) -> Result, &'a str)> + 'a> { struct TrackingPrint<'a> { dst: &'a mut String, lines: Vec, line_offsets: Vec>, } impl Print for TrackingPrint<'_> { fn write_str(&mut self, s: &str) -> io::Result<()> { self.dst.push_str(s); Ok(()) } fn start_line(&mut self, offset: Option) { self.lines.push(self.dst.len()); self.line_offsets.push(offset); } } let mut output = TrackingPrint { dst: storage, lines: Vec::new(), line_offsets: Vec::new(), }; self.print(wasm, &mut output)?; let TrackingPrint { dst, lines, line_offsets, } = output; let end = dst.len(); let dst = &dst[..]; let mut offsets = line_offsets.into_iter(); let mut lines = lines.into_iter().peekable(); Ok(std::iter::from_fn(move || { let offset = offsets.next()?; let i = lines.next()?; let j = lines.peek().copied().unwrap_or(end); let line = &dst[i..j]; Some((offset, line)) })) } } impl Printer<'_, '_> { fn read_names<'a>( &mut self, mut bytes: &'a [u8], mut parser: Parser, state: &mut State, ) -> Result<()> { loop { let payload = match parser.parse(bytes, true)? { Chunk::NeedMoreData(_) => unreachable!(), Chunk::Parsed { payload, consumed } => { bytes = &bytes[consumed..]; payload } }; match payload { Payload::CodeSectionStart { size, .. } => { if size as usize > bytes.len() { bail!("invalid code section size"); } bytes = &bytes[size as usize..]; parser.skip_section(); } #[cfg(feature = "component-model")] Payload::ModuleSection { unchecked_range: range, .. } | Payload::ComponentSection { unchecked_range: range, .. } => { let offset = range.end - range.start; if offset > bytes.len() { bail!("invalid module or component section range"); } bytes = &bytes[offset..]; } Payload::CustomSection(c) => { // Ignore any error associated with the name sections. match c.as_known() { KnownCustom::Name(reader) => { drop(self.register_names(state, reader)); } #[cfg(feature = "component-model")] KnownCustom::ComponentName(reader) => { drop(self.register_component_names(state, reader)); } KnownCustom::BranchHints(reader) => { drop(self.register_branch_hint_section(reader)); } _ => {} } } Payload::End(_) => break, _ => {} } } Ok(()) } fn ensure_module(states: &[State]) -> Result<()> { if !matches!(states.last().unwrap().encoding, Encoding::Module) { bail!("a module section was encountered when parsing a component"); } Ok(()) } #[cfg(feature = "component-model")] fn ensure_component(states: &[State]) -> Result<()> { if !matches!(states.last().unwrap().encoding, Encoding::Component) { bail!("a component section was encountered when parsing a module"); } Ok(()) } fn print_contents(&mut self, mut bytes: &[u8]) -> Result<()> { self.result.start_line(Some(0)); let mut expected = None; let mut states: Vec = Vec::new(); let mut parser = Parser::new(0); #[cfg(feature = "component-model")] let mut parsers = Vec::new(); let mut validator = if self.config.print_operand_stack { operand_stack::Validator::new() } else { None }; loop { let payload = match parser.parse(bytes, true)? { Chunk::NeedMoreData(_) => unreachable!(), Chunk::Parsed { payload, consumed } => { bytes = &bytes[consumed..]; payload } }; if let Some(validator) = &mut validator { match validator.payload(&payload) { Ok(()) => {} Err(e) => { self.newline_unknown_pos()?; write!(self.result, ";; module or component is invalid: {e}")?; } } } match payload { Payload::Version { encoding, .. } => { if let Some(e) = expected { if encoding != e { bail!("incorrect encoding for nested module or component"); } expected = None; } assert!(states.last().map(|s| s.encoding) != Some(Encoding::Module)); match encoding { Encoding::Module => { states.push(State::new(Encoding::Module)); states.last_mut().unwrap().custom_section_place = Some(("before first", self.line)); if states.len() > 1 { self.start_group("core module")?; } else { self.start_group("module")?; } #[cfg(feature = "component-model")] if states.len() > 1 { let parent = &states[states.len() - 2]; self.result.write_str(" ")?; self.print_name(&parent.core.module_names, parent.core.modules)?; } } Encoding::Component => { #[cfg(feature = "component-model")] { states.push(State::new(Encoding::Component)); self.start_group("component")?; if states.len() > 1 { let parent = &states[states.len() - 2]; self.result.write_str(" ")?; self.print_name( &parent.component.component_names, parent.component.components, )?; } } #[cfg(not(feature = "component-model"))] { bail!( "support for printing components disabled \ at compile-time" ); } } } let len = states.len(); let state = states.last_mut().unwrap(); // First up try to find the `name` subsection which we'll use to print // pretty names everywhere. self.read_names(bytes, parser.clone(), state)?; if len == 1 { if let Some(name) = state.name.as_ref() { self.result.write_str(" ")?; name.write(self)?; } } } Payload::CustomSection(c) => { // If the custom printing trait handles this section, keep // going after that. let printed = self.result .print_custom_section(c.name(), c.data_offset(), c.data())?; if printed { self.update_custom_section_line(&mut states); continue; } // If this wasn't handled specifically above then try to // print the known custom builtin sections. If this fails // because the custom section is malformed then print the // raw contents instead. let state = states.last().unwrap(); let start = self.nesting; match c.as_known() { KnownCustom::Unknown => self.print_raw_custom_section(state, c.clone())?, _ => { match (Printer { config: self.config, result: &mut PrintFmtWrite(String::new()), nesting: 0, line: 0, group_lines: Vec::new(), code_section_hints: Vec::new(), }) .print_known_custom_section(c.clone()) { Ok(true) => { self.print_known_custom_section(c.clone())?; } Ok(false) => self.print_raw_custom_section(state, c.clone())?, Err(e) if !e.is::() => return Err(e), Err(e) => { let msg = format!( "failed to parse custom section `{}`: {e}", c.name() ); for line in msg.lines() { self.newline(c.data_offset())?; write!(self.result, ";; {line}")?; } self.print_raw_custom_section(state, c.clone())? } } } } assert!(self.nesting == start); self.update_custom_section_line(&mut states); } Payload::TypeSection(s) => { self.print_types(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after type"); } Payload::ImportSection(s) => { Self::ensure_module(&states)?; self.print_imports(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after import"); } Payload::FunctionSection(reader) => { Self::ensure_module(&states)?; if reader.count() > MAX_WASM_FUNCTIONS { bail!( "module contains {} functions which exceeds the limit of {}", reader.count(), MAX_WASM_FUNCTIONS ); } for ty in reader { states.last_mut().unwrap().core.func_to_type.push(Some(ty?)) } self.update_custom_section_place(&mut states, "after func"); } Payload::TableSection(s) => { Self::ensure_module(&states)?; self.print_tables(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after table"); } Payload::MemorySection(s) => { Self::ensure_module(&states)?; self.print_memories(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after memory"); } Payload::TagSection(s) => { Self::ensure_module(&states)?; self.print_tags(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after tag"); } Payload::GlobalSection(s) => { Self::ensure_module(&states)?; self.print_globals(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after global"); } Payload::ExportSection(s) => { Self::ensure_module(&states)?; self.print_exports(states.last().unwrap(), s)?; self.update_custom_section_place(&mut states, "after export"); } Payload::StartSection { func, range } => { Self::ensure_module(&states)?; self.newline(range.start)?; self.start_group("start ")?; self.print_idx(&states.last().unwrap().core.func_names, func)?; self.end_group()?; self.update_custom_section_place(&mut states, "after start"); } Payload::ElementSection(s) => { Self::ensure_module(&states)?; self.print_elems(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after elem"); } Payload::CodeSectionStart { .. } => { Self::ensure_module(&states)?; } Payload::CodeSectionEntry(body) => { self.print_code_section_entry( states.last_mut().unwrap(), &body, validator.as_mut().and_then(|v| v.next_func()), )?; self.update_custom_section_place(&mut states, "after code"); } Payload::DataCountSection { .. } => { Self::ensure_module(&states)?; // not part of the text format } Payload::DataSection(s) => { Self::ensure_module(&states)?; self.print_data(states.last_mut().unwrap(), s)?; self.update_custom_section_place(&mut states, "after data"); } #[cfg(feature = "component-model")] Payload::ModuleSection { parser: inner, unchecked_range: range, } => { Self::ensure_component(&states)?; expected = Some(Encoding::Module); parsers.push(parser); parser = inner; self.newline(range.start)?; } #[cfg(feature = "component-model")] Payload::InstanceSection(s) => { Self::ensure_component(&states)?; self.print_instances(states.last_mut().unwrap(), s)?; } #[cfg(feature = "component-model")] Payload::CoreTypeSection(s) => self.print_core_types(&mut states, s)?, #[cfg(feature = "component-model")] Payload::ComponentSection { parser: inner, unchecked_range: range, } => { Self::ensure_component(&states)?; expected = Some(Encoding::Component); parsers.push(parser); parser = inner; self.newline(range.start)?; } #[cfg(feature = "component-model")] Payload::ComponentInstanceSection(s) => { Self::ensure_component(&states)?; self.print_component_instances(states.last_mut().unwrap(), s)?; } #[cfg(feature = "component-model")] Payload::ComponentAliasSection(s) => { Self::ensure_component(&states)?; self.print_component_aliases(&mut states, s)?; } #[cfg(feature = "component-model")] Payload::ComponentTypeSection(s) => { Self::ensure_component(&states)?; self.print_component_types(&mut states, s)?; } #[cfg(feature = "component-model")] Payload::ComponentCanonicalSection(s) => { Self::ensure_component(&states)?; self.print_canonical_functions(states.last_mut().unwrap(), s)?; } #[cfg(feature = "component-model")] Payload::ComponentStartSection { start, range } => { Self::ensure_component(&states)?; self.print_component_start(states.last_mut().unwrap(), range.start, start)?; } #[cfg(feature = "component-model")] Payload::ComponentImportSection(s) => { Self::ensure_component(&states)?; self.print_component_imports(states.last_mut().unwrap(), s)?; } #[cfg(feature = "component-model")] Payload::ComponentExportSection(s) => { Self::ensure_component(&states)?; self.print_component_exports(states.last_mut().unwrap(), s)?; } Payload::End(offset) => { self.end_group()?; // close the `module` or `component` group #[cfg(feature = "component-model")] { let state = states.pop().unwrap(); if let Some(parent) = states.last_mut() { match state.encoding { Encoding::Module => { parent.core.modules += 1; } Encoding::Component => { parent.component.components += 1; } } parser = parsers.pop().unwrap(); continue; } } self.newline(offset)?; if self.config.print_offsets { self.result.newline()?; } break; } other => match other.as_section() { Some((id, _)) => bail!("found unknown section `{}`", id), None => bail!("found unknown payload"), }, } } Ok(()) } fn update_custom_section_place(&self, states: &mut Vec, place: &'static str) { if let Some(last) = states.last_mut() { if let Some((prev, prev_line)) = &mut last.custom_section_place { if *prev_line != self.line { *prev = place; *prev_line = self.line; } } } } fn update_custom_section_line(&self, states: &mut Vec) { if let Some(last) = states.last_mut() { if let Some((_, prev_line)) = &mut last.custom_section_place { *prev_line = self.line; } } } fn start_group(&mut self, name: &str) -> Result<()> { write!(self.result, "(")?; self.result.start_keyword()?; write!(self.result, "{name}")?; self.result.reset_color()?; self.nesting += 1; self.group_lines.push(self.line); Ok(()) } fn end_group(&mut self) -> Result<()> { self.nesting -= 1; if let Some(line) = self.group_lines.pop() { if line != self.line { self.newline_unknown_pos()?; } } self.result.write_str(")")?; Ok(()) } fn register_names(&mut self, state: &mut State, names: NameSectionReader<'_>) -> Result<()> { fn indirect_name_map( into: &mut NamingMap<(u32, u32), K>, names: IndirectNameMap<'_>, name: &str, ) -> Result<()> { for indirect in names { let indirect = indirect?; let mut used = match name { // labels can be shadowed, so maintaining the used names is not useful. "label" => None, "local" | "field" => Some(HashSet::new()), _ => unimplemented!("{name} is an unknown type of indirect names"), }; for naming in indirect.names { let naming = naming?; into.index_to_name.insert( (indirect.index, naming.index), Naming::new(naming.name, naming.index, name, used.as_mut()), ); } } Ok(()) } for section in names { match section? { Name::Module { name, .. } => { let name = Naming::new(name, 0, "module", None); state.name = Some(name); } Name::Function(n) => name_map(&mut state.core.func_names, n, "func")?, Name::Local(n) => indirect_name_map(&mut state.core.local_names, n, "local")?, Name::Label(n) => indirect_name_map(&mut state.core.label_names, n, "label")?, Name::Type(n) => name_map(&mut state.core.type_names, n, "type")?, Name::Table(n) => name_map(&mut state.core.table_names, n, "table")?, Name::Memory(n) => name_map(&mut state.core.memory_names, n, "memory")?, Name::Global(n) => name_map(&mut state.core.global_names, n, "global")?, Name::Element(n) => name_map(&mut state.core.element_names, n, "elem")?, Name::Data(n) => name_map(&mut state.core.data_names, n, "data")?, Name::Field(n) => indirect_name_map(&mut state.core.field_names, n, "field")?, Name::Tag(n) => name_map(&mut state.core.tag_names, n, "tag")?, Name::Unknown { .. } => (), } } Ok(()) } fn print_rec( &mut self, state: &mut State, offset: Option, rec: RecGroup, is_component: bool, ) -> Result<()> { if rec.is_explicit_rec_group() { if is_component { self.start_group("core rec")?; } else { self.start_group("rec")?; } for ty in rec.into_types() { match offset { Some(offset) => self.newline(offset + 2)?, None => self.newline_unknown_pos()?, } self.print_type(state, ty, false)?; } self.end_group()?; // `rec` } else { assert_eq!(rec.types().len(), 1); let ty = rec.into_types().next().unwrap(); self.print_type(state, ty, is_component)?; } Ok(()) } fn print_type(&mut self, state: &mut State, ty: SubType, is_component: bool) -> Result<()> { if is_component { self.start_group("core type ")?; } else { self.start_group("type ")?; } let ty_idx = state.core.types.len() as u32; self.print_name(&state.core.type_names, ty_idx)?; self.result.write_str(" ")?; self.print_sub(state, &ty, ty_idx)?; self.end_group()?; // `type` state.core.types.push(Some(ty)); Ok(()) } fn print_sub(&mut self, state: &State, ty: &SubType, ty_idx: u32) -> Result { let r = if !ty.is_final || !ty.supertype_idx.is_none() { self.start_group("sub")?; self.print_sub_type(state, ty)?; let r = self.print_composite(state, &ty.composite_type, ty_idx)?; self.end_group()?; // `sub` r } else { self.print_composite(state, &ty.composite_type, ty_idx)? }; Ok(r) } fn print_composite(&mut self, state: &State, ty: &CompositeType, ty_idx: u32) -> Result { if ty.shared { self.start_group("shared")?; self.result.write_str(" ")?; } let r = match &ty.inner { CompositeInnerType::Func(ty) => { self.start_group("func")?; let r = self.print_func_type(state, ty, None)?; self.end_group()?; // `func` r } CompositeInnerType::Array(ty) => { self.start_group("array")?; let r = self.print_array_type(state, ty)?; self.end_group()?; // `array` r } CompositeInnerType::Struct(ty) => { self.start_group("struct")?; let r = self.print_struct_type(state, ty, ty_idx)?; self.end_group()?; // `struct` r } CompositeInnerType::Cont(ty) => { self.start_group("cont")?; let r = self.print_cont_type(state, ty)?; self.end_group()?; // `cont` r } }; if ty.shared { self.end_group()?; // `shared` } Ok(r) } fn print_types(&mut self, state: &mut State, parser: TypeSectionReader<'_>) -> Result<()> { for ty in parser.into_iter_with_offsets() { let (offset, rec_group) = ty?; self.newline(offset)?; self.print_rec(state, Some(offset), rec_group, false)?; } Ok(()) } fn print_core_functype_idx( &mut self, state: &State, idx: u32, names_for: Option, ) -> Result> { self.print_core_type_ref(state, idx)?; match state.core.types.get(idx as usize) { Some(Some(SubType { composite_type: CompositeType { inner: CompositeInnerType::Func(ty), shared: false, }, .. })) => self.print_func_type(state, ty, names_for).map(Some), Some(Some(_)) | Some(None) | None => Ok(None), } } /// Returns the number of parameters, useful for local index calculations /// later. fn print_func_type( &mut self, state: &State, ty: &FuncType, names_for: Option, ) -> Result { if !ty.params().is_empty() { self.result.write_str(" ")?; } let mut params = NamedLocalPrinter::new("param"); // Note that named parameters must be alone in a `param` block, so // we need to be careful to terminate previous param blocks and open // a new one if that's the case with a named parameter. for (i, param) in ty.params().iter().enumerate() { params.start_local(names_for, i as u32, self, state)?; self.print_valtype(state, *param)?; params.end_local(self)?; } params.finish(self)?; if !ty.results().is_empty() { self.result.write_str(" ")?; self.start_group("result")?; for result in ty.results().iter() { self.result.write_str(" ")?; self.print_valtype(state, *result)?; } self.end_group()?; } Ok(ty.params().len() as u32) } fn print_field_type( &mut self, state: &State, ty: &FieldType, ty_field_idx: Option<(u32, u32)>, ) -> Result { self.result.write_str(" ")?; if let Some(idxs @ (_, field_idx)) = ty_field_idx { match state.core.field_names.index_to_name.get(&idxs) { Some(name) => { name.write_identifier(self)?; self.result.write_str(" ")?; } None if self.config.name_unnamed => write!(self.result, "$#field{field_idx} ")?, None => {} } } if ty.mutable { self.result.write_str("(mut ")?; } self.print_storage_type(state, ty.element_type)?; if ty.mutable { self.result.write_str(")")?; } Ok(0) } fn print_array_type(&mut self, state: &State, ty: &ArrayType) -> Result { self.print_field_type(state, &ty.0, None) } fn print_struct_type(&mut self, state: &State, ty: &StructType, ty_idx: u32) -> Result { for (field_index, field) in ty.fields.iter().enumerate() { self.result.write_str(" (field")?; self.print_field_type(state, field, Some((ty_idx, field_index as u32)))?; self.result.write_str(")")?; } Ok(0) } fn print_cont_type(&mut self, state: &State, ct: &ContType) -> Result { self.result.write_str(" ")?; self.print_idx(&state.core.type_names, ct.0.as_module_index().unwrap())?; Ok(0) } fn print_sub_type(&mut self, state: &State, ty: &SubType) -> Result { self.result.write_str(" ")?; if ty.is_final { self.result.write_str("final ")?; } if let Some(idx) = ty.supertype_idx { self.print_idx(&state.core.type_names, idx.as_module_index().unwrap())?; self.result.write_str(" ")?; } Ok(0) } fn print_storage_type(&mut self, state: &State, ty: StorageType) -> Result<()> { match ty { StorageType::I8 => self.result.write_str("i8")?, StorageType::I16 => self.result.write_str("i16")?, StorageType::Val(val_type) => self.print_valtype(state, val_type)?, } Ok(()) } fn print_valtype(&mut self, state: &State, ty: ValType) -> Result<()> { match ty { ValType::I32 => self.print_type_keyword("i32")?, ValType::I64 => self.print_type_keyword("i64")?, ValType::F32 => self.print_type_keyword("f32")?, ValType::F64 => self.print_type_keyword("f64")?, ValType::V128 => self.print_type_keyword("v128")?, ValType::Ref(rt) => self.print_reftype(state, rt)?, } Ok(()) } fn print_valtypes(&mut self, state: &State, tys: Vec) -> Result<()> { for ty in tys { self.result.write_str(" ")?; self.print_valtype(state, ty)?; } Ok(()) } fn print_reftype(&mut self, state: &State, ty: RefType) -> Result<()> { if ty.is_nullable() { match ty.as_non_null() { RefType::FUNC => self.print_type_keyword("funcref")?, RefType::EXTERN => self.print_type_keyword("externref")?, RefType::I31 => self.print_type_keyword("i31ref")?, RefType::ANY => self.print_type_keyword("anyref")?, RefType::NONE => self.print_type_keyword("nullref")?, RefType::NOEXTERN => self.print_type_keyword("nullexternref")?, RefType::NOFUNC => self.print_type_keyword("nullfuncref")?, RefType::EQ => self.print_type_keyword("eqref")?, RefType::STRUCT => self.print_type_keyword("structref")?, RefType::ARRAY => self.print_type_keyword("arrayref")?, RefType::EXN => self.print_type_keyword("exnref")?, RefType::NOEXN => self.print_type_keyword("nullexnref")?, _ => { self.start_group("ref")?; self.result.write_str(" null ")?; self.print_heaptype(state, ty.heap_type())?; self.end_group()?; } } } else { self.start_group("ref ")?; self.print_heaptype(state, ty.heap_type())?; self.end_group()?; } Ok(()) } fn print_heaptype(&mut self, state: &State, ty: HeapType) -> Result<()> { match ty { HeapType::Concrete(i) => { self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?; } HeapType::Abstract { shared, ty } => { use AbstractHeapType::*; if shared { self.start_group("shared ")?; } match ty { Func => self.print_type_keyword("func")?, Extern => self.print_type_keyword("extern")?, Any => self.print_type_keyword("any")?, None => self.print_type_keyword("none")?, NoExtern => self.print_type_keyword("noextern")?, NoFunc => self.print_type_keyword("nofunc")?, Eq => self.print_type_keyword("eq")?, Struct => self.print_type_keyword("struct")?, Array => self.print_type_keyword("array")?, I31 => self.print_type_keyword("i31")?, Exn => self.print_type_keyword("exn")?, NoExn => self.print_type_keyword("noexn")?, Cont => self.print_type_keyword("cont")?, NoCont => self.print_type_keyword("nocont")?, } if shared { self.end_group()?; } } } Ok(()) } fn print_type_keyword(&mut self, keyword: &str) -> Result<()> { self.result.start_type()?; self.result.write_str(keyword)?; self.result.reset_color()?; Ok(()) } fn print_imports(&mut self, state: &mut State, parser: ImportSectionReader<'_>) -> Result<()> { for import in parser.into_iter_with_offsets() { let (offset, import) = import?; self.newline(offset)?; self.print_import(state, &import, true)?; match import.ty { TypeRef::Func(idx) => { debug_assert!(state.core.func_to_type.len() == state.core.funcs as usize); state.core.funcs += 1; state.core.func_to_type.push(Some(idx)) } TypeRef::Table(_) => state.core.tables += 1, TypeRef::Memory(_) => state.core.memories += 1, TypeRef::Tag(TagType { kind: _, func_type_idx: idx, }) => { debug_assert!(state.core.tag_to_type.len() == state.core.tags as usize); state.core.tags += 1; state.core.tag_to_type.push(Some(idx)) } TypeRef::Global(_) => state.core.globals += 1, } } Ok(()) } fn print_import(&mut self, state: &State, import: &Import<'_>, index: bool) -> Result<()> { self.start_group("import ")?; self.print_str(import.module)?; self.result.write_str(" ")?; self.print_str(import.name)?; self.result.write_str(" ")?; self.print_import_ty(state, &import.ty, index)?; self.end_group()?; Ok(()) } fn print_import_ty(&mut self, state: &State, ty: &TypeRef, index: bool) -> Result<()> { match ty { TypeRef::Func(f) => { self.start_group("func ")?; if index { self.print_name(&state.core.func_names, state.core.funcs)?; self.result.write_str(" ")?; } self.print_core_type_ref(state, *f)?; } TypeRef::Table(f) => self.print_table_type(state, f, index)?, TypeRef::Memory(f) => self.print_memory_type(state, f, index)?, TypeRef::Tag(f) => self.print_tag_type(state, f, index)?, TypeRef::Global(f) => self.print_global_type(state, f, index)?, } self.end_group()?; Ok(()) } fn print_table_type(&mut self, state: &State, ty: &TableType, index: bool) -> Result<()> { self.start_group("table ")?; if index { self.print_name(&state.core.table_names, state.core.tables)?; self.result.write_str(" ")?; } if ty.shared { self.print_type_keyword("shared ")?; } if ty.table64 { self.print_type_keyword("i64 ")?; } self.print_limits(ty.initial, ty.maximum)?; self.result.write_str(" ")?; self.print_reftype(state, ty.element_type)?; Ok(()) } fn print_memory_type(&mut self, state: &State, ty: &MemoryType, index: bool) -> Result<()> { self.start_group("memory ")?; if index { self.print_name(&state.core.memory_names, state.core.memories)?; self.result.write_str(" ")?; } if ty.memory64 { self.print_type_keyword("i64 ")?; } self.print_limits(ty.initial, ty.maximum)?; if ty.shared { self.print_type_keyword(" shared")?; } if let Some(p) = ty.page_size_log2 { let p = 1_u64 .checked_shl(p) .ok_or_else(|| anyhow!("left shift overflow").context("invalid page size"))?; self.result.write_str(" ")?; self.start_group("pagesize ")?; write!(self.result, "{p:#x}")?; self.end_group()?; } Ok(()) } fn print_tag_type(&mut self, state: &State, ty: &TagType, index: bool) -> Result<()> { self.start_group("tag ")?; if index { self.print_name(&state.core.tag_names, state.core.tags)?; self.result.write_str(" ")?; } self.print_core_functype_idx(state, ty.func_type_idx, None)?; Ok(()) } fn print_limits(&mut self, initial: T, maximum: Option) -> Result<()> where T: fmt::Display, { self.result.start_literal()?; write!(self.result, "{initial}")?; if let Some(max) = maximum { write!(self.result, " {max}")?; } self.result.reset_color()?; Ok(()) } fn print_global_type(&mut self, state: &State, ty: &GlobalType, index: bool) -> Result<()> { self.start_group("global ")?; if index { self.print_name(&state.core.global_names, state.core.globals)?; self.result.write_str(" ")?; } if ty.shared || ty.mutable { self.result.write_str("(")?; if ty.shared { self.print_type_keyword("shared ")?; } if ty.mutable { self.print_type_keyword("mut ")?; } self.print_valtype(state, ty.content_type)?; self.result.write_str(")")?; } else { self.print_valtype(state, ty.content_type)?; } Ok(()) } fn print_tables(&mut self, state: &mut State, parser: TableSectionReader<'_>) -> Result<()> { for table in parser.into_iter_with_offsets() { let (offset, table) = table?; self.newline(offset)?; self.print_table_type(state, &table.ty, true)?; match &table.init { TableInit::RefNull => {} TableInit::Expr(expr) => { self.result.write_str(" ")?; self.print_const_expr(state, expr, self.config.fold_instructions)?; } } self.end_group()?; state.core.tables += 1; } Ok(()) } fn print_memories(&mut self, state: &mut State, parser: MemorySectionReader<'_>) -> Result<()> { for memory in parser.into_iter_with_offsets() { let (offset, memory) = memory?; self.newline(offset)?; self.print_memory_type(state, &memory, true)?; self.end_group()?; state.core.memories += 1; } Ok(()) } fn print_tags(&mut self, state: &mut State, parser: TagSectionReader<'_>) -> Result<()> { for tag in parser.into_iter_with_offsets() { let (offset, tag) = tag?; self.newline(offset)?; self.print_tag_type(state, &tag, true)?; self.end_group()?; debug_assert!(state.core.tag_to_type.len() == state.core.tags as usize); state.core.tags += 1; state.core.tag_to_type.push(Some(tag.func_type_idx)); } Ok(()) } fn print_globals(&mut self, state: &mut State, parser: GlobalSectionReader<'_>) -> Result<()> { for global in parser.into_iter_with_offsets() { let (offset, global) = global?; self.newline(offset)?; self.print_global_type(state, &global.ty, true)?; self.result.write_str(" ")?; self.print_const_expr(state, &global.init_expr, self.config.fold_instructions)?; self.end_group()?; state.core.globals += 1; } Ok(()) } fn print_code_section_entry( &mut self, state: &mut State, body: &FunctionBody<'_>, validator: Option, ) -> Result<()> { self.newline(body.get_binary_reader().original_position())?; self.start_group("func ")?; let func_idx = state.core.funcs; self.print_name(&state.core.func_names, func_idx)?; self.result.write_str(" ")?; let ty = match state.core.func_to_type.get(func_idx as usize) { Some(Some(x)) => *x, _ => panic!("invalid function type"), }; let params = self .print_core_functype_idx(state, ty, Some(func_idx))? .unwrap_or(0); // Hints are stored on `self` in reverse order of function index so // check the last one and see if it matches this function. let hints = match self.code_section_hints.last() { Some((f, _)) if *f == func_idx => { let (_, hints) = self.code_section_hints.pop().unwrap(); hints } _ => Vec::new(), }; if self.config.print_skeleton { self.result.write_str(" ...")?; } else { self.print_func_body(state, func_idx, params, &body, &hints, validator)?; } self.end_group()?; state.core.funcs += 1; Ok(()) } fn print_func_body( &mut self, state: &mut State, func_idx: u32, params: u32, body: &FunctionBody<'_>, branch_hints: &[(usize, BranchHint)], mut validator: Option, ) -> Result<()> { let mut first = true; let mut local_idx = 0; let mut locals = NamedLocalPrinter::new("local"); let mut reader = body.get_binary_reader(); let func_start = reader.original_position(); for _ in 0..reader.read_var_u32()? { let offset = reader.original_position(); let cnt = reader.read_var_u32()?; let ty = reader.read()?; if MAX_LOCALS .checked_sub(local_idx) .and_then(|s| s.checked_sub(cnt)) .is_none() { bail!("function exceeds the maximum number of locals that can be printed"); } for _ in 0..cnt { if first { self.newline(offset)?; first = false; } locals.start_local(Some(func_idx), params + local_idx, self, state)?; self.print_valtype(state, ty)?; locals.end_local(self)?; local_idx += 1; } } locals.finish(self)?; if let Some(f) = &mut validator { if let Err(e) = f.read_locals(body.get_binary_reader()) { validator = None; self.newline_unknown_pos()?; write!(self.result, ";; locals are invalid: {e}")?; } } let nesting_start = self.nesting; let fold_instructions = self.config.fold_instructions; let mut operator_state = OperatorState::new(self, OperatorSeparator::Newline); if fold_instructions { let mut folded_printer = PrintOperatorFolded::new(self, state, &mut operator_state); folded_printer.set_offset(func_start); folded_printer.begin_function(func_idx)?; Self::print_operators( &mut reader, branch_hints, func_start, &mut folded_printer, validator, )?; } else { let mut flat_printer = PrintOperator::new(self, state, &mut operator_state); Self::print_operators( &mut reader, branch_hints, func_start, &mut flat_printer, validator, )?; } // If this was an invalid function body then the nesting may not // have reset back to normal. Fix that up here and forcibly insert // a newline as well in case the last instruction was something // like an `if` which has a comment after it which could interfere // with the closing paren printed for the func. if self.nesting != nesting_start { self.nesting = nesting_start; self.newline(reader.original_position())?; } Ok(()) } fn print_operators<'a, O: OpPrinter>( body: &mut BinaryReader<'a>, mut branch_hints: &[(usize, BranchHint)], func_start: usize, op_printer: &mut O, mut validator: Option, ) -> Result<()> { let mut ops = OperatorsReader::new(body.clone()); while !ops.eof() { if ops.is_end_then_eof() { let mut annotation = None; if let Some(f) = &mut validator { match f.visit_operator(&ops, true) { Ok(()) => {} Err(_) => { annotation = Some(String::from("type mismatch at end of expression")) } } } ops.read()?; // final "end" opcode terminates instruction sequence ops.finish()?; op_printer.finalize(annotation.as_deref())?; return Ok(()); } // Branch hints are stored in increasing order of their body offset // so print them whenever their instruction comes up. if let Some(((hint_offset, hint), rest)) = branch_hints.split_first() { if hint.func_offset == (ops.original_position() - func_start) as u32 { branch_hints = rest; op_printer.branch_hint(*hint_offset, hint.taken)?; } } let mut annotation = None; if let Some(f) = &mut validator { let result = f .visit_operator(&ops, false) .map_err(anyhow::Error::from) .and_then(|()| f.visualize_operand_stack(op_printer.use_color())); match result { Ok(s) => annotation = Some(s), Err(_) => { validator = None; annotation = Some(String::from("(invalid)")); } } } op_printer.set_offset(ops.original_position()); op_printer.visit_operator(&mut ops, annotation.as_deref())?; } ops.finish()?; // for the error message bail!("unexpected end of operators"); } fn newline(&mut self, offset: usize) -> Result<()> { self.print_newline(Some(offset)) } fn newline_unknown_pos(&mut self) -> Result<()> { self.print_newline(None) } fn print_newline(&mut self, offset: Option) -> Result<()> { self.result.newline()?; self.result.start_line(offset); if self.config.print_offsets { match offset { Some(offset) => { self.result.start_comment()?; write!(self.result, "(;@{offset:<6x};)")?; self.result.reset_color()?; } None => self.result.write_str(" ")?, } } self.line += 1; // Clamp the maximum nesting size that we print at something somewhat // reasonable to avoid generating hundreds of megabytes of whitespace // for small-ish modules that have deep-ish nesting. for _ in 0..self.nesting.min(MAX_NESTING_TO_PRINT) { self.result.write_str(&self.config.indent_text)?; } Ok(()) } fn print_exports(&mut self, state: &State, data: ExportSectionReader) -> Result<()> { for export in data.into_iter_with_offsets() { let (offset, export) = export?; self.newline(offset)?; self.print_export(state, &export)?; } Ok(()) } fn print_export(&mut self, state: &State, export: &Export) -> Result<()> { self.start_group("export ")?; self.print_str(export.name)?; self.result.write_str(" ")?; self.print_external_kind(state, export.kind, export.index)?; self.end_group()?; // export Ok(()) } fn print_external_kind(&mut self, state: &State, kind: ExternalKind, index: u32) -> Result<()> { match kind { ExternalKind::Func => { self.start_group("func ")?; self.print_idx(&state.core.func_names, index)?; } ExternalKind::Table => { self.start_group("table ")?; self.print_idx(&state.core.table_names, index)?; } ExternalKind::Global => { self.start_group("global ")?; self.print_idx(&state.core.global_names, index)?; } ExternalKind::Memory => { self.start_group("memory ")?; self.print_idx(&state.core.memory_names, index)?; } ExternalKind::Tag => { self.start_group("tag ")?; write!(self.result, "{index}")?; } } self.end_group()?; Ok(()) } fn print_core_type_ref(&mut self, state: &State, idx: u32) -> Result<()> { self.start_group("type ")?; self.print_idx(&state.core.type_names, idx)?; self.end_group()?; Ok(()) } // Note: in the text format, modules can use identifiers that are defined anywhere, but // components can only use previously-defined identifiers. In the binary format, // invalid components can make forward references to an index that appears in the name section; // these can be printed but the output won't parse. fn print_idx(&mut self, names: &NamingMap, idx: u32) -> Result<()> where K: NamingNamespace, { self._print_idx(&names.index_to_name, idx, K::desc()) } fn _print_idx(&mut self, names: &HashMap, idx: u32, desc: &str) -> Result<()> { self.result.start_name()?; match names.get(&idx) { Some(name) => name.write_identifier(self)?, None if self.config.name_unnamed => write!(self.result, "$#{desc}{idx}")?, None => write!(self.result, "{idx}")?, } self.result.reset_color()?; Ok(()) } fn print_local_idx(&mut self, state: &State, func: u32, idx: u32) -> Result<()> { self.result.start_name()?; match state.core.local_names.index_to_name.get(&(func, idx)) { Some(name) => name.write_identifier(self)?, None if self.config.name_unnamed => write!(self.result, "$#local{idx}")?, None => write!(self.result, "{idx}")?, } self.result.reset_color()?; Ok(()) } fn print_field_idx(&mut self, state: &State, ty: u32, idx: u32) -> Result<()> { self.result.start_name()?; match state.core.field_names.index_to_name.get(&(ty, idx)) { Some(name) => name.write_identifier(self)?, None if self.config.name_unnamed => write!(self.result, "$#field{idx}")?, None => write!(self.result, "{idx}")?, } self.result.reset_color()?; Ok(()) } fn print_name(&mut self, names: &NamingMap, cur_idx: u32) -> Result<()> where K: NamingNamespace, { self._print_name(&names.index_to_name, cur_idx, K::desc()) } fn _print_name( &mut self, names: &HashMap, cur_idx: u32, desc: &str, ) -> Result<()> { self.result.start_name()?; match names.get(&cur_idx) { Some(name) => { name.write(self)?; self.result.write_str(" ")?; } None if self.config.name_unnamed => { write!(self.result, "$#{desc}{cur_idx} ")?; } None => {} } write!(self.result, "(;{cur_idx};)")?; self.result.reset_color()?; Ok(()) } fn print_elems(&mut self, state: &mut State, data: ElementSectionReader) -> Result<()> { for (i, elem) in data.into_iter_with_offsets().enumerate() { let (offset, mut elem) = elem?; self.newline(offset)?; self.start_group("elem ")?; self.print_name(&state.core.element_names, i as u32)?; match &mut elem.kind { ElementKind::Passive => {} ElementKind::Declared => self.result.write_str(" declare")?, ElementKind::Active { table_index, offset_expr, } => { if let Some(table_index) = *table_index { self.result.write_str(" ")?; self.start_group("table ")?; self.print_idx(&state.core.table_names, table_index)?; self.end_group()?; } self.result.write_str(" ")?; self.print_const_expr_sugar(state, offset_expr, "offset")?; } } self.result.write_str(" ")?; if self.config.print_skeleton { self.result.write_str("...")?; } else { match elem.items { ElementItems::Functions(reader) => { self.result.write_str("func")?; for idx in reader { self.result.write_str(" ")?; self.print_idx(&state.core.func_names, idx?)? } } ElementItems::Expressions(ty, reader) => { self.print_reftype(state, ty)?; for expr in reader { self.result.write_str(" ")?; self.print_const_expr_sugar(state, &expr?, "item")? } } } } self.end_group()?; } Ok(()) } fn print_data(&mut self, state: &mut State, data: DataSectionReader) -> Result<()> { for (i, data) in data.into_iter_with_offsets().enumerate() { let (offset, data) = data?; self.newline(offset)?; self.start_group("data ")?; self.print_name(&state.core.data_names, i as u32)?; self.result.write_str(" ")?; match &data.kind { DataKind::Passive => {} DataKind::Active { memory_index, offset_expr, } => { if *memory_index != 0 { self.start_group("memory ")?; self.print_idx(&state.core.memory_names, *memory_index)?; self.end_group()?; self.result.write_str(" ")?; } self.print_const_expr_sugar(state, offset_expr, "offset")?; self.result.write_str(" ")?; } } if self.config.print_skeleton { self.result.write_str("...")?; } else { self.print_bytes(data.data)?; } self.end_group()?; } Ok(()) } /// Prints the operators of `expr` space-separated, taking into account that /// if there's only one operator in `expr` then instead of `(explicit ...)` /// the printing can be `(...)`. fn print_const_expr_sugar( &mut self, state: &mut State, expr: &ConstExpr, explicit: &str, ) -> Result<()> { self.start_group("")?; let mut reader = expr.get_operators_reader(); if reader.read().is_ok() && !reader.is_end_then_eof() { write!(self.result, "{explicit} ")?; self.print_const_expr(state, expr, self.config.fold_instructions)?; } else { self.print_const_expr(state, expr, false)?; } self.end_group()?; Ok(()) } /// Prints the operators of `expr` space-separated. fn print_const_expr(&mut self, state: &mut State, expr: &ConstExpr, fold: bool) -> Result<()> { let mut reader = expr.get_binary_reader(); let mut operator_state = OperatorState::new(self, OperatorSeparator::NoneThenSpace); if fold { let mut folded_printer = PrintOperatorFolded::new(self, state, &mut operator_state); folded_printer.begin_const_expr(); Self::print_operators(&mut reader, &[], 0, &mut folded_printer, None)?; } else { let mut op_printer = PrintOperator::new(self, state, &mut operator_state); Self::print_operators(&mut reader, &[], 0, &mut op_printer, None)?; } Ok(()) } fn print_str(&mut self, name: &str) -> Result<()> { self.result.start_literal()?; self.result.write_str("\"")?; self.print_str_contents(name)?; self.result.write_str("\"")?; self.result.reset_color()?; Ok(()) } fn print_str_contents(&mut self, name: &str) -> Result<()> { for c in name.chars() { let v = c as u32; if (0x20..0x7f).contains(&v) && c != '"' && c != '\\' && v < 0xff { write!(self.result, "{c}")?; } else { write!(self.result, "\\u{{{v:x}}}",)?; } } Ok(()) } fn print_bytes(&mut self, bytes: &[u8]) -> Result<()> { self.result.start_literal()?; self.result.write_str("\"")?; for byte in bytes { if *byte >= 0x20 && *byte < 0x7f && *byte != b'"' && *byte != b'\\' { write!(self.result, "{}", *byte as char)?; } else { self.hex_byte(*byte)?; } } self.result.write_str("\"")?; self.result.reset_color()?; Ok(()) } fn hex_byte(&mut self, byte: u8) -> Result<()> { write!(self.result, "\\{byte:02x}")?; Ok(()) } fn print_known_custom_section(&mut self, section: CustomSectionReader<'_>) -> Result { match section.as_known() { // For now `wasmprinter` has invented syntax for `producers` and // `dylink.0` below to use in tests. Note that this syntax is not // official at this time. KnownCustom::Producers(s) => { self.newline(section.range().start)?; self.print_producers_section(s)?; Ok(true) } KnownCustom::Dylink0(s) => { self.newline(section.range().start)?; self.print_dylink0_section(s)?; Ok(true) } // These are parsed during `read_names` and are part of // printing elsewhere, so don't print them. KnownCustom::Name(_) | KnownCustom::BranchHints(_) => Ok(true), #[cfg(feature = "component-model")] KnownCustom::ComponentName(_) => Ok(true), _ => Ok(false), } } fn print_raw_custom_section( &mut self, state: &State, section: CustomSectionReader<'_>, ) -> Result<()> { self.newline(section.range().start)?; self.start_group("@custom ")?; self.print_str(section.name())?; if let Some((place, _)) = state.custom_section_place { write!(self.result, " ({place})")?; } self.result.write_str(" ")?; if self.config.print_skeleton { self.result.write_str("...")?; } else { self.print_bytes(section.data())?; } self.end_group()?; Ok(()) } fn print_producers_section(&mut self, section: ProducersSectionReader<'_>) -> Result<()> { self.start_group("@producers")?; for field in section { let field = field?; for value in field.values.into_iter_with_offsets() { let (offset, value) = value?; self.newline(offset)?; self.start_group(field.name)?; self.result.write_str(" ")?; self.print_str(value.name)?; self.result.write_str(" ")?; self.print_str(value.version)?; self.end_group()?; } } self.end_group()?; Ok(()) } fn print_dylink0_section(&mut self, mut section: Dylink0SectionReader<'_>) -> Result<()> { self.start_group("@dylink.0")?; loop { let start = section.original_position(); let next = match section.next() { Some(Ok(next)) => next, Some(Err(e)) => return Err(e.into()), None => break, }; match next { Dylink0Subsection::MemInfo(info) => { self.newline(start)?; self.start_group("mem-info")?; if info.memory_size > 0 || info.memory_alignment > 0 { write!( self.result, " (memory {} {})", info.memory_size, info.memory_alignment )?; } if info.table_size > 0 || info.table_alignment > 0 { write!( self.result, " (table {} {})", info.table_size, info.table_alignment )?; } self.end_group()?; } Dylink0Subsection::Needed(needed) => { self.newline(start)?; self.start_group("needed")?; for s in needed { self.result.write_str(" ")?; self.print_str(s)?; } self.end_group()?; } Dylink0Subsection::ExportInfo(info) => { for info in info { self.newline(start)?; self.start_group("export-info ")?; self.print_str(info.name)?; self.print_dylink0_flags(info.flags)?; self.end_group()?; } } Dylink0Subsection::ImportInfo(info) => { for info in info { self.newline(start)?; self.start_group("import-info ")?; self.print_str(info.module)?; self.result.write_str(" ")?; self.print_str(info.field)?; self.print_dylink0_flags(info.flags)?; self.end_group()?; } } Dylink0Subsection::RuntimePath(runtime_path) => { self.newline(start)?; self.start_group("runtime-path")?; for s in runtime_path { self.result.write_str(" ")?; self.print_str(s)?; } self.end_group()?; } Dylink0Subsection::Unknown { ty, .. } => { bail!("don't know how to print dylink.0 subsection id {ty}"); } } } self.end_group()?; Ok(()) } fn print_dylink0_flags(&mut self, mut flags: SymbolFlags) -> Result<()> { macro_rules! print_flag { ($($name:ident = $text:tt)*) => ({$( if flags.contains(SymbolFlags::$name) { flags.remove(SymbolFlags::$name); write!(self.result, concat!(" ", $text))?; } )*}) } // N.B.: Keep in sync with `parse_sym_flags` in `crates/wast/src/core/custom.rs`. print_flag! { BINDING_WEAK = "binding-weak" BINDING_LOCAL = "binding-local" VISIBILITY_HIDDEN = "visibility-hidden" UNDEFINED = "undefined" EXPORTED = "exported" EXPLICIT_NAME = "explicit-name" NO_STRIP = "no-strip" TLS = "tls" ABSOLUTE = "absolute" } if !flags.is_empty() { write!(self.result, " {flags:#x}")?; } Ok(()) } fn register_branch_hint_section(&mut self, section: BranchHintSectionReader<'_>) -> Result<()> { self.code_section_hints.clear(); for func in section { let func = func?; if self.code_section_hints.len() >= MAX_WASM_FUNCTIONS as usize { bail!("found too many hints"); } if func.hints.count() >= MAX_WASM_FUNCTION_SIZE { bail!("found too many hints"); } let hints = func .hints .into_iter_with_offsets() .collect::>>()?; self.code_section_hints.push((func.func, hints)); } self.code_section_hints.reverse(); Ok(()) } } struct NamedLocalPrinter { group_name: &'static str, in_group: bool, end_group_after_local: bool, first: bool, } impl NamedLocalPrinter { fn new(group_name: &'static str) -> NamedLocalPrinter { NamedLocalPrinter { group_name, in_group: false, end_group_after_local: false, first: true, } } fn start_local( &mut self, func: Option, local: u32, dst: &mut Printer, state: &State, ) -> Result<()> { let name = state .core .local_names .index_to_name .get(&(func.unwrap_or(u32::MAX), local)); // Named locals must be in their own group, so if we have a name we need // to terminate the previous group. if name.is_some() && self.in_group { dst.end_group()?; self.in_group = false; } if self.first { self.first = false; } else { dst.result.write_str(" ")?; } // Next we either need a separator if we're already in a group or we // need to open a group for our new local. if !self.in_group { dst.start_group(self.group_name)?; dst.result.write_str(" ")?; self.in_group = true; } // Print the optional name if given... match name { Some(name) => { name.write(dst)?; dst.result.write_str(" ")?; self.end_group_after_local = true; } None if dst.config.name_unnamed && func.is_some() => { write!(dst.result, "$#local{local} ")?; self.end_group_after_local = true; } None => { self.end_group_after_local = false; } } Ok(()) } fn end_local(&mut self, dst: &mut Printer) -> Result<()> { if self.end_group_after_local { dst.end_group()?; self.end_group_after_local = false; self.in_group = false; } Ok(()) } fn finish(self, dst: &mut Printer) -> Result<()> { if self.in_group { dst.end_group()?; } Ok(()) } } macro_rules! print_float { ($name:ident $float:ident $uint:ident $sint:ident $exp_bits:tt) => { fn $name(&mut self, mut bits: $uint) -> Result<()> { // Calculate a few constants let int_width = mem::size_of::<$uint>() * 8; let exp_width = $exp_bits; let mantissa_width = int_width - 1 - exp_width; let bias = (1 << (exp_width - 1)) - 1; let max_exp = (1 as $sint) << (exp_width - 1); let min_exp = -max_exp + 1; // Handle `NaN` and infinity specially let f = $float::from_bits(bits); if bits >> (int_width - 1) != 0 { bits ^= 1 << (int_width - 1); self.result.write_str("-")?; } if f.is_infinite() { self.result.start_literal()?; self.result.write_str("inf ")?; self.result.start_comment()?; write!(self.result, "(;={f};)")?; self.result.reset_color()?; return Ok(()); } if f.is_nan() { let payload = bits & ((1 << mantissa_width) - 1); self.result.start_literal()?; if payload == 1 << (mantissa_width - 1) { self.result.write_str("nan ")?; self.result.start_comment()?; write!(self.result, "(;={f};)")?; } else { write!(self.result, "nan:{:#x} ", payload)?; self.result.start_comment()?; write!(self.result, "(;={f};)")?; } self.result.reset_color()?; return Ok(()); } // Figure out our exponent, but keep in mine that it's in an // integer width that may not be supported. As a result we do a few // tricks here: // // * Make the MSB the top bit of the exponent, then shift the // exponent to the bottom. This means we now have a signed // integer in `$sint` width representing the whole exponent. // * Do the arithmetic for the exponent (subtract) // * Next we only care about the lowest `$exp_bits` bits of the // result, but we do care about the sign. Use sign-carrying of // the signed integer shifts to shift it left then shift it back. // // Overall this should do basic arithmetic for `$exp_bits` bit // numbers and get the result back as a signed integer with `$sint` // bits in `exponent` representing the same decimal value. let mut exponent = (((bits << 1) as $sint) >> (mantissa_width + 1)).wrapping_sub(bias); exponent = (exponent << (int_width - exp_width)) >> (int_width - exp_width); let mut fraction = bits & ((1 << mantissa_width) - 1); self.result.start_literal()?; self.result.write_str("0x")?; if bits == 0 { self.result.write_str("0p+0")?; } else { self.result.write_str("1")?; if fraction > 0 { fraction <<= (int_width - mantissa_width); // Apparently the subnormal is handled here. I don't know // what a subnormal is. If someone else does, please let me // know! if exponent == min_exp { let leading = fraction.leading_zeros(); if (leading as usize) < int_width - 1 { fraction <<= leading + 1; } else { fraction = 0; } exponent -= leading as $sint; } self.result.write_str(".")?; while fraction > 0 { write!(self.result, "{:x}", fraction >> (int_width - 4))?; fraction <<= 4; } } write!(self.result, "p{:+}", exponent)?; } self.result.start_comment()?; write!(self.result, " (;={};)", f)?; self.result.reset_color()?; Ok(()) } }; } impl Printer<'_, '_> { print_float!(print_f32 f32 u32 i32 8); print_float!(print_f64 f64 u64 i64 11); } impl Naming { fn new<'a>( name: &'a str, index: u32, group: &str, used: Option<&mut HashSet<&'a str>>, ) -> Naming { let mut kind = NamingKind::DollarName; if name.chars().any(|c| !is_idchar(c)) { kind = NamingKind::DollarQuotedName; } // If the `name` provided can't be used as the raw identifier for the // item that it's describing then a synthetic name must be made. The // rules here which generate a name are: // // * Empty identifiers are not allowed // * Identifiers have a fixed set of valid characters // * For wasmprinter's purposes we "reserve" identifiers with the `#` // prefix, which is in theory rare to encounter in practice. // * If the name has already been used for some other item and cannot // be reused (e.g. because shadowing in this context is not possible). // // If any of these conditions match then we generate a unique identifier // based on `name` but not it exactly. By factoring in the `group`, // `index`, and `name` we get a guaranteed unique identifier (due to the // leading `#` prefix that we reserve and factoring in of the item // index) while preserving human readability at least somewhat (the // valid identifier characters of `name` still appear in the returned // name). if name.is_empty() || name.starts_with('#') || used.map(|set| !set.insert(name)).unwrap_or(false) { kind = NamingKind::SyntheticPrefix(format!("#{group}{index}")); } return Naming { kind, name: name.to_string(), }; // See https://webassembly.github.io/spec/core/text/values.html#text-id fn is_idchar(c: char) -> bool { matches!( c, '0'..='9' | 'a'..='z' | 'A'..='Z' | '!' | '#' | '$' | '%' | '&' | '\'' | '*' | '+' | '-' | '.' | '/' | ':' | '<' | '=' | '>' | '?' | '@' | '\\' | '^' | '_' | '`' | '|' | '~' ) } } fn write_identifier(&self, printer: &mut Printer<'_, '_>) -> Result<()> { match &self.kind { NamingKind::DollarName => { printer.result.write_str("$")?; printer.result.write_str(&self.name)?; } NamingKind::DollarQuotedName => { printer.result.write_str("$\"")?; printer.print_str_contents(&self.name)?; printer.result.write_str("\"")?; } NamingKind::SyntheticPrefix(prefix) => { printer.result.write_str("$\"")?; printer.result.write_str(&prefix)?; printer.result.write_str(" ")?; printer.print_str_contents(&self.name)?; printer.result.write_str("\"")?; } } Ok(()) } fn write(&self, dst: &mut Printer<'_, '_>) -> Result<()> { self.write_identifier(dst)?; match &self.kind { NamingKind::DollarName | NamingKind::DollarQuotedName => {} NamingKind::SyntheticPrefix(_) => { dst.result.write_str(" ")?; dst.start_group("@name \"")?; dst.print_str_contents(&self.name)?; dst.result.write_str("\"")?; dst.end_group()?; } } Ok(()) } } /// Helper trait for the `NamingMap` type's `K` type parameter. trait NamingNamespace { fn desc() -> &'static str; } macro_rules! naming_namespaces { ($(struct $name:ident => $desc:tt)*) => ($( struct $name; impl NamingNamespace for $name { fn desc() -> &'static str { $desc } } )*) } naming_namespaces! { struct NameFunc => "func" struct NameGlobal => "global" struct NameMemory => "memory" struct NameLocal => "local" struct NameLabel => "label" struct NameTable => "table" struct NameType => "type" struct NameField => "field" struct NameData => "data" struct NameElem => "elem" struct NameTag => "tag" } #[cfg(feature = "component-model")] naming_namespaces! { struct NameModule => "module" struct NameInstance => "instance" struct NameValue => "value" struct NameComponent => "component" } fn name_map(into: &mut NamingMap, names: NameMap<'_>, name: &str) -> Result<()> { let mut used = HashSet::new(); for naming in names { let naming = naming?; into.index_to_name.insert( naming.index, Naming::new(naming.name, naming.index, name, Some(&mut used)), ); } Ok(()) } wasmprinter-0.239.0/src/operand_stack.rs000064400000000000000000000104651046102023000163320ustar 00000000000000use crate::{Config, PrintTermcolor, Printer}; use wasmparser::{ BinaryReader, Frame, OperatorsReader, Payload, Result, ValType, ValidPayload, ValidatorResources, WasmFeatures, }; pub struct Validator { validator: wasmparser::Validator, last_function: Option, } impl Validator { pub fn new() -> Option { Some(Validator { validator: wasmparser::Validator::new_with_features(WasmFeatures::all()), last_function: None, }) } pub fn payload(&mut self, payload: &Payload<'_>) -> Result<()> { match self.validator.payload(payload)? { ValidPayload::Ok | ValidPayload::End(_) | ValidPayload::Parser(_) => Ok(()), ValidPayload::Func(validator, _) => { assert!(self.last_function.is_none()); self.last_function = Some(FuncValidator { push_count: 0, validator: validator.into_validator(Default::default()), }); Ok(()) } } } pub fn next_func(&mut self) -> Option { Some(self.last_function.take().unwrap()) } } pub struct FuncValidator { validator: wasmparser::FuncValidator, push_count: u32, } impl FuncValidator { pub fn read_locals(&mut self, mut reader: BinaryReader<'_>) -> Result<()> { self.validator.read_locals(&mut reader) } pub fn visit_operator(&mut self, reader: &OperatorsReader<'_>, is_end: bool) -> Result<()> { let pos = reader.original_position(); reader .clone() .visit_operator(&mut self.validator.visitor(pos))??; if !is_end { let op = reader.clone().read()?; let arity = op.operator_arity(&self.validator.visitor(pos)); if let Some((_pop_count, push_count)) = arity { self.push_count = push_count; } } Ok(()) } pub fn visualize_operand_stack(&self, use_color: bool) -> crate::Result { let mut buf_color = PrintTermcolor(termcolor::Ansi::new(Vec::new())); let mut buf_nocolor = PrintTermcolor(termcolor::NoColor::new(Vec::new())); let stack_printer = Printer { result: if use_color { &mut buf_color } else { &mut buf_nocolor }, config: &Config::new(), nesting: 0, line: 0, group_lines: Vec::new(), code_section_hints: Vec::new(), }; if let Some(&Frame { height, .. }) = self.validator.get_control_frame(0) { stack_printer.result.start_comment()?; stack_printer.result.write_str("[")?; let max_height = self.validator.operand_stack_height() as usize; for depth in (height..max_height).rev() { if depth + 1 < max_height { stack_printer.result.write_str(" ")?; } if depth + 1 == height + self.push_count as usize { stack_printer.result.start_type()?; } match self.validator.get_operand_type(depth) { Some(Some(ty)) => { stack_printer.result.write_str(&ty_to_str(ty))?; } Some(None) => { stack_printer.result.write_str("(unknown)")?; } None => { stack_printer.result.write_str("(invalid)")?; } } } stack_printer.result.start_comment()?; stack_printer.result.write_str("]")?; stack_printer.result.reset_color()?; } let ret = String::from_utf8(if use_color { buf_color.0.into_inner() } else { buf_nocolor.0.into_inner() })?; return Ok(ret); fn ty_to_str(ty: ValType) -> String { match ty { ValType::I32 => String::from("i32"), ValType::I64 => String::from("i64"), ValType::F32 => String::from("f32"), ValType::F64 => String::from("f64"), ValType::V128 => String::from("v128"), ValType::Ref(r) => format!("{r}"), } } } } wasmprinter-0.239.0/src/operand_stack_disabled.rs000064400000000000000000000013711046102023000201550ustar 00000000000000use wasmparser::{BinaryReader, OperatorsReader, Payload, Result}; pub enum Validator {} impl Validator { pub fn new() -> Option { None } pub fn payload(&mut self, _payload: &Payload<'_>) -> Result<()> { match *self {} } pub fn next_func(&mut self) -> Option { match *self {} } } pub enum FuncValidator {} impl FuncValidator { pub fn read_locals(&mut self, _reader: BinaryReader<'_>) -> Result<()> { match *self {} } pub fn visit_operator(&mut self, _reader: &OperatorsReader<'_>, _is_end: bool) -> Result<()> { match *self {} } pub fn visualize_operand_stack(&self, _use_color: bool) -> anyhow::Result { match *self {} } } wasmprinter-0.239.0/src/operator.rs000064400000000000000000002201031046102023000153400ustar 00000000000000use super::{Config, Print, PrintTermcolor, Printer, State}; use anyhow::{Result, anyhow, bail}; use termcolor::{Ansi, NoColor}; use wasmparser::VisitSimdOperator; use wasmparser::{ BlockType, BrTable, Catch, CompositeInnerType, ContType, FrameKind, FuncType, Handle, MemArg, ModuleArity, Operator, OperatorsReader, Ordering, RefType, ResumeTable, SubType, TryTable, VisitOperator, }; pub struct OperatorState { op_offset: usize, nesting_start: u32, label: u32, label_indices: Vec, sep: OperatorSeparator, } impl OperatorState { pub fn new(printer: &Printer, sep: OperatorSeparator) -> Self { OperatorState { op_offset: 0, nesting_start: printer.nesting, label: 0, label_indices: Vec::new(), sep, } } } pub struct PrintOperator<'printer, 'state, 'a, 'b> { pub(super) printer: &'printer mut Printer<'a, 'b>, state: &'state mut State, operator_state: &'printer mut OperatorState, } struct FoldedInstruction { plain: String, folded: Vec, results: u32, offset: usize, } struct Block { ty: BlockType, kind: FrameKind, plain: String, folded: Vec, predicate: Option>, consequent: Option<(Vec, usize)>, offset: usize, } pub struct PrintOperatorFolded<'printer, 'state, 'a, 'b> { pub(super) printer: &'printer mut Printer<'a, 'b>, state: &'state mut State, operator_state: &'printer mut OperatorState, control: Vec, branch_hint: Option, original_separator: OperatorSeparator, } #[derive(Copy, Clone)] pub enum OperatorSeparator { Newline, None, NoneThenSpace, Space, } impl<'printer, 'state, 'a, 'b> PrintOperator<'printer, 'state, 'a, 'b> { pub(super) fn new( printer: &'printer mut Printer<'a, 'b>, state: &'state mut State, operator_state: &'printer mut OperatorState, ) -> Self { PrintOperator { printer, state, operator_state, } } fn push_str(&mut self, s: &str) -> Result<()> { self.printer.result.write_str(s)?; Ok(()) } fn result(&mut self) -> &mut dyn Print { self.printer.result } fn separator(&mut self) -> Result<()> { match self.operator_state.sep { OperatorSeparator::Newline => self.printer.newline(self.operator_state.op_offset), OperatorSeparator::None => Ok(()), OperatorSeparator::NoneThenSpace => { self.operator_state.sep = OperatorSeparator::Space; Ok(()) } OperatorSeparator::Space => self.push_str(" "), } } /// Called just before an instruction that introduces a block such as /// `block`, `if`, `loop`, etc. fn block_start(&mut self) -> Result<()> { self.separator()?; self.printer.nesting += 1; self.operator_state .label_indices .push(self.operator_state.label); Ok(()) } /// Used for `else` and `delegate` fn block_mid(&mut self) -> Result<()> { self.printer.nesting -= 1; self.separator()?; self.printer.nesting += 1; Ok(()) } /// Used for `end` to terminate the prior block. fn block_end(&mut self) -> Result<()> { if self.printer.nesting > self.operator_state.nesting_start { self.printer.nesting -= 1; } self.separator()?; Ok(()) } fn blockty(&mut self, ty: BlockType) -> Result<()> { let has_name = self.blockty_without_label_comment(ty)?; self.maybe_blockty_label_comment(has_name) } fn blockty_without_label_comment(&mut self, ty: BlockType) -> Result { let key = (self.state.core.funcs, self.operator_state.label); let has_name = match self.state.core.label_names.index_to_name.get(&key) { Some(name) => { write!(self.printer.result, " ")?; name.write(self.printer)?; true } None if self.printer.config.name_unnamed => { // Subtract one from the depth here because the label was // already pushed onto our stack when the instruction was // entered so its own label is one less. let depth = self.cur_depth() - 1; write!(self.result(), " $#label{depth}")?; true } None => false, }; match ty { BlockType::Empty => {} BlockType::Type(t) => { self.push_str(" ")?; self.printer.start_group("result ")?; self.printer.print_valtype(self.state, t)?; self.printer.end_group()?; } BlockType::FuncType(idx) => { self.push_str(" ")?; self.printer .print_core_functype_idx(self.state, idx, None)?; } } Ok(has_name) } fn maybe_blockty_label_comment(&mut self, has_name: bool) -> Result<()> { if !has_name { let depth = self.cur_depth(); self.push_str(" ")?; self.result().start_comment()?; match self.operator_state.sep { OperatorSeparator::Newline | OperatorSeparator::None => { write!(self.result(), ";; label = @{depth}") } _ => write!(self.result(), " (; label = @{depth} ;)"), }?; self.result().reset_color()?; } self.operator_state.label += 1; Ok(()) } fn cur_depth(&self) -> u32 { self.printer.nesting - self.operator_state.nesting_start } fn tag_index(&mut self, index: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.tag_names, index)?; Ok(()) } fn relative_depth(&mut self, depth: u32) -> Result<()> { self.push_str(" ")?; match self.cur_depth().checked_sub(depth) { // If this relative depth is in-range relative to the current depth, // then try to print a name for this label. Label names are tracked // as a stack where the depth matches `cur_depth` roughly, but label // names don't account for the function name so offset by one more // here. Some(i) => { let name = i .checked_sub(1) .and_then(|idx| self.operator_state.label_indices.get(idx as usize).copied()) .and_then(|label_idx| { let key = (self.state.core.funcs, label_idx); self.state.core.label_names.index_to_name.get(&key) }); // This is a bit tricky, but if there's a shallower label than // this target which shares the same name then we can't print // the name-based version. Names resolve to the nearest label // in the case of shadowing, which would be the wrong behavior // here. All that can be done is to print the index down below // instead. let name_conflict = name.is_some() && self.operator_state.label_indices[i as usize..] .iter() .any(|other_label| { let key = (self.state.core.funcs, *other_label); if let Some(other) = self.state.core.label_names.index_to_name.get(&key) { if name.unwrap().name == other.name { return true; } } false }); match name { // Only print the name if one is found and there's also no // name conflict. Some(name) if !name_conflict => name.write(self.printer)?, // If there's no name conflict, and we're synthesizing // names, and this isn't targeting the function itself then // print a synthesized names. // // Note that synthesized label names don't handle the // function itself, so i==0, branching to a function label, // is not supported and otherwise labels are offset by 1. None if !name_conflict && self.printer.config.name_unnamed && i > 0 => { self.result().start_name()?; write!(self.result(), "$#label{}", i - 1)?; self.result().reset_color()?; } _ => { // Last-ditch resort, we gotta print the index. self.result().start_name()?; write!(self.result(), "{depth}")?; self.result().reset_color()?; // Unnamed labels have helpful `@N` labels printed for // them so also try to print where this index is going // (label-wise). Don't do this for a name conflict // though because we wouldn't have printed the numbered // label, and also don't do it for the function itself // since the function has no label we can synthesize. if !name_conflict && i > 0 { self.result().start_comment()?; write!(self.result(), " (;@{i};)")?; self.result().reset_color()?; } } } } // This branch is out of range. Print the raw integer and then leave // a hopefully-helpful comment indicating that it's going nowhere. None => write!(self.result(), "{depth} (; INVALID ;)")?, } Ok(()) } fn targets(&mut self, targets: BrTable<'_>) -> Result<()> { for item in targets.targets().chain([Ok(targets.default())]) { self.relative_depth(item?)?; } Ok(()) } fn function_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.func_names, idx) } fn local_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer .print_local_idx(self.state, self.state.core.funcs, idx) } fn global_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.global_names, idx) } fn table_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.table_names, idx) } fn table(&mut self, idx: u32) -> Result<()> { self.table_index(idx) } fn memory_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.memory_names, idx) } fn type_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_core_type_ref(self.state, idx) } fn cont_type_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.type_names, idx) } fn argument_index(&mut self, idx: u32) -> Result<()> { self.cont_type_index(idx) } fn result_index(&mut self, idx: u32) -> Result<()> { self.cont_type_index(idx) } fn array_type_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.type_names, idx) } fn array_type_index_dst(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.type_names, idx) } fn array_type_index_src(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.type_names, idx) } fn array_size(&mut self, array_size: u32) -> Result<()> { write!(&mut self.printer.result, " {array_size}")?; Ok(()) } fn struct_type_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.type_names, idx) } fn from_ref_type(&mut self, ref_ty: RefType) -> Result<()> { self.push_str(" ")?; self.printer.print_reftype(self.state, ref_ty) } fn to_ref_type(&mut self, ref_ty: RefType) -> Result<()> { self.push_str(" ")?; self.printer.print_reftype(self.state, ref_ty) } fn data_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.data_names, idx) } fn array_data_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.data_names, idx) } fn elem_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.element_names, idx) } fn array_elem_index(&mut self, idx: u32) -> Result<()> { self.push_str(" ")?; self.printer.print_idx(&self.state.core.element_names, idx) } fn lane(&mut self, lane: u8) -> Result<()> { write!(self.result(), " {lane}")?; Ok(()) } fn lanes(&mut self, lanes: [u8; 16]) -> Result<()> { for lane in lanes.iter() { write!(self.result(), " {lane}")?; } Ok(()) } fn memarg(&mut self, memarg: MemArg) -> Result<()> { if memarg.memory != 0 { self.memory_index(memarg.memory)?; } if memarg.offset != 0 { write!(self.result(), " offset={}", memarg.offset)?; } if memarg.align != memarg.max_align { let align = 1_u64 << memarg.align; write!(self.result(), " align={align}")?; } Ok(()) } fn ordering(&mut self, ordering: Ordering) -> Result<()> { write!( self.result(), " {}", match ordering { Ordering::SeqCst => "seq_cst", Ordering::AcqRel => "acq_rel", } )?; Ok(()) } fn try_table(&mut self, table: TryTable) -> Result<()> { let has_name = self.blockty_without_label_comment(table.ty)?; // Nesting has already been incremented but labels for catch start above // this `try_table` not at the `try_table`. Temporarily decrement this // nesting count and increase it below after printing catch clauses. self.printer.nesting -= 2; let try_table_label = self.operator_state.label_indices.pop().unwrap(); for catch in table.catches { self.result().write_str(" ")?; match catch { Catch::One { tag, label } => { self.printer.start_group("catch")?; self.tag_index(tag)?; self.relative_depth(label)?; self.printer.end_group()?; } Catch::OneRef { tag, label } => { self.printer.start_group("catch_ref")?; self.tag_index(tag)?; self.relative_depth(label)?; self.printer.end_group()?; } Catch::All { label } => { self.printer.start_group("catch_all")?; self.relative_depth(label)?; self.printer.end_group()?; } Catch::AllRef { label } => { self.printer.start_group("catch_all_ref")?; self.relative_depth(label)?; self.printer.end_group()?; } } } self.operator_state.label_indices.push(try_table_label); self.printer.nesting += 2; self.maybe_blockty_label_comment(has_name)?; Ok(()) } fn resume_table(&mut self, table: ResumeTable) -> Result<()> { // The start_group("resume/resume_throw") have already // increased the nesting depth, but the labels are defined // above this `resume` or `resume_throw`. Therefore we // temporarily decrement this nesting count and increase it // below after printing the on clauses. self.printer.nesting -= 1; for handle in table.handlers { self.result().write_str(" ")?; self.printer.start_group("on")?; match handle { Handle::OnLabel { tag, label } => { self.tag_index(tag)?; self.relative_depth(label)?; } Handle::OnSwitch { tag } => { self.tag_index(tag)?; self.result().write_str(" switch")?; } } self.printer.end_group()?; } self.printer.nesting += 1; Ok(()) } } macro_rules! define_visit { // General structure of all the operator printer methods: // // * Print the name of the insruction as defined in this macro // * Print any payload, as necessary ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*) )*) => ($( fn $visit(&mut self $( , $($arg: $argty),* )?) -> Self::Output { define_visit!(before_op self $op); self.push_str(define_visit!(name $op))?; $( define_visit!(payload self $op $($arg)*); )? define_visit!(after_op self $op); Ok(()) } )*); // Control-flow related opcodes have special handling to manage nested // depth as well as the stack of labels. // // The catch-all for "before an op" is "print an newline" (before_op $self:ident Loop) => ($self.block_start()?;); (before_op $self:ident Block) => ($self.block_start()?;); (before_op $self:ident If) => ($self.block_start()?;); (before_op $self:ident Try) => ($self.block_start()?;); (before_op $self:ident TryTable) => ($self.block_start()?;); (before_op $self:ident Catch) => ($self.block_mid()?;); (before_op $self:ident CatchAll) => ($self.block_mid()?;); (before_op $self:ident Delegate) => ($self.block_end()?;); (before_op $self:ident Else) => ($self.block_mid()?;); (before_op $self:ident End) => ($self.block_end()?;); (before_op $self:ident $op:ident) => ($self.separator()?;); // After some opcodes the label stack is popped. // (after_op $self:ident Delegate) => ($self.label_indices.pop();); (after_op $self:ident End) => ($self.operator_state.label_indices.pop();); (after_op $self:ident $op:ident) => (); // How to print the payload of an instruction. There are a number of // instructions that have special cases such as avoiding printing anything // when an index is 0 or similar. The final case in this list is the // catch-all which prints each payload individually based on the name of the // payload field. (payload $self:ident CallIndirect $ty:ident $table:ident) => ( if $table != 0 { $self.table_index($table)?; } $self.type_index($ty)?; ); (payload $self:ident ReturnCallIndirect $ty:ident $table:ident) => ( if $table != 0 { $self.table_index($table)?; } $self.type_index($ty)?; ); (payload $self:ident CallRef $ty:ident) => ( $self.push_str(" ")?; $self.printer.print_idx(&$self.state.core.type_names, $ty)?; ); (payload $self:ident ReturnCallRef $ty:ident) => ( $self.push_str(" ")?; $self.printer.print_idx(&$self.state.core.type_names, $ty)?; ); (payload $self:ident TypedSelect $select_ty:ident) => ( $self.push_str(" ")?; $self.printer.start_group("result ")?; $self.printer.print_valtype($self.state, $select_ty)?; $self.printer.end_group()?; ); (payload $self:ident TypedSelectMulti $select_tys:ident) => ( $self.push_str(" ")?; $self.printer.start_group("result")?; $self.printer.print_valtypes($self.state, $select_tys)?; $self.printer.end_group()?; ); (payload $self:ident RefNull $hty:ident) => ( $self.push_str(" ")?; $self.printer.print_heaptype($self.state, $hty)?; ); (payload $self:ident TableInit $segment:ident $table:ident) => ( if $table != 0 { $self.table_index($table)?; } $self.elem_index($segment)?; ); (payload $self:ident TableCopy $dst:ident $src:ident) => ( if $src != 0 || $dst != 0 { $self.table_index($dst)?; $self.table_index($src)?; } ); (payload $self:ident MemoryGrow $mem:ident) => ( if $mem != 0 { $self.memory_index($mem)?; } ); (payload $self:ident MemorySize $mem:ident) => ( if $mem != 0 { $self.memory_index($mem)?; } ); (payload $self:ident MemoryInit $segment:ident $mem:ident) => ( if $mem != 0 { $self.memory_index($mem)?; } $self.data_index($segment)?; ); (payload $self:ident MemoryCopy $dst:ident $src:ident) => ( if $src != 0 || $dst != 0 { $self.memory_index($dst)?; $self.memory_index($src)?; } ); (payload $self:ident MemoryFill $mem:ident) => ( if $mem != 0 { $self.memory_index($mem)?; } ); (payload $self:ident MemoryDiscard $mem:ident) => ( if $mem != 0 { $self.memory_index($mem)?; } ); (payload $self:ident I32Const $val:ident) => ( $self.result().start_literal()?; write!($self.result(), " {}", $val)?; $self.result().reset_color()?; ); (payload $self:ident I64Const $val:ident) => ( $self.result().start_literal()?; write!($self.result(), " {}", $val)?; $self.result().reset_color()?; ); (payload $self:ident F32Const $val:ident) => ( $self.push_str(" ")?; $self.printer.print_f32($val.bits())?; ); (payload $self:ident F64Const $val:ident) => ( $self.push_str(" ")?; $self.printer.print_f64($val.bits())?; ); (payload $self:ident V128Const $val:ident) => ( $self.printer.print_type_keyword(" i32x4")?; $self.result().start_literal()?; for chunk in $val.bytes().chunks(4) { write!( $self.result(), " 0x{:02x}{:02x}{:02x}{:02x}", chunk[3], chunk[2], chunk[1], chunk[0], )?; } $self.result().reset_color()?; ); (payload $self:ident RefTestNonNull $hty:ident) => ( $self.push_str(" ")?; let rty = RefType::new(false, $hty) .ok_or_else(|| anyhow!("implementation limit: type index too large"))?; $self.printer.print_reftype($self.state, rty)?; ); (payload $self:ident RefTestNullable $hty:ident) => ( $self.push_str(" ")?; let rty = RefType::new(true, $hty) .ok_or_else(|| anyhow!("implementation limit: type index too large"))?; $self.printer.print_reftype($self.state, rty)?; ); (payload $self:ident RefCastNonNull $hty:ident) => ( $self.push_str(" ")?; let rty = RefType::new(false, $hty) .ok_or_else(|| anyhow!("implementation limit: type index too large"))?; $self.printer.print_reftype($self.state, rty)?; ); (payload $self:ident RefCastNullable $hty:ident) => ( $self.push_str(" ")?; let rty = RefType::new(true, $hty) .ok_or_else(|| anyhow!("implementation limit: type index too large"))?; $self.printer.print_reftype($self.state, rty)?; ); (payload $self:ident StructGet $ty:ident $field:ident) => ( $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructGetS $ty:ident $field:ident) => ( $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructGetU $ty:ident $field:ident) => ( $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructSet $ty:ident $field:ident) => ( $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicGet $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicGetS $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicGetU $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicSet $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicRmwAdd $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicRmwSub $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicRmwAnd $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicRmwOr $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicRmwXor $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicRmwXchg $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident StructAtomicRmwCmpxchg $order:ident $ty:ident $field:ident) => ( $self.ordering($order)?; $self.struct_type_index($ty)?; $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); (payload $self:ident $op:ident $($arg:ident)*) => ( $($self.$arg($arg)?;)* ); (name Block) => ("block"); (name If) => ("if"); (name Else) => ("else"); (name Loop) => ("loop"); (name End) => ("end"); (name Unreachable) => ("unreachable"); (name Nop) => ("nop"); (name Br) => ("br"); (name BrIf) => ("br_if"); (name BrOnNull) => ("br_on_null"); (name BrOnNonNull) => ("br_on_non_null"); (name BrTable) => ("br_table"); (name Return) => ("return"); (name Call) => ("call"); (name CallIndirect) => ("call_indirect"); (name CallRef) => ("call_ref"); (name ReturnCall) => ("return_call"); (name ReturnCallIndirect) => ("return_call_indirect"); (name ReturnCallRef) => ("return_call_ref"); (name Drop) => ("drop"); (name Select) => ("select"); (name TypedSelect) => ("select"); (name TypedSelectMulti) => ("select"); (name LocalGet) => ("local.get"); (name LocalSet) => ("local.set"); (name LocalTee) => ("local.tee"); (name GlobalGet) => ("global.get"); (name GlobalSet) => ("global.set"); (name TableGet) => ("table.get"); (name TableSet) => ("table.set"); (name I32Load) => ("i32.load"); (name I64Load) => ("i64.load"); (name F32Load) => ("f32.load"); (name F64Load) => ("f64.load"); (name I32Load8S) => ("i32.load8_s"); (name I32Load8U) => ("i32.load8_u"); (name I32Load16S) => ("i32.load16_s"); (name I32Load16U) => ("i32.load16_u"); (name I64Load8S) => ("i64.load8_s"); (name I64Load8U) => ("i64.load8_u"); (name I64Load16S) => ("i64.load16_s"); (name I64Load16U) => ("i64.load16_u"); (name I64Load32S) => ("i64.load32_s"); (name I64Load32U) => ("i64.load32_u"); (name I32Store) => ("i32.store"); (name I64Store) => ("i64.store"); (name F32Store) => ("f32.store"); (name F64Store) => ("f64.store"); (name I32Store8) => ("i32.store8"); (name I32Store16) => ("i32.store16"); (name I64Store8) => ("i64.store8"); (name I64Store16) => ("i64.store16"); (name I64Store32) => ("i64.store32"); (name MemorySize) => ("memory.size"); (name MemoryGrow) => ("memory.grow"); (name MemoryInit) => ("memory.init"); (name MemoryCopy) => ("memory.copy"); (name MemoryFill) => ("memory.fill"); (name MemoryDiscard) => ("memory.discard"); (name DataDrop) => ("data.drop"); (name ElemDrop) => ("elem.drop"); (name TableInit) => ("table.init"); (name TableCopy) => ("table.copy"); (name TableFill) => ("table.fill"); (name TableSize) => ("table.size"); (name TableGrow) => ("table.grow"); (name RefAsNonNull) => ("ref.as_non_null"); (name RefNull) => ("ref.null"); (name RefEq) => ("ref.eq"); (name RefIsNull) => ("ref.is_null"); (name RefFunc) => ("ref.func"); (name I32Const) => ("i32.const"); (name I64Const) => ("i64.const"); (name F32Const) => ("f32.const"); (name F64Const) => ("f64.const"); (name I32Clz) => ("i32.clz"); (name I32Ctz) => ("i32.ctz"); (name I32Popcnt) => ("i32.popcnt"); (name I32Add) => ("i32.add"); (name I32Sub) => ("i32.sub"); (name I32Mul) => ("i32.mul"); (name I32DivS) => ("i32.div_s"); (name I32DivU) => ("i32.div_u"); (name I32RemS) => ("i32.rem_s"); (name I32RemU) => ("i32.rem_u"); (name I32And) => ("i32.and"); (name I32Or) => ("i32.or"); (name I32Xor) => ("i32.xor"); (name I32Shl) => ("i32.shl"); (name I32ShrS) => ("i32.shr_s"); (name I32ShrU) => ("i32.shr_u"); (name I32Rotl) => ("i32.rotl"); (name I32Rotr) => ("i32.rotr"); (name I64Clz) => ("i64.clz"); (name I64Ctz) => ("i64.ctz"); (name I64Popcnt) => ("i64.popcnt"); (name I64Add) => ("i64.add"); (name I64Sub) => ("i64.sub"); (name I64Mul) => ("i64.mul"); (name I64DivS) => ("i64.div_s"); (name I64DivU) => ("i64.div_u"); (name I64RemS) => ("i64.rem_s"); (name I64RemU) => ("i64.rem_u"); (name I64And) => ("i64.and"); (name I64Or) => ("i64.or"); (name I64Xor) => ("i64.xor"); (name I64Shl) => ("i64.shl"); (name I64ShrS) => ("i64.shr_s"); (name I64ShrU) => ("i64.shr_u"); (name I64Rotl) => ("i64.rotl"); (name I64Rotr) => ("i64.rotr"); (name F32Abs) => ("f32.abs"); (name F32Neg) => ("f32.neg"); (name F32Ceil) => ("f32.ceil"); (name F32Floor) => ("f32.floor"); (name F32Trunc) => ("f32.trunc"); (name F32Nearest) => ("f32.nearest"); (name F32Sqrt) => ("f32.sqrt"); (name F32Add) => ("f32.add"); (name F32Sub) => ("f32.sub"); (name F32Mul) => ("f32.mul"); (name F32Div) => ("f32.div"); (name F32Min) => ("f32.min"); (name F32Max) => ("f32.max"); (name F32Copysign) => ("f32.copysign"); (name F64Abs) => ("f64.abs"); (name F64Neg) => ("f64.neg"); (name F64Ceil) => ("f64.ceil"); (name F64Floor) => ("f64.floor"); (name F64Trunc) => ("f64.trunc"); (name F64Nearest) => ("f64.nearest"); (name F64Sqrt) => ("f64.sqrt"); (name F64Add) => ("f64.add"); (name F64Sub) => ("f64.sub"); (name F64Mul) => ("f64.mul"); (name F64Div) => ("f64.div"); (name F64Min) => ("f64.min"); (name F64Max) => ("f64.max"); (name F64Copysign) => ("f64.copysign"); (name I32Eqz) => ("i32.eqz"); (name I32Eq) => ("i32.eq"); (name I32Ne) => ("i32.ne"); (name I32LtS) => ("i32.lt_s"); (name I32LtU) => ("i32.lt_u"); (name I32GtS) => ("i32.gt_s"); (name I32GtU) => ("i32.gt_u"); (name I32LeS) => ("i32.le_s"); (name I32LeU) => ("i32.le_u"); (name I32GeS) => ("i32.ge_s"); (name I32GeU) => ("i32.ge_u"); (name I64Eqz) => ("i64.eqz"); (name I64Eq) => ("i64.eq"); (name I64Ne) => ("i64.ne"); (name I64LtS) => ("i64.lt_s"); (name I64LtU) => ("i64.lt_u"); (name I64GtS) => ("i64.gt_s"); (name I64GtU) => ("i64.gt_u"); (name I64LeS) => ("i64.le_s"); (name I64LeU) => ("i64.le_u"); (name I64GeS) => ("i64.ge_s"); (name I64GeU) => ("i64.ge_u"); (name F32Eq) => ("f32.eq"); (name F32Ne) => ("f32.ne"); (name F32Lt) => ("f32.lt"); (name F32Gt) => ("f32.gt"); (name F32Le) => ("f32.le"); (name F32Ge) => ("f32.ge"); (name F64Eq) => ("f64.eq"); (name F64Ne) => ("f64.ne"); (name F64Lt) => ("f64.lt"); (name F64Gt) => ("f64.gt"); (name F64Le) => ("f64.le"); (name F64Ge) => ("f64.ge"); (name I32WrapI64) => ("i32.wrap_i64"); (name I32TruncF32S) => ("i32.trunc_f32_s"); (name I32TruncF32U) => ("i32.trunc_f32_u"); (name I32TruncF64S) => ("i32.trunc_f64_s"); (name I32TruncF64U) => ("i32.trunc_f64_u"); (name I64ExtendI32S) => ("i64.extend_i32_s"); (name I64ExtendI32U) => ("i64.extend_i32_u"); (name I64TruncF32S) => ("i64.trunc_f32_s"); (name I64TruncF32U) => ("i64.trunc_f32_u"); (name I64TruncF64S) => ("i64.trunc_f64_s"); (name I64TruncF64U) => ("i64.trunc_f64_u"); (name F32ConvertI32S) => ("f32.convert_i32_s"); (name F32ConvertI32U) => ("f32.convert_i32_u"); (name F32ConvertI64S) => ("f32.convert_i64_s"); (name F32ConvertI64U) => ("f32.convert_i64_u"); (name F32DemoteF64) => ("f32.demote_f64"); (name F64ConvertI32S) => ("f64.convert_i32_s"); (name F64ConvertI32U) => ("f64.convert_i32_u"); (name F64ConvertI64S) => ("f64.convert_i64_s"); (name F64ConvertI64U) => ("f64.convert_i64_u"); (name F64PromoteF32) => ("f64.promote_f32"); (name I32ReinterpretF32) => ("i32.reinterpret_f32"); (name I64ReinterpretF64) => ("i64.reinterpret_f64"); (name F32ReinterpretI32) => ("f32.reinterpret_i32"); (name F64ReinterpretI64) => ("f64.reinterpret_i64"); (name I32TruncSatF32S) => ("i32.trunc_sat_f32_s"); (name I32TruncSatF32U) => ("i32.trunc_sat_f32_u"); (name I32TruncSatF64S) => ("i32.trunc_sat_f64_s"); (name I32TruncSatF64U) => ("i32.trunc_sat_f64_u"); (name I64TruncSatF32S) => ("i64.trunc_sat_f32_s"); (name I64TruncSatF32U) => ("i64.trunc_sat_f32_u"); (name I64TruncSatF64S) => ("i64.trunc_sat_f64_s"); (name I64TruncSatF64U) => ("i64.trunc_sat_f64_u"); (name I32Extend8S) => ("i32.extend8_s"); (name I32Extend16S) => ("i32.extend16_s"); (name I64Extend8S) => ("i64.extend8_s"); (name I64Extend16S) => ("i64.extend16_s"); (name I64Extend32S) => ("i64.extend32_s"); (name MemoryAtomicNotify) => ("memory.atomic.notify"); (name MemoryAtomicWait32) => ("memory.atomic.wait32"); (name MemoryAtomicWait64) => ("memory.atomic.wait64"); (name AtomicFence) => ("atomic.fence"); (name I32AtomicLoad) => ("i32.atomic.load"); (name I64AtomicLoad) => ("i64.atomic.load"); (name I32AtomicLoad8U) => ("i32.atomic.load8_u"); (name I32AtomicLoad16U) => ("i32.atomic.load16_u"); (name I64AtomicLoad8U) => ("i64.atomic.load8_u"); (name I64AtomicLoad16U) => ("i64.atomic.load16_u"); (name I64AtomicLoad32U) => ("i64.atomic.load32_u"); (name I32AtomicStore) => ("i32.atomic.store"); (name I64AtomicStore) => ("i64.atomic.store"); (name I32AtomicStore8) => ("i32.atomic.store8"); (name I32AtomicStore16) => ("i32.atomic.store16"); (name I64AtomicStore8) => ("i64.atomic.store8"); (name I64AtomicStore16) => ("i64.atomic.store16"); (name I64AtomicStore32) => ("i64.atomic.store32"); (name I32AtomicRmwAdd) => ("i32.atomic.rmw.add"); (name I64AtomicRmwAdd) => ("i64.atomic.rmw.add"); (name I32AtomicRmw8AddU) => ("i32.atomic.rmw8.add_u"); (name I32AtomicRmw16AddU) => ("i32.atomic.rmw16.add_u"); (name I64AtomicRmw8AddU) => ("i64.atomic.rmw8.add_u"); (name I64AtomicRmw16AddU) => ("i64.atomic.rmw16.add_u"); (name I64AtomicRmw32AddU) => ("i64.atomic.rmw32.add_u"); (name I32AtomicRmwSub) => ("i32.atomic.rmw.sub"); (name I64AtomicRmwSub) => ("i64.atomic.rmw.sub"); (name I32AtomicRmw8SubU) => ("i32.atomic.rmw8.sub_u"); (name I32AtomicRmw16SubU) => ("i32.atomic.rmw16.sub_u"); (name I64AtomicRmw8SubU) => ("i64.atomic.rmw8.sub_u"); (name I64AtomicRmw16SubU) => ("i64.atomic.rmw16.sub_u"); (name I64AtomicRmw32SubU) => ("i64.atomic.rmw32.sub_u"); (name I32AtomicRmwAnd) => ("i32.atomic.rmw.and"); (name I64AtomicRmwAnd) => ("i64.atomic.rmw.and"); (name I32AtomicRmw8AndU) => ("i32.atomic.rmw8.and_u"); (name I32AtomicRmw16AndU) => ("i32.atomic.rmw16.and_u"); (name I64AtomicRmw8AndU) => ("i64.atomic.rmw8.and_u"); (name I64AtomicRmw16AndU) => ("i64.atomic.rmw16.and_u"); (name I64AtomicRmw32AndU) => ("i64.atomic.rmw32.and_u"); (name I32AtomicRmwOr) => ("i32.atomic.rmw.or"); (name I64AtomicRmwOr) => ("i64.atomic.rmw.or"); (name I32AtomicRmw8OrU) => ("i32.atomic.rmw8.or_u"); (name I32AtomicRmw16OrU) => ("i32.atomic.rmw16.or_u"); (name I64AtomicRmw8OrU) => ("i64.atomic.rmw8.or_u"); (name I64AtomicRmw16OrU) => ("i64.atomic.rmw16.or_u"); (name I64AtomicRmw32OrU) => ("i64.atomic.rmw32.or_u"); (name I32AtomicRmwXor) => ("i32.atomic.rmw.xor"); (name I64AtomicRmwXor) => ("i64.atomic.rmw.xor"); (name I32AtomicRmw8XorU) => ("i32.atomic.rmw8.xor_u"); (name I32AtomicRmw16XorU) => ("i32.atomic.rmw16.xor_u"); (name I64AtomicRmw8XorU) => ("i64.atomic.rmw8.xor_u"); (name I64AtomicRmw16XorU) => ("i64.atomic.rmw16.xor_u"); (name I64AtomicRmw32XorU) => ("i64.atomic.rmw32.xor_u"); (name I32AtomicRmwXchg) => ("i32.atomic.rmw.xchg"); (name I64AtomicRmwXchg) => ("i64.atomic.rmw.xchg"); (name I32AtomicRmw8XchgU) => ("i32.atomic.rmw8.xchg_u"); (name I32AtomicRmw16XchgU) => ("i32.atomic.rmw16.xchg_u"); (name I64AtomicRmw8XchgU) => ("i64.atomic.rmw8.xchg_u"); (name I64AtomicRmw16XchgU) => ("i64.atomic.rmw16.xchg_u"); (name I64AtomicRmw32XchgU) => ("i64.atomic.rmw32.xchg_u"); (name I32AtomicRmwCmpxchg) => ("i32.atomic.rmw.cmpxchg"); (name I64AtomicRmwCmpxchg) => ("i64.atomic.rmw.cmpxchg"); (name I32AtomicRmw8CmpxchgU) => ("i32.atomic.rmw8.cmpxchg_u"); (name I32AtomicRmw16CmpxchgU) => ("i32.atomic.rmw16.cmpxchg_u"); (name I64AtomicRmw8CmpxchgU) => ("i64.atomic.rmw8.cmpxchg_u"); (name I64AtomicRmw16CmpxchgU) => ("i64.atomic.rmw16.cmpxchg_u"); (name I64AtomicRmw32CmpxchgU) => ("i64.atomic.rmw32.cmpxchg_u"); (name V128Load) => ("v128.load"); (name V128Load8x8S) => ("v128.load8x8_s"); (name V128Load8x8U) => ("v128.load8x8_u"); (name V128Load16x4S) => ("v128.load16x4_s"); (name V128Load16x4U) => ("v128.load16x4_u"); (name V128Load32x2S) => ("v128.load32x2_s"); (name V128Load32x2U) => ("v128.load32x2_u"); (name V128Load8Splat) => ("v128.load8_splat"); (name V128Load16Splat) => ("v128.load16_splat"); (name V128Load32Splat) => ("v128.load32_splat"); (name V128Load64Splat) => ("v128.load64_splat"); (name V128Load32Zero) => ("v128.load32_zero"); (name V128Load64Zero) => ("v128.load64_zero"); (name V128Store) => ("v128.store"); (name V128Load8Lane) => ("v128.load8_lane"); (name V128Load16Lane) => ("v128.load16_lane"); (name V128Load32Lane) => ("v128.load32_lane"); (name V128Load64Lane) => ("v128.load64_lane"); (name V128Store8Lane) => ("v128.store8_lane"); (name V128Store16Lane) => ("v128.store16_lane"); (name V128Store32Lane) => ("v128.store32_lane"); (name V128Store64Lane) => ("v128.store64_lane"); (name V128Const) => ("v128.const"); (name I8x16Shuffle) => ("i8x16.shuffle"); (name I8x16ExtractLaneS) => ("i8x16.extract_lane_s"); (name I8x16ExtractLaneU) => ("i8x16.extract_lane_u"); (name I8x16ReplaceLane) => ("i8x16.replace_lane"); (name I16x8ExtractLaneS) => ("i16x8.extract_lane_s"); (name I16x8ExtractLaneU) => ("i16x8.extract_lane_u"); (name I16x8ReplaceLane) => ("i16x8.replace_lane"); (name I32x4ExtractLane) => ("i32x4.extract_lane"); (name I32x4ReplaceLane) => ("i32x4.replace_lane"); (name I64x2ExtractLane) => ("i64x2.extract_lane"); (name I64x2ReplaceLane) => ("i64x2.replace_lane"); (name F32x4ExtractLane) => ("f32x4.extract_lane"); (name F32x4ReplaceLane) => ("f32x4.replace_lane"); (name F64x2ExtractLane) => ("f64x2.extract_lane"); (name F64x2ReplaceLane) => ("f64x2.replace_lane"); (name I8x16Swizzle) => ("i8x16.swizzle"); (name I8x16Splat) => ("i8x16.splat"); (name I16x8Splat) => ("i16x8.splat"); (name I32x4Splat) => ("i32x4.splat"); (name I64x2Splat) => ("i64x2.splat"); (name F32x4Splat) => ("f32x4.splat"); (name F64x2Splat) => ("f64x2.splat"); (name I8x16Eq) => ("i8x16.eq"); (name I8x16Ne) => ("i8x16.ne"); (name I8x16LtS) => ("i8x16.lt_s"); (name I8x16LtU) => ("i8x16.lt_u"); (name I8x16GtS) => ("i8x16.gt_s"); (name I8x16GtU) => ("i8x16.gt_u"); (name I8x16LeS) => ("i8x16.le_s"); (name I8x16LeU) => ("i8x16.le_u"); (name I8x16GeS) => ("i8x16.ge_s"); (name I8x16GeU) => ("i8x16.ge_u"); (name I16x8Eq) => ("i16x8.eq"); (name I16x8Ne) => ("i16x8.ne"); (name I16x8LtS) => ("i16x8.lt_s"); (name I16x8LtU) => ("i16x8.lt_u"); (name I16x8GtS) => ("i16x8.gt_s"); (name I16x8GtU) => ("i16x8.gt_u"); (name I16x8LeS) => ("i16x8.le_s"); (name I16x8LeU) => ("i16x8.le_u"); (name I16x8GeS) => ("i16x8.ge_s"); (name I16x8GeU) => ("i16x8.ge_u"); (name I32x4Eq) => ("i32x4.eq"); (name I32x4Ne) => ("i32x4.ne"); (name I32x4LtS) => ("i32x4.lt_s"); (name I32x4LtU) => ("i32x4.lt_u"); (name I32x4GtS) => ("i32x4.gt_s"); (name I32x4GtU) => ("i32x4.gt_u"); (name I32x4LeS) => ("i32x4.le_s"); (name I32x4LeU) => ("i32x4.le_u"); (name I32x4GeS) => ("i32x4.ge_s"); (name I32x4GeU) => ("i32x4.ge_u"); (name I64x2Eq) => ("i64x2.eq"); (name I64x2Ne) => ("i64x2.ne"); (name I64x2LtS) => ("i64x2.lt_s"); (name I64x2GtS) => ("i64x2.gt_s"); (name I64x2LeS) => ("i64x2.le_s"); (name I64x2GeS) => ("i64x2.ge_s"); (name F32x4Eq) => ("f32x4.eq"); (name F32x4Ne) => ("f32x4.ne"); (name F32x4Lt) => ("f32x4.lt"); (name F32x4Gt) => ("f32x4.gt"); (name F32x4Le) => ("f32x4.le"); (name F32x4Ge) => ("f32x4.ge"); (name F64x2Eq) => ("f64x2.eq"); (name F64x2Ne) => ("f64x2.ne"); (name F64x2Lt) => ("f64x2.lt"); (name F64x2Gt) => ("f64x2.gt"); (name F64x2Le) => ("f64x2.le"); (name F64x2Ge) => ("f64x2.ge"); (name V128Not) => ("v128.not"); (name V128And) => ("v128.and"); (name V128AndNot) => ("v128.andnot"); (name V128Or) => ("v128.or"); (name V128Xor) => ("v128.xor"); (name V128Bitselect) => ("v128.bitselect"); (name V128AnyTrue) => ("v128.any_true"); (name I8x16Abs) => ("i8x16.abs"); (name I8x16Neg) => ("i8x16.neg"); (name I8x16Popcnt) => ("i8x16.popcnt"); (name I8x16AllTrue) => ("i8x16.all_true"); (name I8x16Bitmask) => ("i8x16.bitmask"); (name I8x16NarrowI16x8S) => ("i8x16.narrow_i16x8_s"); (name I8x16NarrowI16x8U) => ("i8x16.narrow_i16x8_u"); (name I8x16Shl) => ("i8x16.shl"); (name I8x16ShrS) => ("i8x16.shr_s"); (name I8x16ShrU) => ("i8x16.shr_u"); (name I8x16Add) => ("i8x16.add"); (name I8x16AddSatS) => ("i8x16.add_sat_s"); (name I8x16AddSatU) => ("i8x16.add_sat_u"); (name I8x16Sub) => ("i8x16.sub"); (name I8x16SubSatS) => ("i8x16.sub_sat_s"); (name I8x16SubSatU) => ("i8x16.sub_sat_u"); (name I8x16MinS) => ("i8x16.min_s"); (name I8x16MinU) => ("i8x16.min_u"); (name I8x16MaxS) => ("i8x16.max_s"); (name I8x16MaxU) => ("i8x16.max_u"); (name I8x16AvgrU) => ("i8x16.avgr_u"); (name I16x8ExtAddPairwiseI8x16S) => ("i16x8.extadd_pairwise_i8x16_s"); (name I16x8ExtAddPairwiseI8x16U) => ("i16x8.extadd_pairwise_i8x16_u"); (name I16x8Abs) => ("i16x8.abs"); (name I16x8Neg) => ("i16x8.neg"); (name I16x8Q15MulrSatS) => ("i16x8.q15mulr_sat_s"); (name I16x8AllTrue) => ("i16x8.all_true"); (name I16x8Bitmask) => ("i16x8.bitmask"); (name I16x8NarrowI32x4S) => ("i16x8.narrow_i32x4_s"); (name I16x8NarrowI32x4U) => ("i16x8.narrow_i32x4_u"); (name I16x8ExtendLowI8x16S) => ("i16x8.extend_low_i8x16_s"); (name I16x8ExtendHighI8x16S) => ("i16x8.extend_high_i8x16_s"); (name I16x8ExtendLowI8x16U) => ("i16x8.extend_low_i8x16_u"); (name I16x8ExtendHighI8x16U) => ("i16x8.extend_high_i8x16_u"); (name I16x8Shl) => ("i16x8.shl"); (name I16x8ShrS) => ("i16x8.shr_s"); (name I16x8ShrU) => ("i16x8.shr_u"); (name I16x8Add) => ("i16x8.add"); (name I16x8AddSatS) => ("i16x8.add_sat_s"); (name I16x8AddSatU) => ("i16x8.add_sat_u"); (name I16x8Sub) => ("i16x8.sub"); (name I16x8SubSatS) => ("i16x8.sub_sat_s"); (name I16x8SubSatU) => ("i16x8.sub_sat_u"); (name I16x8Mul) => ("i16x8.mul"); (name I16x8MinS) => ("i16x8.min_s"); (name I16x8MinU) => ("i16x8.min_u"); (name I16x8MaxS) => ("i16x8.max_s"); (name I16x8MaxU) => ("i16x8.max_u"); (name I16x8AvgrU) => ("i16x8.avgr_u"); (name I16x8ExtMulLowI8x16S) => ("i16x8.extmul_low_i8x16_s"); (name I16x8ExtMulHighI8x16S) => ("i16x8.extmul_high_i8x16_s"); (name I16x8ExtMulLowI8x16U) => ("i16x8.extmul_low_i8x16_u"); (name I16x8ExtMulHighI8x16U) => ("i16x8.extmul_high_i8x16_u"); (name I32x4ExtAddPairwiseI16x8S) => ("i32x4.extadd_pairwise_i16x8_s"); (name I32x4ExtAddPairwiseI16x8U) => ("i32x4.extadd_pairwise_i16x8_u"); (name I32x4Abs) => ("i32x4.abs"); (name I32x4Neg) => ("i32x4.neg"); (name I32x4AllTrue) => ("i32x4.all_true"); (name I32x4Bitmask) => ("i32x4.bitmask"); (name I32x4ExtendLowI16x8S) => ("i32x4.extend_low_i16x8_s"); (name I32x4ExtendHighI16x8S) => ("i32x4.extend_high_i16x8_s"); (name I32x4ExtendLowI16x8U) => ("i32x4.extend_low_i16x8_u"); (name I32x4ExtendHighI16x8U) => ("i32x4.extend_high_i16x8_u"); (name I32x4Shl) => ("i32x4.shl"); (name I32x4ShrS) => ("i32x4.shr_s"); (name I32x4ShrU) => ("i32x4.shr_u"); (name I32x4Add) => ("i32x4.add"); (name I32x4Sub) => ("i32x4.sub"); (name I32x4Mul) => ("i32x4.mul"); (name I32x4MinS) => ("i32x4.min_s"); (name I32x4MinU) => ("i32x4.min_u"); (name I32x4MaxS) => ("i32x4.max_s"); (name I32x4MaxU) => ("i32x4.max_u"); (name I32x4DotI16x8S) => ("i32x4.dot_i16x8_s"); (name I32x4ExtMulLowI16x8S) => ("i32x4.extmul_low_i16x8_s"); (name I32x4ExtMulHighI16x8S) => ("i32x4.extmul_high_i16x8_s"); (name I32x4ExtMulLowI16x8U) => ("i32x4.extmul_low_i16x8_u"); (name I32x4ExtMulHighI16x8U) => ("i32x4.extmul_high_i16x8_u"); (name I64x2Abs) => ("i64x2.abs"); (name I64x2Neg) => ("i64x2.neg"); (name I64x2AllTrue) => ("i64x2.all_true"); (name I64x2Bitmask) => ("i64x2.bitmask"); (name I64x2ExtendLowI32x4S) => ("i64x2.extend_low_i32x4_s"); (name I64x2ExtendHighI32x4S) => ("i64x2.extend_high_i32x4_s"); (name I64x2ExtendLowI32x4U) => ("i64x2.extend_low_i32x4_u"); (name I64x2ExtendHighI32x4U) => ("i64x2.extend_high_i32x4_u"); (name I64x2Shl) => ("i64x2.shl"); (name I64x2ShrS) => ("i64x2.shr_s"); (name I64x2ShrU) => ("i64x2.shr_u"); (name I64x2Add) => ("i64x2.add"); (name I64x2Sub) => ("i64x2.sub"); (name I64x2Mul) => ("i64x2.mul"); (name I64x2ExtMulLowI32x4S) => ("i64x2.extmul_low_i32x4_s"); (name I64x2ExtMulHighI32x4S) => ("i64x2.extmul_high_i32x4_s"); (name I64x2ExtMulLowI32x4U) => ("i64x2.extmul_low_i32x4_u"); (name I64x2ExtMulHighI32x4U) => ("i64x2.extmul_high_i32x4_u"); (name F32x4Ceil) => ("f32x4.ceil"); (name F32x4Floor) => ("f32x4.floor"); (name F32x4Trunc) => ("f32x4.trunc"); (name F32x4Nearest) => ("f32x4.nearest"); (name F32x4Abs) => ("f32x4.abs"); (name F32x4Neg) => ("f32x4.neg"); (name F32x4Sqrt) => ("f32x4.sqrt"); (name F32x4Add) => ("f32x4.add"); (name F32x4Sub) => ("f32x4.sub"); (name F32x4Mul) => ("f32x4.mul"); (name F32x4Div) => ("f32x4.div"); (name F32x4Min) => ("f32x4.min"); (name F32x4Max) => ("f32x4.max"); (name F32x4PMin) => ("f32x4.pmin"); (name F32x4PMax) => ("f32x4.pmax"); (name F64x2Ceil) => ("f64x2.ceil"); (name F64x2Floor) => ("f64x2.floor"); (name F64x2Trunc) => ("f64x2.trunc"); (name F64x2Nearest) => ("f64x2.nearest"); (name F64x2Abs) => ("f64x2.abs"); (name F64x2Neg) => ("f64x2.neg"); (name F64x2Sqrt) => ("f64x2.sqrt"); (name F64x2Add) => ("f64x2.add"); (name F64x2Sub) => ("f64x2.sub"); (name F64x2Mul) => ("f64x2.mul"); (name F64x2Div) => ("f64x2.div"); (name F64x2Min) => ("f64x2.min"); (name F64x2Max) => ("f64x2.max"); (name F64x2PMin) => ("f64x2.pmin"); (name F64x2PMax) => ("f64x2.pmax"); (name I32x4TruncSatF32x4S) => ("i32x4.trunc_sat_f32x4_s"); (name I32x4TruncSatF32x4U) => ("i32x4.trunc_sat_f32x4_u"); (name F32x4ConvertI32x4S) => ("f32x4.convert_i32x4_s"); (name F32x4ConvertI32x4U) => ("f32x4.convert_i32x4_u"); (name I32x4TruncSatF64x2SZero) => ("i32x4.trunc_sat_f64x2_s_zero"); (name I32x4TruncSatF64x2UZero) => ("i32x4.trunc_sat_f64x2_u_zero"); (name F64x2ConvertLowI32x4S) => ("f64x2.convert_low_i32x4_s"); (name F64x2ConvertLowI32x4U) => ("f64x2.convert_low_i32x4_u"); (name F32x4DemoteF64x2Zero) => ("f32x4.demote_f64x2_zero"); (name F64x2PromoteLowF32x4) => ("f64x2.promote_low_f32x4"); (name I8x16RelaxedSwizzle) => ("i8x16.relaxed_swizzle"); (name I32x4RelaxedTruncF32x4S) => ("i32x4.relaxed_trunc_f32x4_s"); (name I32x4RelaxedTruncF32x4U) => ("i32x4.relaxed_trunc_f32x4_u"); (name I32x4RelaxedTruncF64x2SZero) => ("i32x4.relaxed_trunc_f64x2_s_zero"); (name I32x4RelaxedTruncF64x2UZero) => ("i32x4.relaxed_trunc_f64x2_u_zero"); (name F32x4RelaxedMadd) => ("f32x4.relaxed_madd"); (name F32x4RelaxedNmadd) => ("f32x4.relaxed_nmadd"); (name F64x2RelaxedMadd) => ("f64x2.relaxed_madd"); (name F64x2RelaxedNmadd) => ("f64x2.relaxed_nmadd"); (name I8x16RelaxedLaneselect) => ("i8x16.relaxed_laneselect"); (name I16x8RelaxedLaneselect) => ("i16x8.relaxed_laneselect"); (name I32x4RelaxedLaneselect) => ("i32x4.relaxed_laneselect"); (name I64x2RelaxedLaneselect) => ("i64x2.relaxed_laneselect"); (name F32x4RelaxedMin) => ("f32x4.relaxed_min"); (name F32x4RelaxedMax) => ("f32x4.relaxed_max"); (name F64x2RelaxedMin) => ("f64x2.relaxed_min"); (name F64x2RelaxedMax) => ("f64x2.relaxed_max"); (name I16x8RelaxedQ15mulrS) => ("i16x8.relaxed_q15mulr_s"); (name I16x8RelaxedDotI8x16I7x16S) => ("i16x8.relaxed_dot_i8x16_i7x16_s"); (name I32x4RelaxedDotI8x16I7x16AddS) => ("i32x4.relaxed_dot_i8x16_i7x16_add_s"); (name StructNew) => ("struct.new"); (name StructNewDefault) => ("struct.new_default"); (name StructGet) => ("struct.get"); (name StructGetS) => ("struct.get_s"); (name StructGetU) => ("struct.get_u"); (name StructSet) => ("struct.set"); (name ArrayNew) => ("array.new"); (name ArrayNewDefault) => ("array.new_default"); (name ArrayNewFixed) => ("array.new_fixed"); (name ArrayNewData) => ("array.new_data"); (name ArrayNewElem) => ("array.new_elem"); (name ArrayGet) => ("array.get"); (name ArrayGetS) => ("array.get_s"); (name ArrayGetU) => ("array.get_u"); (name ArraySet) => ("array.set"); (name ArrayLen) => ("array.len"); (name ArrayFill) => ("array.fill"); (name ArrayCopy) => ("array.copy"); (name ArrayInitData) => ("array.init_data"); (name ArrayInitElem) => ("array.init_elem"); (name AnyConvertExtern) => ("any.convert_extern"); (name ExternConvertAny) => ("extern.convert_any"); (name RefTestNonNull) => ("ref.test"); (name RefTestNullable) => ("ref.test"); (name RefCastNonNull) => ("ref.cast"); (name RefCastNullable) => ("ref.cast"); (name BrOnCast) => ("br_on_cast"); (name BrOnCastFail) => ("br_on_cast_fail"); (name RefI31) => ("ref.i31"); (name I31GetS) => ("i31.get_s"); (name I31GetU) => ("i31.get_u"); (name TryTable) => ("try_table"); (name Throw) => ("throw"); (name ThrowRef) => ("throw_ref"); (name Rethrow) => ("rethrow"); (name Try) => ("try"); (name Catch) => ("catch"); (name CatchAll) => ("catch_all"); (name Delegate) => ("delegate"); (name GlobalAtomicGet) => ("global.atomic.get"); (name GlobalAtomicSet) => ("global.atomic.set"); (name GlobalAtomicRmwAdd) => ("global.atomic.rmw.add"); (name GlobalAtomicRmwSub) => ("global.atomic.rmw.sub"); (name GlobalAtomicRmwAnd) => ("global.atomic.rmw.and"); (name GlobalAtomicRmwOr) => ("global.atomic.rmw.or"); (name GlobalAtomicRmwXor) => ("global.atomic.rmw.xor"); (name GlobalAtomicRmwXchg) => ("global.atomic.rmw.xchg"); (name GlobalAtomicRmwCmpxchg) => ("global.atomic.rmw.cmpxchg"); (name TableAtomicGet) => ("table.atomic.get"); (name TableAtomicSet) => ("table.atomic.set"); (name TableAtomicRmwXchg) => ("table.atomic.rmw.xchg"); (name TableAtomicRmwCmpxchg) => ("table.atomic.rmw.cmpxchg"); (name StructAtomicGet) => ("struct.atomic.get"); (name StructAtomicGetS) => ("struct.atomic.get_s"); (name StructAtomicGetU) => ("struct.atomic.get_u"); (name StructAtomicSet) => ("struct.atomic.set"); (name StructAtomicRmwAdd) => ("struct.atomic.rmw.add"); (name StructAtomicRmwSub) => ("struct.atomic.rmw.sub"); (name StructAtomicRmwAnd) => ("struct.atomic.rmw.and"); (name StructAtomicRmwOr) => ("struct.atomic.rmw.or"); (name StructAtomicRmwXor) => ("struct.atomic.rmw.xor"); (name StructAtomicRmwXchg) => ("struct.atomic.rmw.xchg"); (name StructAtomicRmwCmpxchg) => ("struct.atomic.rmw.cmpxchg"); (name ArrayAtomicGet) => ("array.atomic.get"); (name ArrayAtomicGetS) => ("array.atomic.get_s"); (name ArrayAtomicGetU) => ("array.atomic.get_u"); (name ArrayAtomicSet) => ("array.atomic.set"); (name ArrayAtomicRmwAdd) => ("array.atomic.rmw.add"); (name ArrayAtomicRmwSub) => ("array.atomic.rmw.sub"); (name ArrayAtomicRmwAnd) => ("array.atomic.rmw.and"); (name ArrayAtomicRmwOr) => ("array.atomic.rmw.or"); (name ArrayAtomicRmwXor) => ("array.atomic.rmw.xor"); (name ArrayAtomicRmwXchg) => ("array.atomic.rmw.xchg"); (name ArrayAtomicRmwCmpxchg) => ("array.atomic.rmw.cmpxchg"); (name RefI31Shared) => ("ref.i31_shared"); (name ContNew) => ("cont.new"); (name ContBind) => ("cont.bind"); (name Suspend) => ("suspend"); (name Resume) => ("resume"); (name ResumeThrow) => ("resume_throw"); (name Switch) => ("switch"); (name I64Add128) => ("i64.add128"); (name I64Sub128) => ("i64.sub128"); (name I64MulWideS) => ("i64.mul_wide_s"); (name I64MulWideU) => ("i64.mul_wide_u"); } impl<'a> VisitOperator<'a> for PrintOperator<'_, '_, '_, '_> { type Output = Result<()>; fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> { Some(self) } wasmparser::for_each_visit_operator!(define_visit); } impl<'a> VisitSimdOperator<'a> for PrintOperator<'_, '_, '_, '_> { wasmparser::for_each_visit_simd_operator!(define_visit); } pub trait OpPrinter { fn branch_hint(&mut self, offset: usize, taken: bool) -> Result<()>; fn set_offset(&mut self, offset: usize); fn visit_operator( &mut self, reader: &mut OperatorsReader<'_>, annotation: Option<&str>, ) -> Result<()>; fn finalize(&mut self, annotation: Option<&str>) -> Result<()>; fn use_color(&self) -> bool; } impl OpPrinter for PrintOperator<'_, '_, '_, '_> { fn branch_hint(&mut self, offset: usize, taken: bool) -> Result<()> { self.printer.newline(offset)?; let desc = if taken { "\"\\01\"" } else { "\"\\00\"" }; self.printer.result.start_comment()?; write!(self.printer.result, "(@metadata.code.branch_hint {desc})")?; self.printer.result.reset_color()?; Ok(()) } fn set_offset(&mut self, offset: usize) { self.operator_state.op_offset = offset; } fn visit_operator( &mut self, reader: &mut OperatorsReader<'_>, annotation: Option<&str>, ) -> Result<()> { reader.visit_operator(self)??; if let Some(s) = annotation { self.printer.newline_unknown_pos()?; self.result().start_comment()?; write!(self.result(), ";; {s}")?; self.result().reset_color()?; } Ok(()) } fn finalize(&mut self, annotation: Option<&str>) -> Result<()> { if let Some(s) = annotation { self.printer.newline_unknown_pos()?; self.result().start_comment()?; write!(self.printer.result, ";; {s}")?; self.result().reset_color()?; } Ok(()) } fn use_color(&self) -> bool { self.printer.result.supports_async_color() } } impl OpPrinter for PrintOperatorFolded<'_, '_, '_, '_> { fn branch_hint(&mut self, offset: usize, taken: bool) -> Result<()> { let mut hint = String::new(); hint.push_str("@metadata.code.branch_hint "); hint.push_str(if taken { "\"\\01\"" } else { "\"\\00\"" }); self.branch_hint = Some(FoldedInstruction { plain: hint, folded: Vec::new(), results: 0, offset, }); Ok(()) } fn set_offset(&mut self, offset: usize) { self.operator_state.op_offset = offset; } fn visit_operator( &mut self, reader: &mut OperatorsReader<'_>, annotation: Option<&str>, ) -> Result<()> { let operator = reader.clone().read()?; let (params, results) = operator.operator_arity(self).unwrap_or((0, 0)); let mut buf_color = PrintTermcolor(Ansi::new(Vec::new())); let mut buf_nocolor = PrintTermcolor(NoColor::new(Vec::new())); let internal_config = Config { name_unnamed: self.printer.config.name_unnamed, ..Default::default() }; let mut internal_printer = Printer { config: &internal_config, result: if self.use_color() { &mut buf_color } else { &mut buf_nocolor }, nesting: self.printer.nesting, line: self.printer.line, group_lines: Vec::new(), code_section_hints: Vec::new(), }; let mut op_printer = PrintOperator::new(&mut internal_printer, self.state, self.operator_state); reader.visit_operator(&mut op_printer)??; if let Some(s) = annotation { internal_printer.result.start_comment()?; write!(internal_printer.result, " (; {s}")?; internal_printer.result.start_comment()?; write!(internal_printer.result, " ;)")?; internal_printer.result.reset_color()?; } self.printer.nesting = internal_printer.nesting; self.printer.line = internal_printer.line; let inst = String::from_utf8(if self.use_color() { buf_color.0.into_inner() } else { buf_nocolor.0.into_inner() }) .expect("invalid UTF-8"); match operator { Operator::Loop { blockty } => self.push_block(blockty, FrameKind::Loop, inst), Operator::Block { blockty } => self.push_block(blockty, FrameKind::Block, inst), Operator::TryTable { try_table } => { self.push_block(try_table.ty, FrameKind::TryTable, inst) } Operator::If { blockty } => self.push_if(blockty, inst), Operator::Else => self.handle_else(), Operator::End => self.handle_end(results), Operator::Try { .. } | Operator::Catch { .. } | Operator::CatchAll { .. } | Operator::Delegate { .. } => { bail!("legacy-exceptions not supported") } _ => self.handle_plain(inst, params, results), } } // Recurse through the stack and print each folded instruction. fn finalize(&mut self, annotation: Option<&str>) -> Result<()> { if self.control.len() != 1 { bail!("instruction sequence not closed"); } for inst in &self.control.last().unwrap().folded { PrintOperatorFolded::print(&mut self.printer, &mut self.original_separator, &inst)?; } if let Some(s) = annotation { self.printer.newline_unknown_pos()?; self.printer.result.start_comment()?; write!(self.printer.result, ";; {s}")?; self.printer.result.reset_color()?; } Ok(()) } fn use_color(&self) -> bool { self.printer.result.supports_async_color() } } impl ModuleArity for PrintOperatorFolded<'_, '_, '_, '_> { fn tag_type_arity(&self, tag_idx: u32) -> Option<(u32, u32)> { self.sub_type_arity( self.sub_type_at( *self .state .core .tag_to_type .get(tag_idx as usize)? .as_ref()?, )?, ) } fn type_index_of_function(&self, func_idx: u32) -> Option { *self.state.core.func_to_type.get(func_idx as usize)? } fn sub_type_at(&self, type_idx: u32) -> Option<&SubType> { self.state.core.types.get(type_idx as usize)?.as_ref() } fn func_type_of_cont_type(&self, c: &ContType) -> Option<&FuncType> { let st = self.sub_type_at(c.0.unpack().as_module_index()?)?; if let CompositeInnerType::Func(ft) = &st.composite_type.inner { Some(ft) } else { None } } fn sub_type_of_ref_type(&self, rt: &RefType) -> Option<&SubType> { self.sub_type_at(rt.type_index()?.as_module_index()?) } fn control_stack_height(&self) -> u32 { self.control.len() as u32 } fn label_block(&self, depth: u32) -> Option<(BlockType, FrameKind)> { let cur_depth = self.printer.nesting - self.operator_state.nesting_start; if self.control.len() != cur_depth as usize + 1 { return None; } match (self.control.len() - 1).checked_sub(depth as usize) { Some(i) => Some((self.control[i].ty, self.control[i].kind)), None => None, } } } impl<'printer, 'state, 'a, 'b> PrintOperatorFolded<'printer, 'state, 'a, 'b> { pub(super) fn new( printer: &'printer mut Printer<'a, 'b>, state: &'state mut State, operator_state: &'printer mut OperatorState, ) -> Self { let original_separator = operator_state.sep; operator_state.sep = OperatorSeparator::None; PrintOperatorFolded { printer, state, operator_state, control: Vec::new(), branch_hint: None, original_separator, } } // Set up the outermost block, representing the unnamed function label. pub fn begin_function(&mut self, func_idx: u32) -> Result<()> { match self.state.core.func_to_type.get(func_idx as usize) { Some(Some(type_idx)) => self.control.push(Block { ty: BlockType::FuncType(*type_idx), kind: FrameKind::Block, plain: String::new(), folded: Vec::new(), predicate: None, consequent: None, offset: self.operator_state.op_offset, }), _ => bail!("invalid func_idx"), } Ok(()) } // Set up a catch-all block to represent the constant expression's operand stack. pub fn begin_const_expr(&mut self) { self.control.push(Block { ty: BlockType::Empty, kind: FrameKind::Block, plain: String::new(), folded: Vec::new(), predicate: None, consequent: None, offset: 0, }); } // Handle a "plain" (non-block) instruction. The behavior resembles WABT's WatWriter::PushExpr(). // Each instruction will pop some number of operands off the stack to become the "children" // of the foldedinst phrase. In the presence of multi-value instructions with more than 1 result, // it may not be possible to hit the params target exactly. This doesn't necessarily mean the // Wasm is invalid, but it can't be represented sensibly in folded form. fn handle_plain(&mut self, plain: String, params: u32, mut results: u32) -> Result<()> { let stack = match self.control.last_mut() { Some(stack) => stack, None => bail!("instruction without enclosing block"), }; let mut first_param = stack.folded.len(); let mut param_count: u32 = 0; if params > 0 { for (pos, inst) in stack.folded.iter().enumerate().rev() { param_count = param_count.saturating_add(inst.results); if param_count == params { first_param = pos; break; } else if param_count > params { // unfoldable instruction results = u32::MAX; break; } } } let mut inst = FoldedInstruction { plain, folded: stack.folded.drain(first_param..).collect(), results, offset: self.operator_state.op_offset, }; if let Some(hint) = self.branch_hint.take() { inst.folded.push(hint); } stack.folded.push(inst); Ok(()) } // Print a folded instruction to the "real" printer. First print the "plain" // instruction, then recursively print each instruction that was folded in to the // foldedinst phrase. fn print( printer: &mut Printer, sep: &mut OperatorSeparator, inst: &FoldedInstruction, ) -> Result<()> { match sep { OperatorSeparator::Newline => printer.newline(inst.offset)?, OperatorSeparator::None => (), OperatorSeparator::NoneThenSpace => *sep = OperatorSeparator::Space, OperatorSeparator::Space => printer.result.write_str(" ")?, } printer.result.write_str("(")?; printer.result.write_str(&inst.plain)?; if inst.folded.is_empty() && inst.plain.contains(";;") { // Wasm line comment (e.g. label annotation) shouldn't comment out the closing parenthesis printer.newline(inst.offset)?; } printer.nesting += 1; for fi in &inst.folded { PrintOperatorFolded::print(printer, sep, &fi)?; } printer.nesting -= 1; printer.result.write_str(")")?; Ok(()) } // The folding printer doesn't try to handle branch hints attached to blocks other than `if`. fn reject_branch_hint(&mut self) -> Result<()> { if self.branch_hint.is_some() { bail!("branch hints are only supported on an `if` or a plain instructions"); } Ok(()) } fn push_block(&mut self, ty: BlockType, kind: FrameKind, plain: String) -> Result<()> { self.reject_branch_hint()?; self.control.push(Block { ty, kind, plain, folded: Vec::new(), predicate: None, consequent: None, offset: self.operator_state.op_offset, }); Ok(()) } fn push_if(&mut self, ty: BlockType, plain: String) -> Result<()> { let mut predicate = Vec::new(); if let Some(phrase) = self .control .last_mut() .ok_or_else(|| anyhow!("no enclosing block"))? .folded .pop() { predicate.push(phrase) } if let Some(hint) = self.branch_hint.take() { predicate.push(hint); } self.control.push(Block { ty, kind: FrameKind::If, plain, folded: Vec::new(), predicate: Some(predicate), consequent: None, offset: self.operator_state.op_offset, }); Ok(()) } fn handle_else(&mut self) -> Result<()> { self.reject_branch_hint()?; match self.control.pop() { Some(Block { ty, kind: FrameKind::If, plain, predicate, folded, offset, .. }) => self.control.push(Block { ty, kind: FrameKind::Else, plain, folded: Vec::new(), predicate, consequent: Some((folded, offset)), offset: self.operator_state.op_offset, }), _ => bail!("no enclosing if block"), } Ok(()) } // The end instruction closes the current block and transforms it to the // corresponding form of foldedinst belonging to the parent block. This reuses/abuses // the "plain" nomenclature to also represent the opening delimiters // of block instructions and other block-like clauses (e.g. "then", "else"). fn handle_end(&mut self, results: u32) -> Result<()> { self.reject_branch_hint()?; let frame = self.control.pop(); let inst = match frame { Some(Block { kind: FrameKind::Block | FrameKind::Loop | FrameKind::TryTable, plain, folded, offset, .. }) => FoldedInstruction { plain, folded, results, offset, }, Some(Block { kind: FrameKind::If, plain, folded, predicate: Some(predicate), offset, .. }) => { let then_clause = FoldedInstruction { plain: String::from("then"), folded, results, offset, }; let mut folded = predicate; folded.push(then_clause); FoldedInstruction { plain, folded, results, offset, } } Some(Block { kind: FrameKind::Else, plain, folded, predicate: Some(predicate), consequent: Some((consequent, if_offset)), offset, .. }) => { let then_clause = FoldedInstruction { plain: String::from("then"), folded: consequent, results, offset: if_offset, }; let else_clause = FoldedInstruction { plain: String::from("else"), folded, results, offset, }; let mut folded = predicate; folded.push(then_clause); folded.push(else_clause); FoldedInstruction { plain, folded, results, offset: if_offset, } } _ => bail!("unhandled frame kind"), }; self.control .last_mut() .ok_or_else(|| anyhow!("end without outer block"))? .folded .push(inst); Ok(()) } } wasmprinter-0.239.0/src/print.rs000064400000000000000000000133711046102023000146500ustar 00000000000000use std::fmt; use std::io; use termcolor::{Color, ColorSpec}; /// Trait used to print WebAssembly modules in this crate. /// /// Instances of this trait are passed to /// [`Config::print`](super::Config::print). Instances of this trait are where /// the output of a WebAssembly binary goes. /// /// Note that this trait has built-in adapters in the `wasmprinter` crate: /// /// * For users of [`std::io::Write`] use [`PrintIoWrite`]. /// * For users of [`std::fmt::Write`] use [`PrintFmtWrite`]. pub trait Print { /// Writes the given string `s` in its entirety. /// /// Returns an error for any I/O error. fn write_str(&mut self, s: &str) -> io::Result<()>; /// Indicates that a newline is being printed. /// /// This can be overridden to hook into the offset at which lines are /// printed. fn newline(&mut self) -> io::Result<()> { self.write_str("\n") } /// Indicates that a new line in the output is starting at the /// `binary_offset` provided. /// /// Not all new lines have a binary offset associated with them but this /// method should be called for new lines in the output. This enables /// correlating binary offsets to lines in the output. fn start_line(&mut self, binary_offset: Option) { let _ = binary_offset; } /// Enables usage of `write!` with this trait. fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { struct Adapter<'a, T: ?Sized + 'a> { inner: &'a mut T, error: io::Result<()>, } impl fmt::Write for Adapter<'_, T> { fn write_str(&mut self, s: &str) -> fmt::Result { match self.inner.write_str(s) { Ok(()) => Ok(()), Err(e) => { self.error = Err(e); Err(fmt::Error) } } } } let mut output = Adapter { inner: self, error: Ok(()), }; match fmt::write(&mut output, args) { Ok(()) => Ok(()), Err(..) => output.error, } } /// Helper to print a custom section, if needed. /// /// If this output has a custom means of printing a custom section then this /// can be used to override the default. If `Ok(true)` is returned then the /// section will be considered to be printed. Otherwise an `Ok(false)` /// return value indicates that the default printing should happen. fn print_custom_section( &mut self, name: &str, binary_offset: usize, data: &[u8], ) -> io::Result { let _ = (name, binary_offset, data); Ok(false) } /// Sets the colors settings for literals (`"foo"`) to be printed. fn start_literal(&mut self) -> io::Result<()> { Ok(()) } /// Sets the colors settings for a name (`$foo`) to be printed. fn start_name(&mut self) -> io::Result<()> { Ok(()) } /// Sets the colors settings for a keyword (`(module ...)`) to be printed. fn start_keyword(&mut self) -> io::Result<()> { Ok(()) } /// Sets the colors settings for a type (`(param i32)`) to be printed. fn start_type(&mut self) -> io::Result<()> { Ok(()) } /// Sets the colors settings for a comment (`;; ...`) to be printed. fn start_comment(&mut self) -> io::Result<()> { Ok(()) } /// Resets colors settings to the default. fn reset_color(&mut self) -> io::Result<()> { Ok(()) } /// Returns `true` if the device uses colors without interacting synchronously with a terminal (e.g. ANSI) fn supports_async_color(&self) -> bool { false } } /// An adapter between the [`std::io::Write`] trait and [`Print`]. pub struct PrintIoWrite(pub T); impl Print for PrintIoWrite where T: io::Write, { fn write_str(&mut self, s: &str) -> io::Result<()> { self.0.write_all(s.as_bytes()) } } /// An adapter between the [`std::fmt::Write`] trait and [`Print`]. pub struct PrintFmtWrite(pub T); impl Print for PrintFmtWrite where T: fmt::Write, { fn write_str(&mut self, s: &str) -> io::Result<()> { match self.0.write_str(s) { Ok(()) => Ok(()), Err(fmt::Error) => Err(io::Error::new( io::ErrorKind::Other, "failed to write string", )), } } } /// An adapter between the [`termcolor::WriteColor`] trait and [`Print`]. pub struct PrintTermcolor(pub T); impl Print for PrintTermcolor where T: termcolor::WriteColor, { fn write_str(&mut self, s: &str) -> io::Result<()> { self.0.write_all(s.as_bytes()) } fn start_name(&mut self) -> io::Result<()> { self.0 .set_color(ColorSpec::new().set_fg(Some(Color::Magenta))) } fn start_literal(&mut self) -> io::Result<()> { self.0.set_color(ColorSpec::new().set_fg(Some(Color::Red))) } fn start_keyword(&mut self) -> io::Result<()> { self.0.set_color( ColorSpec::new() .set_fg(Some(Color::Yellow)) .set_bold(true) .set_intense(true), ) } fn start_type(&mut self) -> io::Result<()> { self.0.set_color( ColorSpec::new() .set_fg(Some(Color::Green)) .set_bold(true) .set_intense(true), ) } fn start_comment(&mut self) -> io::Result<()> { self.0.set_color(ColorSpec::new().set_fg(Some(Color::Cyan))) } fn reset_color(&mut self) -> io::Result<()> { self.0.reset() } fn supports_async_color(&self) -> bool { self.0.supports_color() && !self.0.is_synchronous() } } wasmprinter-0.239.0/tests/all.rs000064400000000000000000000027601046102023000146370ustar 00000000000000#[test] fn no_oom() { // Whatever is printed here, it shouldn't take more than 500MB to print // since it's only 20k functions. let mut s = String::new(); s.push_str("(module\n"); for _ in 0..20_000 { s.push_str("(func i32.const 0 if i32.const 0 else i32.const 0 end)\n"); } s.push(')'); let bytes = wat::parse_str(&s).unwrap(); let wat = wasmprinter::print_bytes(&bytes).unwrap(); assert!(wat.len() < 500_000_000); } #[test] fn offsets_and_lines_smoke_test() { const MODULE: &str = r#" (;@0 ;) (module (;@b ;) (type (;0;) (func (param i32) (result i32))) (;@1f ;) (func (;0;) (type 0) (param i32) (result i32) (;@20 ;) local.get 0 ) (;@17 ;) (export "f" (func 0)) ) "#; let bytes = wat::parse_str(MODULE).unwrap(); let mut storage = String::new(); let actual: Vec<_> = wasmprinter::Config::new() .offsets_and_lines(&bytes, &mut storage) .unwrap() .collect(); #[rustfmt::skip] let expected = vec![ (Some(0), "(module\n"), (Some(0xb), " (type (;0;) (func (param i32) (result i32)))\n"), (Some(0x17), " (export \"f\" (func 0))\n"), (Some(0x1f), " (func (;0;) (type 0) (param i32) (result i32)\n"), (Some(0x20), " local.get 0\n"), (None, " )\n"), (None, ")\n"), (Some(0x23), ""), ]; assert_eq!(actual, expected); }